0% found this document useful (0 votes)
49 views27 pages

L10-Django Forms & Class-Based Views

Uploaded by

123 456
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
49 views27 pages

L10-Django Forms & Class-Based Views

Uploaded by

123 456
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 27

CSC309 – LECTURE 10

DJANGO FORMS & CLASS-BASED VIEWS


Khai Truong
Django Forms
https://docs.djangoproject.com/en/4.2/topics/forms/
Working with forms
Django provides support for working with HTML forms
Frontend: converts Django fields to input elements & renders form
from django import forms
class NameForm(forms.Form):
name = forms.CharField(label='Your name', max_length=100)

<label for="id_name">Your name:</label>
<input type="text" name="name" maxlength="100" required id="id_name">

Backend: sanitizes and validates form data


Convention for large projects
Creating & importing Django forms
Create a forms directory and put each form class in a separate file
Add __init__.py and import each form class
Validating forms
Override the clean() method to add custom logic
By the time the form’s clean() method is called, sanitization has been
done already
def clean(self):
data = super().clean()
user = authenticate(username=data['username'], password=data['password'])
if user:
data['user'] = user
return data
raise ValidationError({'username' : 'Bad username or password'})
Using Django forms
Create a new instance of a form with POST or GET data
Check if form data is_valid() (which first initiates the form validation)
If is_valid returns true, cleaned_data can be accessed; otherwise
cleaned_data may not exist or has bad data
def get_name(request):
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
# process form here
name = form.cleaned_data['name']
return render(request, "thank.html", { 'name' : name })
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
ModelForm
https://docs.djangoproject.com/en/4.2/topics/forms/modelforms/
In a database-driven app, forms often map closely with Django models
Defining field types in a form would be redundant when they have
already been defined in a model
Meta inner class is defined to automatically generate fields from a
specified model
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['pub_date', 'headline', 'content', 'reporter']
Data bound to the ModelForm can be used to create or update a
database object with the save() method
f = ArticleForm(request.POST)
article = f.save()
Form template rendering
Forms can be passed into template and rendered
E.g., {{ form }}

<label for=”id_name”>Name:</label>
<input type=”text” name=”name” maxlength=”40” required id=”id_name”>

Other rendered format: {{ form.as_p }} , {{ form.as_table }} ,


{{ form.as_ul }}
Form widgets
Some form fields can be rendered differently
E.g., a CharField can be rendered as a text input, password input,
textarea, etc.
class LoginForm(forms.Form):
username = forms.CharField(max_length=150)
password = forms.CharField(widget=forms.PasswordInput())

Widgets also allows for specification of HTML attributes


forms.Textarea(attrs={'placeholder': 'Describe mission statement'})

Not recommended for large projects


In MVC pattern, views should be separate
from controller
Django view types &
Class-Based Views
Django views
Views have 3 requirements
1. They are callable
2. They must accept an HttpRequest object as its first argument
3. They must return an HttpResponse object or raise an exception

Developers can create views using two approaches:


1. Django started out with only function-based views
2. Class-based views were added to inherit functionality
Function-based views
So far, we’ve mostly have only talked about function-based views
Easy to read and work with
from django.http import HttpResponse
def simple_response(request):
return HttpResponse("This is a response!")

One view can support multiple HTTP methods


def simple_view(request, id):
if request.method == "GET":
return HttpResponse(f"My ID is {id}")
elif request.method == "POST":
return redirect("accounts:login") Function-based views do not take
else: advantage of OOP principles
return HttpResponseNotAllowed()
Involves code that is more WET than DRY
Class-based views
A subclass of django.views.View
A new instance of the object is created for every request
HTTP requests are routed to methods of the respective names
● E.g., HTTP GET request will call view.get(request, …)

Convention for large projects: create a views directory and put each
view in a separate file
● Add __init__.py and import each view
● In urls.py, each class-based view must call the as_view() method
Comparison between views
Function-based views Class-based views
def simple_view(request, id): from django.views import View
if request.method == "GET": class SimpleView(View):
return HttpResponse(f"My ID is {id}") def get(self, request, id):
elif request.method == "POST": return HttpResponse(f"My ID is {id}")
return redirect("accounts:login") def post(self, request, *a, **k):
else: return redirect("accounts:login")
return HttpResponseNotAllowed()

In the urls.py file


urlpatterns = [
path('func/<int:id>/', simple_view, name='simple_func'),
path('cls/<int:id>/', SimpleView.as_view(), name='simple_cls'),
]
Create-Read-Update-Delete
CRUD views
Most views support CRUD actions
Django provides CRUD base classes for these views, which can be extended
to speed up development and reduce the amount of code that has to be
written
Follows a predefined set of steps
● Your job is then to
set up each step
Generic views
Generic display views: designed to display data
● DetailView, ListView
● https://docs.djangoproject.com/en/4.2/ref/class-based-views/gene
ric-display/

Generic editing views: designed to create, update, or delete data


