Horai Nuri
Horai Nuri

Reputation: 5578

Understanding Django Class-Based (DetailView and View)

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

Answers (3)

Alasdair
Alasdair

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

Serafeim
Serafeim

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

anjaneyulubatta505
anjaneyulubatta505

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

Related Questions