William Liu

Django Web Framework


##Table of Contents

##Summary

Django is a web framework in Python. The idea is to be able to receive requests and send responses.

##Object Relational Mapper

Django has an Object Relational Mapper (ORM) that takes database tables and represents this as Python objects (mostly Django Models, though other types are possible).

You can either query for one object or if there are multiple objects, you’ll get an iterable called a QuerySets (which is a collection of objects from your database). This is like a SELECT statement in SQL.

You can have zero, one, or more filters to narrow down your query results. This is like the WHERE or LIMIT statements in SQL. For example, Stuff.objects.filter(name__contains='MyStuff')

If you want to get all except these specific items, then do an exclude, like Stuff.objects.exclude(name__contains='MyStuff')

You interact with a Queryset with the Model’s Manager. Each model has at least one Manager and it’s called objects by default. Managers are only accessible via the model classes, not from the model instances.

>>> Blog.objects
<django.db.models.manager.Manager object at ...>

QuerySets are lazy in that they don’t do any database activity until the QuerySet is evaluated.

####Shell and Shell_Plus

You can interact with the ORM using python manage.py shell. If you have the library django-extensions, you can use python manage.py shell_plus to automatically load in all your django models. You can exit with the exit() command.

####Difference between Object and QuerySet

Here’s the difference between an individual Object and a QuerySet (collection of objects).

>>> me = User.objects.get(username='wliu')
>>> me
<User: wliu>
>>> type(me)
accounts.models.User

>>> queryset = User.objects.all()[:1]
>>> queryset
[<User: admin>]
>>> type(queryset)
django.db.models.query.QuerySet
>>> queryset[0].name
u'admin'

####ORM Data Types

You don’t have to return just an object or a standard queryset. Django is flexible with returning:

####ORM Get

You can get specific objects from the database.

>>> me = User.objects.get(username='will')

To get all the models in your database, do:

>>> from django.apps import apps
>>> apps.get_models()
[django.contrib.admin.models.LogEntry,
 accounts.models.User,
 blog.models.BlogPost,
 blog.models.Comment
]

####ORM Object Fields

To get all the fields in your model, do:

>>> Comment._meta.fields  # does not include reverse relations
[<django.db.models.fields.AutoField: id>,
 <django.db.models.fields.related.ForeignKey: blogpost>,
 <django.db.models.fields.TextField: content>,
 <django.db.models.fields.DateField: date_created',
 <django.db.models.fields.related.OneToOneField: user>]

>>> Comment._meta.get_all_field_names()  # includes reverse relations
['blogpost',
u'blogpost_id',
 'content',
 'date_created',
 'id',
 'user']

####ORM Select All

You can select objects from the database. Suppose we have a model Post from our app blogs.

>>> from blog.models import Post
>>> Post.objects.all()
[<Post: my post title>, <Post: another post title>]

####ORM Create

You can create objects in the database.

 >>> from django.contrib.auth.models import User
 >>> Post.objects.create(author=me, title='Sample title', text='Test')

####ORM Filter

You can filter objects. You can use two _ characters between a field name and a filter operation (e.g. title and contains).

>>> Post.objects.filter(author=me)
>>> Post.objects.filter(title__contains='title')

####ORM Relational

So say you have a Blog model that has a field (user) that is related as a ForeignKey to a User model (that we’re trying to access the username field). You can filter for the Blogs that are by that user using:

>>> mypost = Blog.objects.filter(user__username__exact='william')

####ORM related_name

In Django, you can add a related_name argument on a ForeignKey, OneToOneField, or say ManyToManyField fields. The related_name attribute specifies the name of the reverse relation from the Model back to your model. For example:

class Event(models.Model):
    creator = models.ForeignKey(User, related_name='event_creator')

# Shell
me = User.objects.first()  # this is an instance of Model User
me.event_creator.all()  # this calls the 'related_name' of the Model User
#[<Event: stuff>]  # this returns an instance of an Event

####ORM Publish

You can publish items in the database.

>>> mypost = Post.objects.get(title='Sample title')
>>> mypost.publish()

####ORM Delete

You can delete items in the database.

>>> mypost = Post.objects.get(title='Sample title')
>>> mypost.delete()

####ORM Update

You can update items in the database.

>>> mypost = Post.objects.filter(title='Sample title').update(is_active=False)

####ORM Order by

You can order the list of objects.

>>> Post.objects.order_by('created_date')
>>> Post.objects.order_by('-created_date')

####ORM Limit

You can limit the number of items.

>>> Post.objects.all()[:5]  # return first 5 objects

####ORM Shortcut Combinations

A lot of times you’ll want to get an item and if it isn’t there, do an action (like Create or return a 404 response). This is a shortcut to doing the long way of Try/Except.

# Long way
try:
    obj = Person.objects.get(first_name='Will', last_name='Liu')
