Innovate anywhere, anytime withruncode.io Your cloud-based dev studio.
Django

Django - Migrating from Function Based Views to Class Based Views

2022-07-25

The single most significant advantage in Django class-based views is inheritance. On a large project, it's likely that we will have lots of similar views. Instead of writing the repeated code we can simply have our views inherit from a base view. Also, Django ships with a collection of generic view classes that can be used to do some of the most common tasks.

1. Template View

Function based view

urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
         url(r'^about-us/$', views.about_us, name="about_us"),
]

views.py

from django.shortcuts import render
def about_us(request):
       return render(request, 'templates/contact.html')

Class based view

urls.py

from django.conf.urls import url
from .views import AboutUs
urlpatterns = [
         url(r'^about-us/$', AboutUs.as_view(), name="about_us"),
]

views.py

from django.views.generic import TemplateView
class AboutUs(TemplateView):
        template_name = "templates/about.html"

or we can directly write it in    "urls.py" 

urls.py

from django.conf.urls import url
from django.views.generic import TemplateView
urlpatterns = [
         url(r'^about-us/$', TemplateView.as_view(template_name= "templates/about.html"), name="about_us"),
]

2. Detail View

Function based view

urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
         url(r'^author/(?P<pk>[0-9]+)/details/$', views.author_details, name="author_details"),
]

views.py

from django.shortcuts import render
from django.shortcuts import get_object_or_404
from books.models import Author

def author_details(request, pk):
       author = get_object_or_404(Author, id=pk)
       context = {'author': author}
       return render(request,
                             context, 
                             'templates/author_details.html')

Class based view

urls.py

from django.conf.urls import url
from .views import AuthorDetails
urlpatterns = [
         url(r'^author/(?P<pk>[0-9]+)/details/$', AuthorDetails.as_view(), name="author_details"),
]

views.py

from django.views.generic import DetailView
from books.models import Author

class  AuthorDetails(DetailView):
        model = Author
        slug_field = 'pk'         #  for identifying the object uniquely
        context_object_name = 'author'         # this name is used access the object inside template (it's optional)
        template_name = "templates/author_details.html"

we can access object in template as  "object" or model name with lowercase letters "modelname" (ex:  object.name or author.name)

Note : To send extra context data to the template we can override  the super class method  get_context_data 

from django.views.generic import DetailView
from .models import Author, Book

class  AuthorDetails(DetailView):
        model = Author
        slug_field = 'pk'
        template_name = "templates/author_details.html"
        
        def get_context_data(self, **kwargs):
               context = super(AuthorDetails, self).get_context_data(**kwargs)
               context['book_list'] = Book.objects.filter(author=self.get_object())
               return context

3. List View

Function based view

urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
         url(r'^authors-list/$', views.authors_list, name="authors_list"),
]

views.py

from django.shortcuts import render
from books.models import Author
def authors_list(request):
       authors_list = Author.objects.all()
       context = {'authors_list': authors_list}
       return render(request,
                             context, 
                             'templates/authors_list.html')

Class based view

urls.py

from django.conf.urls import url
from .views import AuthorDetails
urlpatterns = [
         url(r'^authors-list/$', AuthorsList.as_view(), name="authors_list"),
]

views.py

from django.views.generic import ListView
from books.models import Author

class  AuthorsList(ListView):
        model = Author
        template_name = "templates/authors_list.html"

we can access objects in template as  "object_list" or model name with lowercase letters "modelname_list" 

List view with dynamic filtering

urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
         url(r'^authors-list/subject/(?P<category>[-\w]+)/$', views.authors_list, name="authors_list"),
]

views.py

from django.shortcuts import render
from books.models import Author
def authors_list(request, category):
       authors_list = Author.objects.filter(category=category)
       context = {'authors_list': authors_list,
                        'category': category}
       return render(request,
                             context, 
                             'templates/authors_list.html')

Class based view

urls.py

from django.conf.urls import url
from .views import AuthorsList
urlpatterns = [
         url(r'^authors-list/subject/(?P<category>)/$', AuthorsList.as_view(), name="authors_list"),
]

views.py

from django.views.generic import ListView
from books.models import Author

class  AuthorsList(ListView):
        model = Author
        template_name = "templates/authors_list.html"
        paginate_by = 10      # It will paginate the objects such that  each page contains atmost 10 objects
        
        def get_queryset(self):
              query_set = self.model.objects.filter(category=self.kwargs.get('category'))
              return query_set

we are overriding the super class method "get_queryset"  to get  desired query set.

4. Form View

forms.py

