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'),
]
Related
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.
To redirect the user after filling out the CreateView form I would like to access an argument from the form and pass it to the reverse_lazy function.
How can I access the parameters of the form within CreateView?
I actually use the argument I'm looking for to pass it to the form itself (self.request.META.get('HTTP_REFERER').split('/')[-1]), but seem not to be able to use this logic in reverse_lazy.
get_form_kwargs also seems not to achieve the result:
views.py
class PieceInstanceCreate(LoginRequiredMixin, CreateView):
model = PieceInstance
fields = ['version', 'piece_image', 'status']
def form_valid(self, form):
obj = form.save(commit=False)
obj.piece = Piece.objects.get(id=self.request.META.get('HTTP_REFERER').split('/')[-1])
return super(PieceInstanceCreate, self).form_valid(form)
def get_form_kwargs(self):
kwargs = super(PieceInstanceCreate, self).get_form_kwargs()
return kwargs['piece']
success_url = reverse_lazy('piece-detail', kwargs={'pk': get_form_kwargs(self)})
urls.py
path('piece/<int:pk>', views.PieceDetailView.as_view(), name='piece-detail')
You don't pass it to reverse_lazy. Instead of using success_url, you should define the get_success_url method, which allows you to create the URL dynamically using whatever parameters you want.
However there are few other things wrong with your code here. Firstly, you should not be trying to do all that calculation based on the HTTP_REFERER attribute. If your view needs a piece of information, you should pass it in the URL as a keyword parameter, which you can then get in your view by using self.kwargs. In your case it looks like your view already has the pk argument; you can use self.kwargs['pk'].
Given that, your get_success_url method would look like:
def get_success_url(self):
return reverse('piece-detail', kwargs={'pk': self.kwargs['pk']})
Secondly, your get_form_kwargs method will always give a KeyError; the super method won't return a dictionary with a "piece" key, and even if it did the method must return a dict, not an individual value, including all the relevant items like the actual POST data. Again it's not clear what you are trying to do with this method; since you don't specify a custom form, it doesn't need custom kwargs. You should remove this method altogether.
Finally, you don't need to call form.save() inside your form_valid method, even with commit=False. A CreateView already assigns an instance to the form, so you can just do form.instance.piece = ....
Here the reworked and working class (using the inputs from #DanielRoseman):
class PieceInstanceCreate(LoginRequiredMixin, CreateView):
model = PieceInstance
fields = ['version', 'piece_image', 'status']
def form_valid(self, form):
form.instance.piece = Piece.objects.get(id=self.kwargs['pk'])
return super(PieceInstanceCreate, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('piece-detail', kwargs={'pk': self.kwargs['pk']})
You don't need to do that when you use CBV
Just see this example:
models.py
class Author(models.Model):
name = models.CharField(max_length=200)
def get_absolute_url(self):
return reverse('author-detail', kwargs={'pk': self.pk})
views.py
class AuthorCreate(CreateView):
model = Author
fields = ['name']
I have an issue with my DetailView. I want to make sure both values are in the url string and then want to display the page. However I am always receiving this error here:
KeyError at /orders/ticket/ug2dc78agz-1/d04fkjmo37/
'order_reference'
views.py
class TicketView(DetailView):
model = Attendee
template_name = 'orders/ticket_view.html'
def get_queryset(self):
return Attendee.objects.filter(
order__order_reference=self.kwargs['order_reference'],
).filter(
access_key=self.kwargs['access_key'],
)
urls.py
urlpatterns = [
path(
'ticket/<slug:ticket_reference>/<slug:access_key>/',
TicketView.as_view(),
name='ticket'
),
]
You get the error because you are trying to access self.kwargs['order_reference'], but you don't use order_reference in the path().
Your path() uses,
'ticket/<slug:ticket_reference>/<slug:access_key>/'
therefore you can use self.kwargs['ticket_reference'] and self.kwargs['access_key'].
Since your path does not contain slug or pk, Django will not know how to fetch the object for the detail view. I would override get_object instead of get_queryset:
def get_object(self):
return get_object_or_404(
Attendee,
order__order_reference=self.kwargs['slug:ticket_reference'],
access_key=self.kwargs['access_key'],
)
You have ticket_reference url variable, but in view using order_reference. You should rename it:
class TicketView(DetailView):
model = Attendee
template_name = 'orders/ticket_view.html'
def get_queryset(self):
return Attendee.objects.filter(
order__order_reference=self.kwargs['ticket_reference'],
).filter(
access_key=self.kwargs['access_key'],
)
I'm using Django django.forms.Form and django.views.generic.edit.FormView to render a HTML template.
I would to add a default value for some of the fields in my form but it encounters a problem:
Here is my code:
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView
class SignForm(forms.Form):
greeting_message = forms.CharField(
label='Greeting message',
widget=forms.Textarea,
required=True,
max_length=100,
)
book_name = forms.CharField(
label='Guestbook name',
max_length=10,
required=True,
)
class SignView(FormView):
"""Assign initial value for field 'book_name'."""
form_class = SignForm(
initial={
'book_name': 'aaaa'
}
)
def form_valid(self, form):
...
Can anyone help me?
Like the comment above says, you must assign form_class just to the class. By doing what you have with the parenthesis and arguments, you are instantiating an object, which is why you have an exception.
Instead, to set initial data, define the get_initial function, like so:
def get_initial(self):
initial = super(SignView, self).get_initial()
initial['book_name'] = 'aaaa'
return initial
Docs are available here.
You are attributing an instance to form_class instead of a form class like the name of the attribute implies. SignForm is the class, SignForm(*args) is an instance.
You should override the get_initial() method in the view:
def get_initial():
return {'book_name': 'aaaa'}
I'm using Class Based Views for the first time. I'm having trouble understating how using class based views I would implement django-endless-pagination twitter styling paging.
Could I have an example of how one would go about this?
This is my view:
class EntryDetail(DetailView):
"""
Render a "detail" view of an object.
By default this is a model instance looked up from `self.queryset`, but the
view will support display of *any* object by overriding `self.get_object()`.
"""
context_object_name = 'entry'
template_name = "blog/entry.html"
slug_field = 'slug'
slug_url_kwarg = 'slug'
def get_object(self, query_set=None):
"""
Returns the object the view is displaying.
By default this requires `self.queryset` and a `pk` or `slug` argument
in the URLconf, but subclasses can override this to return any object.
"""
slug = self.kwargs.get(self.slug_url_kwarg, None)
return get_object_or_404(Entry, slug=slug)
Since this is a broad question, I would like to combine several solutions for pagination now.
1.Use the generic ListView:
from django.views.generic import ListView
class EntryList(ListView):
model = Entry
template_name = 'blog/entry_list.html'
context_object_name = 'entry_list'
paginate_by = 10
It would be way faster using only urls.py:
url(r'^entries/$', ListView.as_view(model=Entry, paginate_by=10))
So basically you don't need django-endless-pagination in this solution. You can check the example of template here: How do I use pagination with Django class based generic ListViews?
2.Use django-endless-pagination's AjaxListView:
from endless_pagination.views import AjaxListView
class EntryList(AjaxListView):
model = Entry
context_object_name = 'entry_list'
page_template = 'entry.html'
Or faster (again) with urls.py only:
from endless_pagination.views import AjaxListView
url(r'^entries/$', AjaxListView.as_view(model=Entry))
Reference: http://django-endless-pagination.readthedocs.org/en/latest/generic_views.html
If anyone knows different solution, please comment.