Reputation: 5578
I'm transitioning some Function-Based Views to Class-Based Views in order to make my views more readable. But I am not understanding quite well the use of DetailView
and how I can actually integrate it to my code in order to pass a slug
into my function.
For now, I am using View
and passing the slug
into my function as below:
#urls.py
path('preview/<slug:slug>/', views.Preview.as_view(), name='toPreview')
#views.py
@method_decorator(auth0_login_required, name='dispatch')
class Preview(View):
template_name = 'authenticated/preview.html'
@card_is_chosen
def get(self, slug, request, *args, **kwargs):
person = get_object_or_404(Person, slug=slug, status=True)
...
return render(request, self.template_name, {...})
I'm am also not quite sure if it's the best practice, in case it is then what does DetailView offer?
EDIT: Trying to use DetailView as mentionned in the comments, how can I fix this error ?
#urls.py
path('preview/<slug:slug>/', views.Preview.as_view(), name='toPreview')
#views.py
@method_decorator(auth0_login_required, name='dispatch')
class Preview(DetailView):
model = Person
template_name = 'preview.html'
@card_is_chosen
def get(self, request, *args, **kwargs):
print(slug)
#slug is not defined, how should I fetch slug parameter?
...
return render(request, self.template_name, {...})
Traceback:
File "C:\Users\...\Desktop\env\src\...\apps\businesscards\decorators.py", line 60, in wrap
return function(self, request, slug, *args, **kwargs)
File "C:\Users\...\Desktop\env\src\...\apps\businesscards\views.py", line 160, in get
person = get_object_or_404(Person, slug=slug, status=True)
NameError: name 'slug' is not defined
Upvotes: 1
Views: 2064
Reputation: 308879
Since your slug field is called slug
, you just need to specify the model
and template_name
in your detail view. The detail view will take care of fetching the person with that slug.
from django.views.generic import DetailView
@method_decorator([auth0_login_required, card_is_chosen] name='dispatch')
class PersonDetailView(DetailView):
model = Person
template_name = 'authenticated/preview.html'
def get_context_data(self, **kwargs):
"""
get_context_data is one of the important hooks in generic class
based views. It lets you add extra variables to the template context
"""
context = super(PersonDetailView, self).get_context_data(**kwargs)
context['extra'] = 'extra value'
return context
In the above, I've assumed that it's ok to decorate the dispatch
method with card_is_chosen
. If that's not the case you could add method_decorator(card_is_chosen, name='get')
instead. I've added an get_context_data
- it's not required for the code above, but it might be useful for some of the ...
code from your question that you didn't show.
If you are overriding the get
method, I would use usual signature def get(self, request, *args, **kwargs):
then fetch slug for self.kwargs
def get(self, request, *args, **kwargs):
slug = self.kwargs['slug']
You should usually avoid overriding get
and post
for generic class-based-views. You risk losing functionality or having to duplicate code. There are normally more specific attributes or methods that you can override.
Finally, don't assume that class based views are always better. Personally, I would find the following function-based-view easier to read than your Preview
view.
@auth0_login_required
@card_is_chosen
def person(request, slug):
person = get_object_or_404(Person, slug=slug, status=True)
template_name = 'authenticated/preview.html'
...
return render(request, template_name, {...})
Upvotes: 4
Reputation: 15084
Well the advantage of using CBVs is that you can re-use functionality both from other django CBVs or from your own classes/mixins. This results in having a more DRY code.
I've written a lengthy tutorial exactly about this which I recommend reading before starting to use CBVs: https://spapas.github.io/2018/03/19/comprehensive-django-cbv-guide/
Now, on your specific example: You rarely need to inherit from View
- instead you inherit from sublasses of View, for example DetailView
as you mention. Now, you could implement the Preview
view like this:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import DetailView
class Preview(LoginRequiredMixin, View):
template_name = 'authenticated/preview.html'
mode = Person
I'm not sure what the @card_is_chosen
decorator does but the above has the same functionality as your example (I've changed auth0_login_required
with login_required
because I also don't know what auth0_login_required
does). Notice that you don't need to redefine get nor you need to call any code for retrieving the object instance based on the slug.
Upvotes: 0
Reputation: 11665
We can use DetailView
as below
from django.views.generic import DetailView
@method_decorator(login_required, name='dispatch')
@method_decorator(card_is_chosen, name='get')
class Preview(DetailView):
template_name = 'authenticated/preview.html'
def get_object(self):
return get_object_or_404(Person, slug=self.kwargs['slug'], status=True)
access object in template with variable/name object
Upvotes: 2