from django import forms
from django.core.mail import send_mail

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    subject = forms.CharField(max_length=100, required=False)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)

    def send_email(self):
        name = self.cleaned_data.get('name', '')
       subject = self.cleaned_data.get('subject', '')
        email = self.cleaned_data.get('name', '')
        message = self.cleaned_data.get('message', '')

        send_mail( subject,
                          message,
                          email,
                          ['[email protected]'],
                          fail_silently=False, )

Function based view

urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
         url(r'^contact-us/$', views.contact_us, name="contact_us"),
]

views.py

from .forms import ContactForm
django.http import HttpResponseRedirect

def contact_us(request):
      form = ContactForm()
       if request.POST:
            form = ContactForm(request.POST)
            if form.is_valid():
                form.send_email()
                return HttpResponseRedirect("/thanks/")
       context = {'form': form}
       return render(request,
                             context, 
                             'templates/contact_us.html')

Class based view

urls.py

from django.conf.urls import url
from .views import  ContactUs
urlpatterns = [
         url(r'^contact-us/$', ContactUs.as_view(), name="contact_us"),
]

views.py

from django.views.generic.edit import FormView
from .forms import ContactForm

class ContactUs(FormView):
     template_name = 'templates/contact_us.html'
     form_class = ContactForm
     success_url = '/thanks/'

     def form_valid(self, form):
          form.send_email()
          return super(ContactUs, self).form_valid(form)

5. Create View

forms.py

from django import forms
from .models import Author

class AuthorForm(forms.ModelForm):
    class Meta:
          model = Author
          fields  = ("first_name",
                         "last_name",
                         "about",
                         "country")

Function based view

urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
         url(r'^create-author/$', views.create_author, name="create_author"),
]

views.py

from .forms import AuthorForm
django.http import HttpResponseRedirect

def create_author(request):
      form =AuthorForm()
       if request.POST:
            form = AuthorForm(request.POST)
            if form.is_valid():
                form.save()
                return HttpResponseRedirect("/authors-list/")
       context = {'form': form}
       return render(request,
                             context, 
                             'templates/create_author.html')

Class based view

urls.py

from django.conf.urls import url
from .views import  CreateAuthor
urlpatterns = [
         url(r'^create-author/$', CreateAuthor.as_view(), name="create_author"),
]

views.py

from django.views.generic.edit import CreateView
from .forms import AuthorForm
from .models import Author

class CreateAuthor(CreateView):
     model = Author                 # provide if you don't use model form
     template_name = 'templates/create_author.html'
     form_class = AuthorForm
     success_url = '/authors-list/'

6. Update View

Function based view:

urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
         url(r'^update-author/(?P<author_id>[\d]+)/$', views.update_author, name="update_author"),
]

views.py

from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from .forms import AuthorForm
from .models import Author

def update_author(request, author_id):
      author =get_object_or_404(Author, id=author_id)
      if request.POST:
            form = AuthorForm(request.POST, instance=author)
            if form.is_valid():
                  form.save()
                  return HttpResponseRedirect("/authors-list/")
      form = AuthorForm(instance=author)
      context = {'form': form}
      return render(request,
                            context, 
                            'templates/create_author.html')

Class based view

urls.py

from django.conf.urls import url
from .views import  UpdateAuthor
urlpatterns = [
         url(r'^update-author/(?P<author_id>[\d]+)/$', UpdateAuthor.as_view(), name="update_author"),
]

views.py

from django.views.generic.edit import UpdateView
from .forms import AuthorForm
from .models import Author

class UpdateAuthor(UpdateView):
     model = Author              # provide if you don't use model form
     slug_field = 'author_id'
     template_name = 'templates/create_author.html'
     form_class = AuthorForm
     success_url = '/authors-list/'

7. Delete View

Function based view:

urls.py

from django.conf.urls import url
from . import views
urlpatterns = [
         url(r'^delete-author/(?P<author_id>[\d]+)/$', views.delete_author, name="delete_author"),
]

views.py

from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from .models import Author

def delete_author(request, author_id):
      author = get_object_or_404(Author, id=author_id)
      author.delete()
      return HttpResponseRedirect("/authors-list/")

Class based view

urls.py

from django.conf.urls import url
from .views import DeleteAuthor
urlpatterns = [
         url(r'^delete-author/(?P<author_id>[\d]+)/$', DeleteAuthor.as_view(), name="delete_author"),
]

views.py

from django.views.generic.edit import DeleteView
from .models import Author

class DeleteAuthor(DeleteView):
     model = Author
     slug_field = 'author_id'
     success_url = '/authors-list/'

You can always override super class methods if required,  see UML diagrams of Django-generic views for better understanding http://epydoc.pythondiary.com/generic-views/