except Person.DoesNotExist:
    obj = Person(first_name='Will', last_name='Liu')
    obj.save()

# Shortcut
obj, created = Person.objects.get_or_create(first_name='Will', last_name='Liu')

The get_or_create shortcut will return the object and whether it was created (True or False). If multiple items appear, then error MultipleObjectsReturned is raised. Remember to only use this shortcut for POST (not for GET) and to keep this inside the views.

There’s plenty of these shortcuts including:

####ORM Order of Operations

Some operations when put together care about the order, others don’t. If you do a .annotate().filter(), everything is annotated but you only get the filtered objects. If you reverse the order, .filter().annotate(), your filtered objects are the only ones annotated. This also applies to say values() instead of filter().

####ORM Copying Objects

You can copy objects by setting the pk to None.

 >>> mypost = Post(name='My Post', comment='This is my comment')
 >>> mypost.save()  # mypost.pk == 1
 >>> mypost.pk = None
 >>> mypost.save()  # mypost.pk == 2

####ORM to values

Often you’ll want to take a Model object and turn that into a dictionary. You can specify field names by passing in the field names into values() as arguments.

# this list contains Model objects
>>> Blog.objects.filter(name__startswith='Beatles')
<Queryset [<Blog: Beatles Blog>]>

# this list contains a dictionary
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest news'}]>

####Chaining QuerySets

You can chain QuerySets together to write complex queries.

 >>> Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')

####Iterating through QuerySets

Since QuerySets are iterables of multiple objects, you can iterate through the objects.

 >>> queryset = Post.objects.all()
 >>> print([x.somefield for x in queryset])
 >>> print([x.someotherfield for x in queryset])  # this is cached from evaluation earlier

####Querysets are Lazy

Since databases are one of the slower pieces in a web application, we want to limit the number of queries to them. Querysets are just representations of rows in the database. You can apply filters, exclude rows, etc. The idea is that as you define your querysets, they’re not evaluated until you start iterating over them.

queryset = Post.objects.all()  # not evaluated yet

for i in queryset:
    print Post.title  # now it gets evaluated, hits the database

for i in queryset:
    print Post.comment  # Django automatically caches this

####Querysets Cacheing

Since Querysets are lazy (they automatically cache), we want to be efficient on how we use them. For example, using the if statement reevaluates the entire queryset and sometimes you don’t need that when you can just check if the queryset just .exists().

if queryset:  # this reevaluates the queryset and creates cache again
    print "At least one Post exists"

if queryset.exists():  # this will 
    print "At least one Post exists"

When working with huge datasets, you might want to use an iterator() to avoid unnecessary cacheing. An example would be:

data_set = Post.objects.all()

if data_set.exists():
    for data in data_set.iterator():
        print data.title

Be careful that this type of optimization may actually hurt your application if not done properly.

####ORM select_related()

If you want to follow foreign-key relationships and want to do this efficiently (without hitting the database multiple times), do a select_related(). This will add some complexity to the query, but will make the operations more efficient by having less lookups. Remember that this is mainly for cacheing so you don’t have to hit the database multiple times unnecessarily.

 >>> queryset = Post.objects.select_related('User')

####ORM Counting and Aggregation

With Models, you can count and do aggregate queries.

from django.db.models import Count, Sum, Avg, Max, Min

Post.objects.count()  # 6
Post.objects.filter(title__icontains=='cat').count()  # 1
Post.objects.all().aggregate(Sum('likes'))  # 23
Post.objects.all().aggregate(Avg('likes'))  # {'likes__avg': 4.53}
Post.objects.all().aggregate(Max('likes'))  # {'likes__avg': Decimal(5.00)}
Post.objects.all().aggregate(clicks_per_views = Sum('clicks')/Sum('views'))

####ORM annotate()

You can annotate(), which will allow you to create your own annotations (i.e. a field) as key/values that you can attach to objects. This is useful for making notes on Models about counts, aggregation, etc.

comments_per_post = Post.objects.annotate(num_comments=Count('comment'))
print type(comments_per_post)  # <class 'django.db.models.query.QuerySet'>

# annotated field accessed as an object
first_post = comments_per_post[0]  # Returns an object instead of queryset
print type(first_post)  # blog.models.Post
print first_post.num_comments  # 1

# annotated field accessed as a queryset
print([i.num_comments for i in comments_per_post])  # [1, 1, 0, 1, 1]

####ORM querysets debugging

You can look into the queryset to get some additional information:

>>> a = BlogPost.objects.select_related('User')

