I want create a templateview but need to use a editable template on-the-fly, to this i can set a template from a code stored inside a field from a table.
# some_app/views.py
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html" -> from table.field
Some ideia?
Great #WillemVanOnsem nice solution. But to complicate more, if i want to do in Django Rest like:
class FormularioDisplayView(APIView):
renderer_classes = [TemplateHTMLRenderer]
def get(self, request, pk):
formulario = get_object_or_404(Formulario, codigo=pk)
serializer = FormularioDisplaySerializer()
template_data = TemplateData.objects.get(name='model_01')
return Response({'serializer': serializer, 'formulario': formulario}) -> Render using template_data.template as the template
We can alter the TemplateView to obtain the content of a template from the database. For example we can have a model like:
# app/models.py
class TemplateData(models.Model):
name = models.CharField(max_length=128, unique=True)
template = models.CharField(max_length=32768)
Here the TemplateData model thus associates a name with the content of a template.
Next we can implement a TemplateFromDatabaseView where we override the render_to_response method [Django-doc]:
# app/views.py
from app.models import TemplateData
from django.http import HttpResponse
from django.views.generic import TemplateView
from django.template import Template, Context
class TemplateFromDatabaseView(TemplateView):
def render_to_response(self, context, **response_kwargs):
template_data = TemplateData.objects.get(name=self.template_name)
return HttpResponse(
Template(template_data.template).render(
RequestContext(self.request, context)
)
), **response_kwargs)
Then you can subclass it, for example with:
# app/views.py
# …
class AboutView(TemplateFromDatabaseView):
template_name = 'database_template_name'
Of course that means you need to add a TemplateData object to the database with as name the 'database_template_name' and with as template field, the content you want to render.
Related
I want to turn a function view into a class-based view.
Here is the View & URL path that I have right now.
View:
def customer(request, pk):
customer = Customer.objects.get(id=pk)
return render(request, 'accounts/customer.html)
URL Path:
path('customer/<str:pk>/, views.customer, name='customer')
What would the proper syntax be to turn this view into a class-based view.
I am mainly curious about how to access the primary key here using a class-based view.
Thanks!
You can use Detail View.
in your views.py
from django.views.generic.detail import DetailView
from .models import Customer
class CustomerDetails(DetailView):
model = Customer
template_name = 'customerdetail.html' #deafaults to customer_detail.html
in urls.py
path('customer/<int:pk>', views.CustomerDetails.as_view(), name='customer')
the context that will be passed to customerdetail.html template will have the default name 'object'. access the fields of customer instance by {{object.<field_name>}}.
You can override the attributes and methods of DetailView.
for moreinfo : DetailView
If you want more control in your view you can use generic View
from django.views.generic.detail import View
from .models import Customer
class CustomerDetails(View):
def get(self, *args, **kwargs):
customer = Customer.objects.get(id=kwargs['pk'])
return render(self.request, 'accounts/customer.html)
urls.py
path('customer/<int:pk>', views.CustomerDetails.as_view(), name='customer')
arguments found in the url are stored in **kwargs
views.py
from django.shortcuts import render
from django.views.generic import DetailView
from .models import Producto
def general_view(request):
context = {
'articulos': Producto.objects.all(),
}
return render(request, 'shop/general_view.html', context)
class DetailedView(DetailView):
model = Producto
template_name = 'shop/specific_view.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['determinado'] = Producto.objects.get(pk=*¿?*)
return context
urls.py
from django.urls import path
from . import views
from .views import DetailedView
urlpatterns = [
path('', views.general_view, name='general-view'),
path('<int:pk>/', DetailedView.as_view(), name='specific-article'),
]
As you see, the problem occurs because I don't know how to call the id or pk in the detailed_view in views.py, I'm guessing you maybe have to do a dictionary but I don't know where to do it nor how. It works if I set the id to 1, but obviously what this does is that is shows in every url with a different id the same article.
You do not need to do that yourself. The idea is that the DetailView has the boilerplate code, to automatically filter on the primary key. You can simply set the .context_object_name attribute [Django-doc] to 'determinado':
class DetailedView(DetailView):
model = Producto
template_name = 'shop/specific_view.html'
context_object_name = 'determinado'
# no override of get_context_data
Creating a page for an ecommerce site that allows a user to add an item to their cart. Im having issues with no object user found in the ProductDetailView. I overrode the get_context_data function to the class based view, so it will be called when they access the specific product page.
Error:
'ProductDetailView' object has no attribute 'user'
I'm assuming this is because the view doesn't see users, because it needs a user.objects.all()?
views.py
from django.shortcuts import render, redirect
from django.shortcuts import render
from django.views.generic import CreateView, DetailView, ListView, RedirectView, UpdateView
from django.views import generic
from django.contrib.auth.decorators import login_required # This is to block pages to non users using function views
from django.contrib.auth.mixins import LoginRequiredMixin
from .forms import ProductForm, ContactForm
from .models import Product, Contact
from checkout.models import OrderItem, Order
from users.models import User, Profile
# Create your views here.
class ProductDetailView(generic.DetailView):
model = Product
template_name = 'portal/product_detail.html'
def get_context_data(request, **kwargs):
filtered_orders = Order.objects.filter(owner=request.user.user_profile, is_ordered=False)
current_order_products = []
if filtered_orders.exists():
user_order = filtered_orders[0]
user_order_items = user_order.items.all()
current_order_products = [product.product for product in user_order_items]
context = {
'current_order_products': current_order_products
}
return context
The problem is in your def get_context_data(request, **kwargs), you need to have self as first argument. You need to remove request from the arguments and use self.request to access it:
def get_context_data(self, **kwargs):
filtered_orders = Order.objects.filter(owner=self.request.user.user_profile, is_ordered=False)
So I'm trying to enable the currently logged in user to delete their own post on my site. I'm using a DeleteView with select_related mixing. I keep getting an error that says it's improperly configured and needs to be a tuple or a list. Here is the error, my post models, and views. Not really sure where to go from here, and this doesn't seem to be a very popular error as I'm not really able to find much with a Google search.
Error:
ImproperlyConfigured at /colorsets/delete/7/
DeletePost's select_related property must be a tuple or list.
App Views.py
from django.shortcuts import render
from colorsets.forms import ColorForm
from colorsets import models
from colorsets.models import ColorSet
from django.utils import timezone
from django.contrib.auth import authenticate,login,logout
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse,reverse_lazy
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from braces.views import SelectRelatedMixin
from django.views.generic import (TemplateView,ListView,
DetailView,CreateView,
UpdateView,DeleteView)
# Create your views here.
#def index(request):
# return render(request,'index.html')
class PostListView(ListView):
model = ColorSet
def get_queryset(self):
return ColorSet.objects.filter(published_date__lte=timezone.now()).order_by('-published_date')
class CreateColorSetView(LoginRequiredMixin,CreateView):
login_url = '/login/'
redirect_field_name = 'index.html'
form_class = ColorForm
model = ColorSet
def form_valid(self,form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
class DeletePost(LoginRequiredMixin,SelectRelatedMixin,DeleteView):
model = models.ColorSet
select_related = ('user')
success_url = reverse_lazy('index')
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user_id=self.request.user.id)
def delete(self,*args,**kwargs):
messages.success(self.request,'Post Deleted')
return super().delete(*args,**kwargs)
App models.py
from django.db import models
from django.core.urlresolvers import reverse
from django.conf import settings
from django.contrib.auth import get_user_model
User = get_user_model()
# Create your models here.
class ColorSet(models.Model):
user = models.ForeignKey(User,related_name='colorset')
published_date = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=50,blank=False)
color_one = models.CharField(max_length=6,blank=False,default='cccccc')
color_two = models.CharField(max_length=6,blank=False,default='ffffff')
color_three = models.CharField(max_length=6,blank=False,default='e5e5e5')
color_four = models.CharField(max_length=6,blank=False,default='f0f0f0')
color_five = models.CharField(max_length=6,blank=False,default='bababa')
def publish(self):
self.save()
def get_absolute_url(self):
return reverse('index')
def __str__(self):
return self.name
Traceback:
Let me know if you need to see anything else.
You are missing the comma to make select_related a tuple.
select_related = ('user',)
Without the comma, ('user') is the string 'user'.
I have a custom class-based view
# myapp/views.py
from django.views.generic import *
class MyView(DetailView):
template_name = 'detail.html'
model = MyModel
def get_object(self, queryset=None):
return queryset.get(slug=self.slug)
I want to pass in the slug parameter (or other parameters to the view) like this
MyView.as_view(slug='hello_world')
Do I need to override any methods to be able to do this?
If your urlconf looks something like this:
url(r'^(?P<slug>[a-zA-Z0-9-]+)/$', MyView.as_view(), name = 'my_named_view')
then the slug will be available inside your view functions (such as 'get_queryset') like this:
self.kwargs['slug']
Every parameter that's passed to the as_view method is an instance variable of the View class. That means to add slug as a parameter you have to create it as an instance variable in your sub-class:
# myapp/views.py
from django.views.generic import DetailView
class MyView(DetailView):
template_name = 'detail.html'
model = MyModel
# additional parameters
slug = None
def get_object(self, queryset=None):
return queryset.get(slug=self.slug)
That should make MyView.as_view(slug='hello_world') work.
If you're passing the variables through keywords, use what Mr Erikkson suggested: https://stackoverflow.com/a/11494666/9903
It's worth noting you don't need to override get_object() in order to look up an object based on a slug passed as a keyword arg - you can use the attributes of a SingleObjectMixin https://docs.djangoproject.com/en/1.5/ref/class-based-views/mixins-single-object/#singleobjectmixin
# views.py
class MyView(DetailView):
model = MyModel
slug_field = 'slug_field_name'
slug_url_kwarg = 'model_slug'
context_object_name = 'my_model'
# urls.py
url(r'^(?P<model_slug>[\w-]+)/$', MyView.as_view(), name = 'my_named_view')
# mymodel_detail.html
{{ my_model.slug_field_name }}
(both slug_field and slug_url_kwarg default to 'slug')
If you want to add an object to the context for the template you can override get_context_data and add to its context. The request is also a part of self in case you need the request.user.
def get_context_data(self, **kwargs):
context = super(MyTemplateView, self).get_context_data(**kwargs)
if 'slug' in self.kwargs:
context['object'] = get_object_or_404(MyObject, slug=self.kwargs['slug'])
context['objects'] = get_objects_by_user(self.request.user)
return context
You can pass parameters from urls.py
https://docs.djangoproject.com/en/1.7/topics/http/urls/#passing-extra-options-to-view-functions
This also works for generic views. Example:
url(r'^$', views.SectionView.as_view(), { 'pk': 'homepage', 'another_param':'?'}, name='main_page'),
In this case the parameters passed to the view should not necessarily be instance variables of the View class. Using this method you don't need to hardcode default page name into YourView model, but you can just pass it as a parameter from urlconf.
As stated by Yaroslav Nikitenko, if you don't want to hardcode a new instance variable to the View class, you can pass extra options to view functions from urls.py like this:
url(r'^$', YourView.as_view(), {'slug': 'hello_world'}, name='page_name')
I just wanted to add how to use it from the view. You can implement one of the following methods:
# If slug is optional
def the_function(self, request, slug=None):
# use slug here
# if slug is an optional param among others
def the_function(self, request, **kwargs):
slug = kwargs.get("slug", None)
other_param = kwargs.get("other_param", None)
# If slug is required
def the_function(self, request, slug):
# use slug here
For django 3.0, this is what worked for me:
# myapp/views.py
from django.views.generic import DetailView
class MyView(DetailView):
template_name = 'detail.html'
slug = None
def get_object(self, queryset=None):
self.slug = self.kwargs.get('slug', None)
return queryset.get(slug=self.slug)
# myapp/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('slug/<slug:slug>/', views.MyView.as_view(), name='myview_by_tag'),
]