Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
Mastering Django: Core

You're reading from   Mastering Django: Core The Complete Guide to Django 1.8 LTS

Arrow left icon
Product type Paperback
Published in Dec 2016
Publisher
ISBN-13 9781787281141
Length 694 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Nigel George Nigel George
Author Profile Icon Nigel George
Nigel George
Arrow right icon
View More author details
Toc

Table of Contents (33) Chapters Close

Mastering Django: Core
Credits
About the Author
www.PacktPub.com
Preface
1. Introduction to Django and Getting Started FREE CHAPTER 2. Views and URLconfs 3. Templates 4. Models 5. The Django Admin Site 6. Forms 7. Advanced Views and URLconfs 8. Advanced Templates 9. Advanced Models 10. Generic Views 11. User Authentication in Django 12. Testing in Django 13. Deploying Django 14. Generating Non-HTML Content 15. Django Sessions 16. Djangos Cache Framework 17. Django Middleware 18. Internationalization 19. Security in Django 20. More on Installing Django 21. Advanced Database Management Model Definition Reference Database API Reference Generic View Reference Settings Built-in Template Tags and Filters Request and Response Objects Developing Django with Visual Studio

Form handling with class-based views


Form processing generally has 3 paths:

  • Initial GET (blank or prepopulated form)
  • POST with invalid data (typically redisplay form with errors)
  • POST with valid data (process the data and typically redirect)

Implementing this yourself often results in a lot of repeated boilerplate code (see Using a form in a view). To help avoid this, Django provides a collection of generic class-based views for form processing.

Basic forms

Given a simple contact form:

# forms.py 
 
from django import forms 
 
class ContactForm(forms.Form): 
   name = forms.CharField() 
   message = forms.CharField(widget=forms.Textarea) 
 
   def send_email(self): 
       # send email using the self.cleaned_data dictionary 
       pass 

The view can be constructed using a FormView:

# views.py 
 
from myapp.forms import ContactForm 
from django.views.generic.edit import FormView 
 
class ContactView(FormView): 
   template_name = 'contact.html' 
   form_class = ContactForm 
   success_url = '/thanks/' 
 
   def form_valid(self, form): 
       # This method is called when valid form data has been POSTed. 
       # It should return an HttpResponse. 
       form.send_email() 
       return super(ContactView, self).form_valid(form) 

Notes:

  • FormView inherits TemplateResponseMixin so template_name can be used here
  • The default implementation for form_valid() simply redirects to the success_url

Model forms

Generic views really shine when working with models. These generic views will automatically create a ModelForm, so long as they can work out which model class to use:

  • If the model attribute is given, that model class will be used
  • If get_object() returns an object, the class of that object will be used
  • If a queryset is given, the model for that queryset will be used

Model form views provide a form_valid() implementation that saves the model automatically. You can override this if you have any special requirements; see below for examples.

You don't even need to provide a success_url for CreateView or UpdateView-they will use get_absolute_url() on the model object if available.

If you want to use a custom ModelForm (for instance to add extra validation) simply set form_class on your view.

Note

When specifying a custom form class, you must still specify the model, even though the form_class may be a ModelForm.

First we need to add get_absolute_url() to our Author class:

# models.py 
 
from django.core.urlresolvers import reverse 
from django.db import models 
 
class Author(models.Model): 
    name = models.CharField(max_length=200) 
 
    def get_absolute_url(self): 
        return reverse('author-detail', kwargs={'pk': self.pk}) 

Then we can use CreateView and friends to do the actual work. Notice how we're just configuring the generic class-based views here; we don't have to write any logic ourselves:

# views.py 
 
from django.views.generic.edit import CreateView, UpdateView, DeleteView 
from django.core.urlresolvers import reverse_lazy 
from myapp.models import Author 
 
class AuthorCreate(CreateView): 
    model = Author 
    fields = ['name'] 
 
class AuthorUpdate(UpdateView): 
    model = Author 
    fields = ['name'] 
 
class AuthorDelete(DeleteView): 
    model = Author 
    success_url = reverse_lazy('author-list') 

We have to use reverse_lazy() here, not just reverse as the urls are not loaded when the file is imported.

The fields attribute works the same way as the fields attribute on the inner Meta class on ModelForm. Unless you define the form class in another way, the attribute is required and the view will raise an ImproperlyConfigured exception if it's not.

If you specify both the fields and form_class attributes, an ImproperlyConfigured exception will be raised.

Finally, we hook these new views into the URLconf:

# urls.py 
 
from django.conf.urls import url 
from myapp.views import AuthorCreate, AuthorUpdate, AuthorDelete 
 
urlpatterns = [ 
    # ... 
    url(r'author/add/$', AuthorCreate.as_view(), name='author_add'), 
    url(r'author/(?P<pk>[0-9]+)/$', AuthorUpdate.as_view(),   
        name='author_update'), 
    url(r'author/(?P<pk>[0-9]+)/delete/$', AuthorDelete.as_view(),  
        name='author_delete'), 
] 

In this example:

  • CreateView and UpdateView use myapp/author_form.html
  • DeleteView uses myapp/author_confirm_delete.html

If you wish to have separate templates for CreateView and UpdateView, you can set either template_name or template_name_suffix on your view class.

Models and request.user

To track the user that created an object using a CreateView, you can use a custom ModelForm to do this. First, add the foreign key relation to the model:

# models.py 
 
from django.contrib.auth.models import User 
from django.db import models 
 
class Author(models.Model): 
    name = models.CharField(max_length=200) 
    created_by = models.ForeignKey(User) 
 
    # ... 

In the view, ensure that you don't include created_by in the list of fields to edit, and override form_valid() to add the user:

# views.py 
 
from django.views.generic.edit import CreateView 
from myapp.models import Author 
 
class AuthorCreate(CreateView): 
    model = Author 
    fields = ['name'] 
 
    def form_valid(self, form): 
        form.instance.created_by = self.request.user 
        return super(AuthorCreate, self).form_valid(form) 

Note that you'll need to decorate this view using login_required(), or alternatively handle unauthorized users in the form_valid().

AJAX example

Here is a simple example showing how you might go about implementing a form that works for AJAX requests as well as normal form POST:

from django.http import JsonResponse 
from django.views.generic.edit import CreateView 
from myapp.models import Author 
 
class AjaxableResponseMixin(object): 
    def form_invalid(self, form): 
        response = super(AjaxableResponseMixin, self).form_invalid(form) 
        if self.request.is_ajax(): 
            return JsonResponse(form.errors, status=400) 
        else: 
            return response 
 
    def form_valid(self, form): 
        # We make sure to call the parent's form_valid() method because 
        # it might do some processing (in the case of CreateView, it will 
        # call form.save() for example). 
        response = super(AjaxableResponseMixin, self).form_valid(form) 
        if self.request.is_ajax(): 
            data = { 
                'pk': self.object.pk, 
            } 
            return JsonResponse(data) 
        else: 
            return response 
 
class AuthorCreate(AjaxableResponseMixin, CreateView): 
    model = Author 
    fields = ['name'] 
lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at £13.99/month. Cancel anytime
Visually different images