#To see the details of a queryset
>>> a.query.__dict__
>>> {'_aggregate_select_cache': None,
 '_aggregates': None,
 '_extra': None,
 '_extra_select_cache': None,
 'aggregate_select_mask': None,
 'alias_map': {},
 'alias_refcount': {},
 ...

# see the queryset in SQL
>>> print a.query
>>> SELECT "blog_blogpost"."id", "blog_blogpost"."title", "blog_blogpost"."content", "blog_blogpost"."date_created", "blog_blogpost"."date_updated", "blog_blogpost"."user_id" FROM "blog_blogpost" ORDER BY "blog_blogpost"."date_created" ASC

####Complex Queries with Q

A Q object is an object used to encapsulate a collection of keyword arguments. You can do more complex operations like OR statements. You create a Q object with your filters, then pass the Q object into your query / queryset.

# Example 1
>>> from django.db.models import Q
>>> Q(question__startswith='Who') | Q(question__startswith='What')  # Who or What
>>> ~Q(pub_date__year=2005) # not published on 2005
>>> Poll.objects.get(Q(question__startswith='Who', ~Q(pub_date__year=2005) | Q(pub_date__year=2002))

# Example 2
>>> a = Q(title__icontains='hello') | ~Q(content__icontains='testing')
>>> BlogPost.objects.filter(a)
# [<BlogPost: Test>, <BlogPost: cats>]

##Model Managers

There’s at least one manager for every Django Model that namespaces objects. You might notice that when you do a query, you’re doing People.objects.all(); the default Model Manager of objects has a lot of default functionality like .all(). Here’s how it looks under the hood:

from django.db import models

class Person(models.Model):
    # ...
    objects = models.Manager()

If you want to add functionality across tables (not just instances), then use Model Managers (otherwise just create Model methods).

####Custom Model Managers

You can create your own custom manager. It doesn’t have to return a queryset, it can return anything you want. If you do return a queryset, you can chain the queryset. You can also have multiple managers per model. Note: my preference is to use as_manager() below.

from django.db import models

class ManagerA(models.Manager):
    return "Stuff"  # this can be a custom queryset that applies for all tables, a dict, whatever

class ManagerB(models.Manager):
    def get_queryset(self):
        # By returning a queryset like this, we can chain querysets
        return super(ManagerB, self).get_queryset().filter(some_field='stuff')

class Book(models.Model):
    #
    objects = models.Manager()  # default manager, called e.g: Book.models.all()
    authors = ManagerA()  # called: Book.authors  # returns 'stuff'
    baboons = ManagerB()  # called: Book.baboons.all()

####Custom Query Sets

You can write custom Querysets. Note that these can also be put inside a custom Manager. Note: my preference is to use as_manager() below.

class PersonQuerySet(models.Queryset):
    def authors(self):
        return self.filter(role='A')

    def editors(self):
        return self.filter(role='E')

####As Manager

I don’t really use the Custom Model Manager or Custom QuerySets because they could end up with some duplication of code. Instead, I like making a QuerySet and then putting as_manager() like below.

class SpecificDateQuerySet(models.QuerySet):
    
    def filter_month_and_year(self, month, year):
        month_filter = Q(date__month=month)
        year_filter = Q(date__year=year)
        return self.filter(month_filter & year_filter)
    def get_cats(self):
        results = self.aggregate(Sum('cats'))
        if results['cats__sum']:
            return results['cats__sum']
        return 0

class Pets(models.Model):
    date = models.DateField()
    cats = models.IntegerField(default=0)

    SpecificDateQuerySet.as_manager()

##Migrations

Migrations is how Django updates your database schema to fit in line with the current models of the app (on a per app basis). This is done when you update a field, delete a model, etc. The core commands are:

####makemigrations command

makemigrations is done after you make changes to your models (e.g. add a field). To run, do: python manage.py makemigrations [your_app_here]. Note that you can specify / limit to a single app, but this may cause issues in schemas that have relationships (like ForeignKey, OneToOneField, ManyToManyField).

####migrate command

This synchronizes unmigrated apps (i.e. adds or removes them if they’re new or no longer there) and then it runs the migrations that have not been applied yet.

####Migration files

Migrations are actually just a Python file that list dependencies (a list of the migration names that this depends on) and operations (list of operations that define what this migration does, like what model was deleted, what field added).

####Data Migrations with RunPython

You can use migrations to change the data in the database itself; this is called data migrations. To do this, create an empty migrations file: python manage.py makemigrations --empty. We then need to modify the migrations file and add in an operation called migrations.RunPython.

from django.db import models, migrations

def combine_names(apps, schema_editor):
    Person = apps.get_model("yourappname", "Person")
    for person in Person.objects.all():
        person.name = "%s %s" % (person.first_name, person.last_name)
        person.save()

class Migration(migrations.Migration):
    dependencies = [
        ('yourappname', '0001_initial'),
    ]
    operations = [
        migrations.RunPython(combine_names),
    ]

####Squash Migrations with squashmigrations

After enough migrations (say hundreds), you might want to reduce the number of migrations to just one file that represents the same thing. Django extracts the operations and puts them all in a sequence (while taking into account if say we Create a Model, then Delete a Model).

To run this, do python manage.py squashmigrations myappname 0004 to squash the migrations up to that migration number. Be warned, this can create errors like CircularDependencyError. You can also try with the flag: --no-optimize. Note with older versions of Django using South, you may have to use python manage.py migrate --fake myappname so that it runs more than one initial migration.

After a migration is squashed successfully, make sure to:

##Views

When the server gets a request, a view decides what models and templates to use. You can render views using render and render_to_response. A view always returns either a HttpResponse or a 404.

####requests

When you get a request, there’s important information in the request.META, which is a Python dictionary that has all the available HTTP Headers (e.g. user IP Address, user agent info like the web browser). Do note that this information does not have to exist. Useful header info includes:

####render_to_response()

You receive a request and this creates a response. If you use this, your template is passed a Context instance by default (not a RequestContext).

####render()

The render shortcut is the same as render_to_response() except there is a context_instance argument that forces the use of RequestContext.

##Class Based Views (CBV) and Generic Class Based Views (GCBV)

Class Based Views allow you to respond to different HTTP methods (GET, POST) without using a bunch of if-statements. Otherwise, it’s like a Function Based View in that a View still returns a HttpResponse.

Class Based Views start with one of three base views, which are: View, TemplateView, or RedirectView. You can use the above base views with Mixins or you can use Generic Class Based Views (e.g. ListView, DetailView, CreateView, UpdateView, FormView, DeleteView, etc). Note: This is a similar setup to say Django REST Framework.

The docs here show a nice overview: https://docs.djangoproject.com/en/1.9/ref/class-based-views/flattened-index/

####CBV Flow

So what happens when you use something like a base view? The methods work like this:

####CBV Mixins

So there’s a lot of methods that you can define in your Class Based Views. For instance, if you want to get the context, you can create a get_context_data() method. If you want to get a queryset, you can create a get_queryset() method. If you don’t want to repeat all this code, you can also just use a Mixin. Remember that these Mixins just add additional functionality and sometimes the Mixins conflict (so just note that you can’t just throw in a bunch of Mixins and expect things to work).

####Debugging GCBVs

Use the mro() command to get the Method Resolution Order, which tells you the order in which methods should be inherited.

from pprint import pprint
pprint(ListView.mro())

##URLs

Django has a URL dispatcher that makes clean urls.

The way Django knows how to route the url is that it looks for a root URLconf module (normally under a variable ROOT_URLCONF in your settings). From that file, it will load the urlpatterns, which is just a Python list of url instances. Python searches through the list and as soon as a pattern matches the url, the corresponding view is called (and passed an instance of the HttpRequest). The ordering matters because the key concept is that the first pattern matched is routed. If no pattern is found then we return an error.

Example:

# urls.py
urlpatterns = [ 
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
]

A request to the url /articles/2005/03/ would match the urlpattern above, then call the function with the following keyword arguments: views.month_archive(request, '2005', '03').

In the view, you might see something like this:

# views.py
def month_archive(request, year=2001, month=1, template='/myapp/site.html'):
    """ Set defaults as January 2001 """
    ...
    return render(request, template)

####URL Regex Patterns

To make a url regular expression (aka regex) pattern, we want to make sure to:

####Named URL patterns

You should use named URL patterns, like the below. When naming your url, make sure to use appname-viewfunctionname; this way you won’t get conflicts between multiple urls.

# urls.py
urlpatterns = [
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), name='news-month_archive'  # assuming an app named 'news'
]

The reasoning is that you can then use a url template tag to access data in a template.

####URL Passing Optional Data

You can pass optional information as a dictionary, like this:

urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), name='news-year-archive'
]

