So far we have coded views as a function. But most of the advanced projects have class based views inhertiting from
django.views.generic
In urls.py, make sure to call {class_name}.as_view()
Organization of code related to specific HTTP methods (GET, POST, etc.) can be addressed by separate methods instead of conditional branching.
Object oriented techniques such as mixins (multiple inheritance) can be used to factor code into reusable components.
Look at Documentation and list of available views for more info
In views.py, instead of using index function, this class can be used
from django.views.generic imoprt View
class CBView(View):
def get(self,request):
#contents as under index(request)
return render(request,'index.html',{'text':'Simple CBV'})
In urls.py
path("",views.CBView.as_view()),
This class is built over simple view. This class automatically renders, thereby eliminating the need for render function
In views.py
#URL : templateview/
#Template view
from django.views.generic import TemplateView
class IndexView(TemplateView):
template_name = 'index.html'
#Injecting content
def get_context_data(self,**kwargs):
# **kwargs are the other key word args
context = super().get_context_data(**kwargs)
context['text'] = "Template view!"
return context
This class is built over template view. This class takes in a model and automatically creates context dict
In models.py
# Create your models here.
class School(models.Model):
name = models.CharField(max_length=256)
principal = models.CharField(max_length=256)
location = models.CharField(max_length=256)
def __str__(self):
return self.name #This is printed when used as Foreign key
class Student(models.Model):
name = models.CharField(max_length=256)
age = models.PositiveIntegerField()
#use of related name is seen in list view
school = models.ForeignKey(School,on_delete=models.CASCADE,related_name='students')
def __str__(self):
return self.name
In views.py, instead of getting list of entries as
list = models.{class_name}.objects.all()we just use the ListView class
#List view
#URL : listview/
from django.views.generic import ListView
from . import models
#Lists all the school
class SchoolListView(ListView):
model = models.School
#This class takes in the model class name, lower cases it and creates default context object and html files
template_name = 'index.html'
# in absense of template_name, this class will look into 'basic_app/school_list.html'
context_object_name = 'schools'
# in absence of context name, it will be 'school_list'
def get_context_data(self,**kwargs):
context = super().get_context_data(**kwargs)
context['text'] = "List view!"
return context
In index.html (called from url : listview/)
{% if schools %}
<h2>List of schools : </h2>
<ol>
{% for s in schools %}
<!-- id is an autogenetrated primary key -->
<li><a href="">, </a></li>
<!-- the link becomes /listview/{id}-->
<!-- if href="/", link becomes /{id} -->
<!-- the link should match with the detailsView in urls.py -->
<!-- 'students' is a related name in foreign key -->
{% for student in s.students.all %}
<p>, years old</p>
{% endfor %}
{% endfor %}
</ol>
{% endif %}
This view creates a separate URL based on primary key for each entry in model. This looks same as ListView, except for URL mappings and naming conventions
In views.py
#Lists the details of school, like students
#Each entery in the model gets a separate URL
class SchoolDetailView(DetailView):
model = models.School
template_name = 'index.html'
# in absense of template_name, this class will look into 'basic_app/school_detail.html'
context_object_name = 'school_detail'
# in absence of context name, it will be 'school'
def get_context_data(self,**kwargs):
context = super().get_context_data(**kwargs)
context['text'] = "Detail view!"
return context
In urls.py
path('listview/<int:pk>/',views.SchoolDetailView.as_view(),name='details'), #pk stands for primary key
In index.html (renders separate for each entry under URL 'listview/{id}'
{% if school_detail %} <h2>Schools details: </h2> <p>Name : </p> <p>Principal : </p> <p>Location : </p> <h3>Students : </h3> <!-- 'students' is a related name in foreign key --> {% for student in school_detail.students.all %} <p>, years old</p> {% endfor %} {% endif %}
Creates a HTML form with fields that can be filled and added to database
In views.py
#CreateView
#URL : createview/
from django.views.generic import CreateView
#Creates a form to add entry into model
class SchoolCreateView(CreateView):
model = models.School
#fields are mandatory
fields = ('name','principal','location')
template_name = 'index.html'
#by default, looks for basic_app/school_form.html
# context variable = form
This requires get_absolute_url() method to be implemented in model class. This gives the URL to redirect upon submission of form. 'action' attribute in form element does not work.
Instead of get_absolute_url, one can also specify success_url attribute in the CBV
In models.py under the class School
#This needs to be implemented to use CreateView
def get_absolute_url(self):
return reverse("details",kwargs={'pk':self.pk})
# return "/listview/"
Each entry gets a separate URL with form fields to update. This also requires get_absolute_url() to be implemented / success_url to be defined
In views.py
#UpdateView
from django.views.generic import UpdateView
#Each entry gets a separate URL
class SchoolUpdateView(UpdateView):
model = models.School
fields = ('name','principal')
# if is used in html, it wont be rendered
template_name= 'index.html'
#by default, looks for basic_app/school_form.html
# context variable = form
#success_ur = reverse_lazy('index)
In urls.py
path('update/<int:pk>/',views.SchoolUpdateView.as_view(),name='update'),
In index.html
{% if form %} {% if not form.instance.pk %} <!-- when entered from SchoolCreateView --> <h1>Create school</h1> {% else %} <!-- when entered from SchoolUpdateView --> <h1>Update school</h1> {% endif %} <form method="POST"> {% csrf_token %} <input type="submit" class='btn btn-primary' value="Submit"> </form> {% endif %}
Each entry has a separate URL
In views.py
#DeleteView
from django.views.generic import DeleteView
from django.urls import reverse_lazy
#Each entry gets a separate URL
class SchoolDeleteView(DeleteView):
model = models.School
#reverse_lazy redirects only upon success
success_url = reverse_lazy("list")
template_name= 'school_confirm_delete.html'
#basic_app/school_confirm_delete.html is the default html file
context_object_name = 'school_delete'
#default is school
In urls.py
path('delete/<int:pk>/',views.SchoolDeleteView.as_view(),name='delete'),
In school_confirm_delete.html
<h1>Delete ?</h1> <form method="post"> {% csrf_token %} <input type="submit" class="btn btn-danger" value="Delete"> <a class="btn btn-warning" href="{% url 'details' pk=school_delete.pk %}">Cancel</a> </form>