● CreateView, UpdateView, DeleteView, FormView
● https://docs.djangoproject.com/en/4.2/ref/class-based-views/gene
ric-editing/
https://docs.djangoproject.com/en/5.0/ref/class-based-views/generic-display/#listview

List View
A page that displays a list of objects
View Template
from django.views.generic.list import ListView {% extends 'base.html' %}
from .models import Store {% block content %}
<ol>
class StoresList(ListView): {% for store in stores %}
model = Store <li><a href="{{ store.url }}">
context_object_name = 'stores' {{ store.name }}</a></li>
template_name = 'stores/list.html' {% endfor %}
</ol>
{% endblock %}

Specify template to
render
https://docs.djangoproject.com/en/5.0/ref/class-based-views/generic-display/#detailview

Detail View
A page that displays a single object
View Template
from django.views.generic.detail import DetailView {% extends 'base.html' %}
from .models import Store {% block content %}
<h1>{{ store.name }}</h1>
class StoresDetail(DetailView): <dl>
model = Store <dt>Website:</dt>
context_object_name = 'store' <dd>{{ store.url }}</dt>
template_name = 'stores/detail.html' <dt>E-mail:</dt>
<dd>{{ store.email }}</dt>
...
</dl>
Specify template to {% endblock %}
render
Display View Attributes
model: The model of the generic view, assumes Store.objects.all()

queryset: Same as model, but allows you to specify a subset or


ordering
● e.g., Store.objects.filter(is_active=True)
get_queryset(self): Also specifies queryset, but overriding this allows
for arguments
● Note: URL arguments are stored under self.kwargs
● e.g., self.kwargs['pk’]
Display View Attributes (continued)
get_object(self)
● DetailView only
● Override to retrieve object differently
● Note: Request object stored under self.request
Example: ensure only user can see own store (return 404 NOT FOUND
otherwise)
class StoresDetail(DetailView):
template_name = 'stores/detail.html' Django shortcut for retrieving object
context_object_name = 'store' or return error 404
def get_object(self):
return get_object_or_404(Store,
pk=self.kwargs['pk’],
owner=self.request.user)
Display View Attributes (continued)
context_object_name: By default, this is object_list (for ListView) or
object (for DetailView)
● Allows for alternative context name
get_context_data(self, **kwargs): Override to add extra context
class ProductView(ListView):
template_name = 'stores/products.html'
context_object_name = 'products'

# ... assume get_queryset is defined


# now template has access to variables 'store' and 'products'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['store'] = get_object_or_404(Store, pk=self.kwargs['store_id'])
return context
Form View
Most generic of Django’s editing view
Typically, only used when form has no underlying database model
form_valid() method is called when form is valid
● Where business logic should be placed
● Note: the POST request contains valid data
class LoginView(FormView):
form_class = LoginForm
template_name = 'accounts/login.html'
success_url = reverse_lazy('accounts:admin')
def form_valid(self, form):
login_user(self.request, form.cleaned_data['user'])
return super().form_valid(form)

form_invalid() method can be overridden to custom handle invalid data


Create and Update Views
CreateView: A subclass of FormView whose form_class is a ModelForm
UpdateView: A subclass of CreateView that implements the get_object()
method
A default form_valid() method is implemented to save the object

class SignupView(CreateView):
form_class = SignupForm
template_name = 'accounts/signup.html'
success_url = reverse_lazy('accounts:welcome')

def form_valid(self, form):


self.request.session['from'] = 'signup'
return super().form_valid(form)
Editing View Attributes
fields/exclude: Used to create form class automatically; requires
model to be also be defined
success_url: Redirect URL when success. Note: must use reverse_lazy
here
● URL dispatcher is loaded after the definition of the class
get_success_url(self): Needed if reverse requires additional
argument(s)
class StoresUpdate(UpdateView):
model = Store
template_name = 'stores/update.html'
fields = ['name', 'url', 'email', 'owner', 'is_active']
def get_success_url(self):
return reverse('stores:detail', kwargs={'pk' : self.kwargs['pk']})
Authenticated Views
Simplifies views where user must be logged in
Function-based views
from django.contrib.auth.decorators import login_required
@login_required(login_url=reverse_lazy('accounts:login'))
def admin(request):
return render(request, "accounts/admin.html", {})

Class-based views: requires login_url to be specified for redirect


from django.contrib.auth.mixins import LoginRequiredMixin
class DeleteUserView(LoginRequiredMixin, DeleteView):
model = User
login_url = reverse_lazy('accounts:login')
success_url = reverse_lazy('accounts:admin')
Much of this lecture was taken from content previously created by Jack Sun & Kianoosh Abbasi

Reproduction and/or sharing of course materials is prohibited. Course materials include lecture slides, course notes,
assignments, data and documents provided by the instructors. Course materials created by the instructors of this course
are their intellectual properties. They may not be shared, posted, rehosted, sold, or otherwise distributed and/or
modified without expressed permission from the authors. All such reproduction or dissemination is an infringement of
copyright and is prohibited. All rights are reserved by the instructors.

Thank you & questions?

You might also like