####URL include() and namespace

With a lot of apps, we’ll have multiple urls.py files. What happens is that when you use reverse() template function, we might get the wrong URL if the URL-pattern matches the name in another app.

If you specify the arguments namespace and app_name, we can determine the current app and call the app_name based on the namespace. This is useful for when you have multiple apps that have a ‘polls’ view. For example:

# views.py
# we set request.current_app = request.resolver_match.namespace

#urls.py
from django.conf.urls import patterns, url, include

urlpatterns = [
    url(r'^author-polls/', include('polls.urls', namespace='author-polls', app_name='polls')),
    url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls', app_name='polls')),
]

# We can then call these in the template
url 'polls:index'   # gets the current namespace (say 'author-polls'), app(poll), urlpattern name (index)

With include(), we can do something like include(mysmallerapp.urls) to add the urls in the file mysmallerapp.urls into the global namespace.

##Template

A template determines how to display information. Technically, a template is a text file that can be any text format (e.g. HTML, CSV, XML). These templates contain variables, which are replaced with values when the template is evaluated. There are also tags, which control the logic of the template (e.g. for loop through a list of items).

The template engine’s goal is meant to express presentation of data, not program logic.

####Template Variables

Template variables look like ``. When a variable is encountered, the engine replaces the variable with the result. Use the _ if you need spacing and the . if you need to access attributes of the variable.

####Template Tags

Template tags can have for-loops and if, elif, else statements. Variables can also be passed from the view to the template. If any variables are invalid, they are seen as None.

####for-loops

<ul>

</ul>

####Custom Template Tags

If you want to make some custom presentation logic, you can create your own tags to really do anything. For example, if you want an alert about any issues, you can define a custom tag that shows these alerts anywhere you put the tag. Or you might want to show some debug information by simply putting a tag.

You can either use some of the built-in helpers that Django has (e.g. simple_tag, assignment_tag, inclusion_tag) or you can make your own tag from scratch.

The general idea is that you can define these functions in your templatetags folder (same level as models.py, views.py), then make them available by using the load tag (e.g. load mystuff_tags) while making sure to restart your development server AND naming the file the same as the tag (e.g. mystuff_tags.py).

from django import template

register = template.Library()  # to be valid tag library, needs this variable

####simple_tag and assignment_tag

Here’s how to use the built-in shortcut simple_tag, which calls the tag and accepts any number of arguments. The idea is that you can do a custom tag to do any logic with Python (e.g. pass in a python list) and output some custom formatting in HTML all in a single tag. There’s another tag called assignment_tag that saves a variable into the context so you can use it later instead of directly outputting the results using as argument.

# block_tags.py
import datetime
from django import template

register = template.Library()

@register.simple_tag
def current_time(format_string):
    return datetime.datetime.now().strftime(format_string)

@register.simple_tag(takes_context=True)
def current_time(context, format_string):
    timezone = context['timezone']
    return your_get_current_time_method(timezone, format_string)

# load block_tags
# current_time

Note that if we have a lot of variables, we can use: takes_context=True and store the values in the context.

####Inclusion tags

Inclusion tags displays some data by rendering another template. This is good if you have a lot of different logic or CSS for say a button or object.

# Template Tags
@register.inclusion_tag('results.html')
def show_results(posts):
    blog_posts = Blog.objects.all()
    return {'blog_posts': blog_posts}  # notice, has to be a dict

# Template: results.html
<ul>

</ul>

# Template: main.html
show_results mydata

# What it looks like in main.html
<ul>
    <li>First Post</li>
    <li>Second Post</li>
    <li>Third Post</li>
</ul>

####Advanced Custom Template Tags

To write an advanced custom tag, we have two steps: we need to specify how to compile and then render the templating system.

When Django compiles a template, the raw template text is split into nodes where each node is an instance of django.template.Node and has a render() method. The compiled template is simply a list of Node objects.

What we’re doing is telling how the raw template tag is converted into a node (the compilation function) and what the node’s render() method does. For example:

# Templatetags
# Custom Template Tag: Compilation
@register.tag
def do_current_time(parser, token):
    try:
        # split_contents() knows not to split quoted strings
        tag_name, format_string = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError("%r tag requires\
            exactly one argument" % token.contents.split()[0])
    if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
        raise template.TemplateSyntaxError("%r tag's argument should be \
            in quotes" % tag_name)
    return CurrentTimeNode(format_string[1:-1])

# Custom Template Tag: Rendering
class CurrentTimeNode(template.Node):
    def __init__(self, format_string):
        self.format_string = format_string

    def render(self, context):
        return datetime.datetime.now().strftime(self.format_string)
    
register.tag(do_current_time)

#Template to load custom tags, say main.html

load blog_tags
#do_current_time "%Y-%m-%d %I:%M %p"

####Custom Template Filters

Custom filters are Python functions that take one or two arguments:

As an example, the filter `` the filter foo would be passed the variable var and the argument “bar”.

Example:

from django import template

register = template.Library()  # to be valid tag library, needs this variable
    
@register.filter  # by default, Django will use the funciton's name to register
def lower(value):  # only one argument
    """ converts a string into all lowercase """
    return value.lower()

@register.filter(name='cut')
def cut(value, arg):
    """ Removes all values of arg from the given string """
    return value.replace(arg, '')

@register.filter(name='novowels')
def novowels(value, arg):
    """ Remove any vowels from value """
    vowels = ['a', 'e', 'i', 'o', 'u']
    if any(word in value for word in vowels):
        single_letters = list(value)
        for i, value in enumerate(single_letters):
            if value in vowels:
                single_letters.remove(value)

return single_letters

register.filter('novowels', novowels)
register.filter('cut', cut)
register.filter('lower', lower)

# Use Case in Template
#

##Forms

Forms allow you to accept input from users and processes how to respond.

####HTML Forms

In HTML, forms are a collection of elements inside the <form> ... </form> tags that allow users to enter text, select options, and basically provide user input (often using the HTML <input> elements). Forms must specify:

####Form GET and POST

With Django’s GET method, a GET is used to send data that does not affect the state of the system and can be public (e.g. web search would appear as website.com/search/?forms&release=1).

With Django’s POST method, the browser gets all the form data together, encodes it to send, sends it to the server, then receives back a response. POST is used to make changes in the database or for private data.

####Django HTML Form Template

Here is a form that gets a user’s name through the <input> tags, returns the form to the URL /your-name/ with a POST method, displays a text field label indicating to the user to input their name, and pre-fills the `` if it exists.

<form action="/your-name/" method="post">
    <label for="your_name">Your name: </label>
    <input id="your_name" type="text" name="your_name" value="">
    <input type="submit" value="OK">
</form>

####Django Form Process

In Django, when we render an object, we typically do these steps:

Rendering a form has similar steps except when we instantiate a form, we have the possible options:

####Django Forms Code

If we want to move away from the Django HTML Form Template and into Python code, we can do that with Django forms. When we look at Django’s forms, we need to be aware if the form is bound (data associated with it) or unbound form (no data associated with it). You can check if a form is bound by checking the attribute .is_bound

# forms.py
from django import forms

class NameForm(forms.Form):
    your_name = forms.CharField(label='Your name', max_length=100)

This code takes the Form instance and does a .is_valid() method check, which runs validation routines for all the fields. When this is called, if all fields have valid data, it returns True and places the form’s data in the cleaned_data attribute.

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

Note that this does not include the <form> tags or a submit button, we’ll need to manually provide these.

The form is sent to the view.

#views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect

from .forms import NameForm

def get_name(request):
    # if POST request, we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request
        form = NameForm(request.POST)
        # check if form is valid
        if form.is_valid():
            # Process the data in form.cleaned_data
            # Then redirect to a new URL
            return HttpResponseRedirect('/thanks/')
    # if GET (or any other method) we create a blank form
    else:
         form = NameForm()

    return render(request, 'name.html', {'form': form})

The new HTML template would be the below, where `` unpacks the Python code into HTML:

<form action="/your-name/" method="post">
    #remember to put csrf_token
    
    <input type="submit" value="Submit" />
</form>

Note that if you are creating an editable item and you want to edit existing content inside that object, you can instantiate it with the object.

 obj = MyStuff.objects.get(id=pk)
 form = MyStuffForm(instance=obj)

##Context

Once you have a Template object, you can reuse the same template to render it several times with different contexts. A context is basically a dictionary with variables names as the key and the values as the value. Normally you pass a fully populated dictionary to Context() and modify it using standard dictionary syntax.

The context class is under django.template.Context and takes a couple arguments (name of the application and a dictionary mapping of the variable names to variable values)

>>> from django.template import Template, Context
>>> template = Template("My name is .")
>>> context = Context({"my_name": "Will"})
>>> template.render(context)
"My name is Will."
>>> context = Context({"my_name": "Bill"})
>>> template.render(context)
"My name is Bill."

##Context Stack: Push() and Pop()

A Context object is a stack; this means you can push() and pop(). By default a Context has ‘True’, ‘False’, ‘None’ values defined.

>>> c = Context()
>>> c['foo'] = 'first level'
[{'False': False, 'None': None, 'foo': 'first level'}]
>>> c.push()
{}
>>> c
[{'False': False, 'None': None, 'foo': 'first level', 'True': True}, {}]
>>> c['foo'] = 'second level'
[{'False': False, 'None': None, 'foo': 'first level', 'True': True}, {'foo': 'second level'}]

####RequestContext

There’s a special subclass of the Context class called a RequestContext that instead of taking an application name, takes a HttpRequest. This RequestContext takes in a context_processors configuration option that returns a dictionary of items to be merged into the context. This is under django.template.RequestContext.

####Context Processor

In Django, if you want to put the context dictionary everywhere, you can create a Context Processor. So remember that a context is just a dictionary of keys and values being mapped into a template. A context processor is just a variable in your settings.py file (TEMPLATE_CONTEXT_PROCESSORS) where the purpose is that you don’t need to specify what each context is, the key/values are automatically included in all your requests. A use case would be to insert certain variables inside your template that you would like to use in everything (e.g. a User’s location). If you don’t want to include these dictionaries everywhere, you can just specify for a specific instance by using only RequestContext. You can also add an optional third argument called processors to pass in some additional processors.

# want context processors listed in settings.py as well as some more specific ones
return render_to_response('template.html', {'foo':'bar'}, context_instance=RequestContext(request, processors=extra_processors))

# want only context processors listed in settings.py
return render_to_response('template.html', {'foo':'bar'}, context_instance=RequestContext(request))

# no context processors
return render_to_response('template.html', {'foo':'bar'})

##Users

Django handles both user authentication and authorization. Authentication says that a user is who they claim to be (authentication is through django.contrib.auth). Authorization says what a user is allowed to do (permissions is through django.contrib.contenttypes.

>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user(username='will', email='william.q.liu@gmail.com', password='mypassword')
>>> user.set_password('newpassword')
>>> user.save()

####Create Superusers

You can create superusers using python manage.py createsuperuser --username=will --email=william.q.liu@gmail.com

####Permissions

Django can assign permissions to individual users and to groups of users in the admin site. You can extend this to your site if you want. Users have an add, change, and delete permissions.

####Login Required

So what can you do with users? You can make certain views available only if the user is logged in using the @login_required decorator (from django.contrib.auth.decorators import login_required). If the user isn’t logged in, it redirects them to settings.LOGIN_URL.

####Permission Required

To check if a user has a particular permission, we can use the permission_required decorator.

from django.contrib.auth.decorators import permission_required

@permission_required('polls.can_vote')
def my_view(request):
    ....

####User Passes Test

Additionally, you can have the logged in User pass a certain check (e.g. if email ends in @jobwaffle.com) before accessing a view. Remember that users can be anonymous.

from django.contrib.auth.decorators import user_passes_test

def email_check(user):
    return user.email.endswith('@jobwaffle.com')

@user_passes_test(email_check)
def my_view(request):
    ...

##Serializers

Django has a serialization framework that translates Django models into other usable formats like XML, JSON, or YAML. Note that Django’s serializers is different than the Python json library.

####dumpdata management command

If you want to get data from your tables, you can run python manage.py dumpdata. You can specify the format (e.g. JSON), the app, and/or database.

####loaddata management command

If you want to get data into tables, you can run python manage.py loaddata. You can specify the format (e.g. JSON), the app, and/or database. This is good for loading fixtures.

##Middleware

Middleware hooks into Django’s request and response cycle. It’s a plugin system for globally altering the input or output. You can activate common middleware (like Authentication, CSRF protection, messaging, sessions), or you can write your own. Middleware is defined in your settings.py as MIDDLEWARE_CLASSES.

####Middleware Order

Remember that the order of the middleware matters because it’ll determine when the middleware is processed.

####Middleware Folders

You normally setup your middleware in:

Then the actual middleware looks like:

 class MyMiddleWare(object):
     def process_request(self, request):
         print "My middleware has been accessed!"
         print request.path

##Signals

Django has signals that help decoupled applications get notifications when something occurs. There are senders that send a signal to alert receivers that take some action has taken place. In order to be decoupled from applications, I recommend placing a signals folder right under your app name, then registering a handlers.py and an apps.py.

####Built-In Signals

There are built-in senders that include:

####Listening to Signals with Receivers

In order to receive a signal, you have to make and register a receiver function that gets called when a signal is sent. We accomplish this with Signal.connect().

In this example, we make the receiver function (my_callback) that is called when a request is finished (using the built-in request_finished signal).

from django.core.signals import request_finished, pre_save
from myappname.models import MyModel

# Receiver function
def my_callback(sender, **kwargs):
    print "Request finished!"

@receiver(pre_save, sender=MyModel)  # can use a decorator instead of connect()
def my_callback_specific_model(sender, **kwargs):
    print "This is only called when MyModel does a pre_save"
    
# connect signal, dispatch_uid is used to make sure no duplicate signals
request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")

####Sending Signals

You can send signals using two ways (Signal.send(sender, **kwargs) or Signal.send_robust(sender, **kwargs)) where sender is a Class. THe difference between these two methods is that send() does not catch any exceptions raised by receivers while send_robust() catches errors and receives are notified. These return a list of tuple pairs ([(receiver, response), ...]). For example:

import django.dispatch

class PizzaStore(object):

    def send_pizza(self, toppings, size):
         pizza_done.send(sender=self.__class__, toppings=toppings, size=size)

# creates a pizza_done signal that provide receivers with arguments for 'toppings' and 'size'
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

##Admin

Django has an Admin that allows other users (admins) to quickly do CRUD operations. We use a ModelAdmin class that represents the model in the admin interface.

####Admin Setup

In your app, create an admin.py file and register your models with their respective ModelAdmin.

from django.contrib import admin

from .models import MyModel, OtherModel

class MyCustomFilter(admin.SimpleListFilter):
    title = _('has pants?')
    parameter_name = 'pants'  # parameter that appears on the URL

    def lookups(self, request, model_admin):
        """ 
        returns list of tuples: first element is value that
        appears in the URL; second element is human readable value
        that appears in admin sidebar
        """
        return (
            ('yes_pants', _('pant titles only')),
            ('no_pants', _('no pant titles'))
        )

    def queryset(self, request, queryset):
        """
        returns filtered queryset based on value provided in the
        query string and retrievable via 'self.value()'.
        """
        if self.value() == 'yes_pants':
            return queryset.filter(title__icontains='pant')
        if self.value() == 'no_pants':
            return queryset.exclude(title__icontains='pant')

class OtherModelTabularInline(admin.TabularInline):
    model = OtherMOdel
    readonly_fields = ('y_field', 'x_field')

def get_extra(self, request, obj=None, **kwargs):
    """ Only show 1 extra place """
    extra = 1
    return extra

class OtherModelStackedInline(admin.StackedInline):
    model = OtherModel

class MyModelAdmin(admin.ModelAdmin):
    inlines = [OtherModelTabularInline, ]
    fields = ('myfield', 'anotherfield')  # display fields in detail
    list_display = ('myfield', 'somefield')  # display fields in object overview
    list_filter = ('user', MyCustomFilter)

class OtherModelAdmin(admin.ModelAdmin):
    pass

admin.site.register(MyModel, MyModelAdmin)
admin.site.register(OtherModel, OtherModelAdmin)

####Admin Customizations

By default, the ModelAdmin just lists all fields. We can specify what fields to include, what fields should be joined together (e.g. (('first_name', 'last_name')), what fields are grouped together (using fieldsets), what models should be displayed together (admin.TabularInline, admin.StackedInline), any custom filters for the admin (e.g. admin.SimpleListFilter, or if there’s any extra formatting we should apply (e.g. css classes using classes)

##Custom Management Commands

In Django, you can create your own manage.py customcommand just like syncdb or migrate, but probably not that complex. These are good for running standalone scripts. For example, if you want to update a field that is the average ratings for each Blog Post, you would write a script for this.

####Custom Management Commands Setup

In your Django app, create a directory management/commands/ and make sure to put your init.py files (if Python 2). The file that you create (say you name it update_post_ratings.py) will be your command that you use to execute (e.g. python manage.py update_post_ratings)

####Custom Management Commands Code

We define a Command that can take in arguments (none, required, or optional) and then runs a handler.

from django.core.exceptions import FieldError
from django.core.management.base import BaseCommand, CommandError

from accounts.models import User
from blog.models import Post


class Command(BaseCommand):
    args = "<username>"
    help = "Calculates the Average Blog Likes for that User"

    def add_arguments(self, parser):
        parser.add_argument('username', nargs='+', type=str)

    def handle(self, *args, **options):

        if args:
            try:
                user = User.objects.get(username=args[0])
                result = Post.objects.filter(user=user)
                result = result.aggregate(Avg('likes'))
                if result.get('likes__avg') is not None:  # handles empty
                    print result
            except FieldError:
                raise CommandError('Could not find a user by that name')
        else:
            raise CommandError('Please enter a username')

##Sessions

Django has a built-in sessions framework, which is Django’s tool for keeping a persistent state of data for users on the server through the use of cookies. This can be set by including the right middleware and by default session data will be stored in a Django Model under the database, though there’s other options like storing session info into Cookies, etc. With this enabled, every HttpRequest has a request.session dictionary-like object attached that holds specific information.

####Accessing Session data in a View with request.session

In every HttpRequest, we get a request.session that has a dictionary where we can access and write to. Additionally there are specific functions for setting cookies, testing cookies, deleting cookies, etc.

>>> print(request.session.keys())
>>> print(request.session.items())
# [(u'_auth_user_hash', u'fdlajfklasfjklasfjlasfjl'), (u'_auth_user_id', 1), (u'_auth_user_backend', u'django.contrib.auth.backends.ModelBackend')]
>>> print()
>>> request.session.set_test_cookie()

To set and access session data in a view, we just use a single session attribute for querying and modifying the session:

def test_session(request):
    request.session['stuff'] = 10
    print request.session['stuff']

The important thing is that when a user sees the actual HTTP traffic, the cookie does not have the hidden variable ‘stuff’ and instead only shows the sessionid. In Google Chrome, you can find this under: chrome://settings/cookies

Set-Cookie:sessionid=a92d67e44a9b92d7dafca67e507985c0;
       expires=Thu, 07-Jul-2011 04:16:28 GMT;
       Max-Age=1209600;
       Path=/

####How Django stores Sessions

By default, Django’s session module stores sessions in the main DB’s django_session. The session_key is the ID in the cookie. The session_data contains the actual session data.

from django.contrib.sessions.models import Session

mysession = Session.objects.get(pk='a92d67e44a9b92d7dafca67e507985c0')
print mysession.session_data  # ZmEyNDVhNTBhMTk2ZmRjNzVlYzQ4NTFjZDk2Y2UwODc3YmVjNWVjZjqAAn1xAVUFY291bnRxAksGcy4=
print mysession.get_decoded()  # {'stuff': 10}

Be aware that Session data does not get cleaned automatically. When a person visits the site, they create a session. If they log out, the

####Accessing Session data out of a View with SessionStore

You can access Session data outside of a View with SessionStore. request.session is a SessionStore object.