Getting Started With Django: A Crash Course Documentation, Summaries of Logic

We do need to run python manage.py syncdb to get our default tables set up. ... <a class=navbar-brand href=#>PyCon Survival Guide</a>.

Typology: Summaries

2022/2023

Uploaded on 02/28/2023

eknathia
eknathia 🇺🇸

4.4

(26)

264 documents

1 / 39

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Getting Started With Django: A Crash
Course Documentation
Release 1.0
Kenneth Love
Sep 22, 2017
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27

Partial preview of the text

Download Getting Started With Django: A Crash Course Documentation and more Summaries Logic in PDF only on Docsity!

Getting Started With Django: A Crash

Course Documentation

Release 1.

Kenneth Love

Sep 22, 2017

ii

CHAPTER 1

Prerequisites:

  • A text editor or IDE
  • A terminal
  • Git
  • Python 2.
  • A Heroku account and the Heroku toolbelt installed
  • Completed the Polls tutorial

Contents:

virtualenv

We want at least virtualenv installed so we don’t have to pollute our global site-packages with our project-specific

packages. This also lets us use our requirements.txt file locally and on Heroku when we deploy later.

pip install virtualenv will install virtualenv for us. You may have to sudo this command or tell pip

to install to your user space with the --user argument.

virtualenvwrapper

If you’re on a compatible system, install virtualenvwrapper with pip install virtualenvwrapper

and add:

export WORKON_HOME=$HOME/.virtualenvs source /usr/local/bin/virtualenvwrapper.sh

to whatever config file your shell uses (e.g. ~/.bashrc or ~/.zshrc). You may then need to restart your terminal

or source the config file to make this active.

Global Layouts

My convention for site-wide templates (and partials, both site-wide and app-specific) is to prepend the file or directory

name with an _, so inside templates make a new directory named _layouts.

Inside there, we need to touch base.html and give it the following code:

<!DOCTYPE>

< html > < head > < title >{% block title %}PyCon Survival Guide{% endblock title %}</ title > < link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/ ˓→bootstrap.min.css"> < style > body { padding-bottom : 20 px ; padding-top : 70 px ; }

. messages { list-style : none ; } </ style > {% block css %}{% endblock css %} </ head > < body > < div class="navbar navbar-inverse navbar-fixed-top" role="navigation"> < div class="container"> < div class="navbar-header"> < button type="button" class="navbar-toggle" data-toggle="collapse" data- ˓→target=".navbar-collapse"> < span class="sr-only">Toggle navigation</ span > < span class="icon-bar"></ span > < span class="icon-bar"></ span > < span class="icon-bar"></ span > </ button > < a class="navbar-brand" href="#">PyCon Survival Guide</ a > </ div > < div class="navbar-collapse collapse"> </ div > </ div > </ div > < div class="jumbotron"> < div class="container">{% block headline %}{% endblock headline %}</ div > </ div > < div class="container"> {% block content %}{% endblock content %} </ div > < script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></ ˓→ script > {% block js %}{% endblock js %} </ body > </ html >

1.2. Django 3

Auth views

HomeView

Before we start any authentication views, we should probably have a home page. So, let’s make one. Our stub,

pycon/survivalguide/survivalguide/ doesn’t have a views.py, so let’s go ahead and create it with

touch survivalguide/views.py. Let’s create our first view here:

from django.views import generic

class HomePageView (generic.TemplateView): template_name = 'home.html'

Template

Now we need to touch templates/home.html and open it up for editing. It’ll be a pretty simple view so let’s

just put the following into it:

{% extends '_layouts/base.html' %}

{% block headline %}< h1 >Welcome to the PyCon Survival Guide!</ h1 >{% endblock headline ˓→%}

{% block content %} < p >Howdy{% if user.is_authenticated %} {{ user.username }}{% endif %}!</ p > {% endblock %}

URL

Finally, we need an URL so we can reach the view. In survivalguide/urls.py, add the following:

[...]

from .views import HomePageView [...]

url('^$', HomePageView.as_view(), name='home'),

Now any time we go to / on our site, we’ll get our template.

SignUpView

Now, we need to make a view for users to be able to signup at. Let’s update our survivalguide/views.py file

like so:

from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User

class SignUpView (generic.CreateView): form_class = UserCreationForm model = User template_name = 'accounts/signup.html'

4 Chapter 1. Prerequisites:

from django.contrib.auth.forms import UserCreationForm

from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, ButtonHolder, Submit

class RegistrationForm (UserCreationForm): def init(self, *args, *kwargs): super(RegistrationForm, self).init(args, **kwargs)

self.helper = FormHelper() self.helper.layout = Layout( 'username', 'password1', 'password2', ButtonHolder( Submit('register', 'Register', css_class='btn-primary') ) )

View changes

In survivalguide/views.py, we need to change our form import from:

from django.contrib.auth.forms import UserCreationForm

to

from .forms import RegistrationForm

Since we’re using relative imports, we should add:

from future import absolute_import

to the top of the file to ensure that our imports work like we want.

Change the form_class in the view to RegistrationForm and the view should be done.

Template change

Finally, in the template, change the
area to be: {% crispy form %} and load the

django-crispy-forms tags with {% load crispy_forms_tags %} near the top of the file. If we refresh

the page, we should now see a decent looking form that works to sign up our user.

LogInView

Most of LogInView is the same work as SignUpView. Since we know we’re going to need a custom form, because

we want to use django-crispy-forms, let’s start there.

Form

Back in survivalguide/forms.py:

6 Chapter 1. Prerequisites:

from django.contrib.auth.forms import AuthenticationForm

class LoginForm (AuthenticationForm): def init(self, *args, *kwargs): super(LoginForm, self).init(args, **kwargs)

self.helper = FormHelper() self.helper.layout = Layout( 'username', 'password', ButtonHolder( Submit('login', 'Login', css_class='btn-primary') ) )

View

Then, we should create a view.

[...]

from django.contrib.auth import authenticate, login, logout from django.core.urlresolvers import reverse_lazy [...] from .forms import LoginForm [...]

class LoginView (generic.FormView): form_class = LoginForm success_url = reverse_lazy('home') template_name = 'accounts/login.html'

def form_valid(self, form): username = form.cleaned_data['username'] password = form.cleaned_data['password'] user = authenticate(username=username, password=password)

if user is not None and user.is_active: login(self.request, user) return super(LoginView, self).form_valid(form) else : return self.form_invalid(form)

URL

In our survivalguide/urls.py file, we need to add a route to our new login view.

from .views import LoginView [...] url(r'^accounts/login/$', LoginView.as_view(), name='login'), [...]

1.3. Auth views 7

{% if not user.is_authenticated %} < a href="{% url 'signup' %}" class="btn btn-default navbar-btn">Register</ a > < a href="{% url 'login' %}" class="btn btn-default navbar-btn">Login</ a > {% else %} < a href="{% url 'logout' %}" class="btn btn-default navbar-btn">Logout</ a > {% endif %}

django-braces

Our views are complete and pretty solid but it’s a little weird that logged-in users can go to the login view and signup

view and that logged-out users can go to the logout view. It would also be nice to send the users messages when

something happens. Writing code to do all of these things is easy enough but there are already packages out there that

provide this functionality. Namely django-braces.

As usual, install it with pip install django-braces. Since braces doesn’t provide any models or tem-

plates, we don’t have to add it to INSTALLED_APPS, but, as we want to show messages, we should update our

base.html file to provide a place for them.

Messages

Open up templates/_layouts/base.html and add:

{% if messages %}

    {% for message in messages %} {{ ˓→message }} {% endfor %}
{% endif %}

before the .jumbotron area. This snippet will show any messages in the session in a way that Bootstrap expects.

Views

Now, back in survivalguide/views.py, we need to import django-braces, so add:

from braces import views

to the imports area near the top of the file. We need to add a few mixins and attributes to a few of the views, so let’s

do that now.

SignUpView

Add views.AnonymousRequiredMixin and views.FormValidMessageMixin to the class’s signature.

We should also add a form_valid_message attribute to the class which’ll be shown to the user when they have

successfully signed up.

The AnonymousRequiredMixin prevents authenticated users from accessing the view.

1.3. Auth views 9

LogInView

Add the same two mixins to this view as well and set a form_valid_message that tells the user that they’re logged

in.

LogOutView

LogOutView needs the views.LoginRequiredMixin and the views.MessageMixin added to it.

The LoginRequiredMixin prevents this view from being accessed by anonymous users.

We also need to update the get method on the view and add:

self.messages.success("You've been logged out. Come back soon!")

to it before the super() call.

Now all of our views should be properly protected and give useful feedback when they’re used.

The talks app

Now we can get to the meat of our project, the talks app.

startapp

To start our app, we can to tell Django to give us some boilerplate right away with python manage.py

startapp talks. We want app names to be plural, generally, as they usually concern themselves with multi-

ple model instances and work around them.

This will give us a structure similar to:

/talks

  • init.py
  • admin.py
  • models.py
  • tests.py
  • views.py

We’ll start off with the models.py in here.

Note: I’ll be giving paths relative to the talks/ directory from here on out, so be sure to adjust them in your head

as needed. Most text editors seem to offer a fuzzy file finder now, so editing should be fairly painless.

TalkList model

We want to be able to organize our talks into lists, things like “Attend or Else” and “Watch Online”. So the first thing

we should probably have is a model for the list. Open up models.py and add the following:

10 Chapter 1. Prerequisites:

15 url(r'^lists/', include(lists_patterns, namespace='lists')), 16 )

This line sets up an internal namespace of lists for all of our TalkList-specific URLs. Now we need to add the

talks namespace that our get_absolute_url mentioned.

Open up survivalguide/urls.py and add:

url(r'^talks/', include('talks.urls', namespace='talks')),

This sets up the talks namespace.

south

Now, before we actually create the model, we should add south into the mix. pip install south will install

it and we need to add 'south' to our INSTALLED_APPS. Since south has a model of its own, we also need to

run python manage.py syncdb again to add it.

We should now add our 'talks' app to INSTALLED_APPS and, instead of running syncdb, we should run

python manage.py schemamigration --initial talks. south will create a migration that gener-

ates our database table and put it in the migrations/ directory. Then we apply it with python manage.py

migrate.

Note: python manage.py schemamigration is a really long command to have to type repeatedly, so I

recommend creating a shell alias for it to save yourself some time.

Warning: Django 1.7 introduces an internal migration tool much like south. This tutorial does not cover that

tool. While south will likely work with Django 1.7, you should use the new tool instead.

Default list

Now that we have a model and a database table, let’s make our SignUpView automatically create a default list for

everyone. Open survivalguide/views.py and change SignUpView to match this:

1 [...]

2 from talks.models import TalkList 3 [...] 4 5 class SignUpView (views.AnonymousRequiredMixin, views.FormValidMessageMixin, 6 generic.CreateView): 7 form_class = RegistrationForm 8 form_valid_message = "Thanks for signing up! Go ahead and login." 9 model = User 10 success_url = reverse_lazy('login') 11 template_name = 'accounts/signup.html' 12 13 def form_valid(self, form): 14 resp = super(SignUpView, self).form_valid(form) 15 TalkList.objects.create(user=self.object, name='To Attend') 16 return resp

12 Chapter 1. Prerequisites:

TalkList Views

Our TalkLists need a few different views, so let’s look at creating those.

TalkListListView

We should have a view to show all of our lists. In views.py:

1 [...]

2 from braces import views 3 4 from. import models 5 6 class TalkListListView (views.LoginRequiredMixin, generic.ListView): 7 model = models.TalkList 8 9 def get_queryset(self): 10 return self.request.user.lists.all()

There’s not really anything fancy about this view other than overriding get_queryset. We want users to only be

able to view lists that they own, so this does that for us. We didn’t specify a template, so Django will look for the

default one at talks/talklist_list.html.

Template

Since this is an app and the templates are only for this app, I think it’s best to put them in the app. This makes it easier

to focus on the files for a specific app and it also makes it easier to make an app reusable elsewhere.

mkdir -p talks/templates/talks touch talks/templates/talks/talklist_list.html

We need to namespace the template inside of a directory named the same as our app. Open up the template file and

add in:

{% extends '_layouts/base.html' %}

{% block title %}Lists | {{ block.super }}{% endblock title %}

{% block headline %}< h1 >Your Lists</ h1 >{% endblock headline %}

{% block content %} < div class="row"> < div class="col-sm-6"> < ul class="list-group"> {% for object in object_list %} < li class="list-group-item"> < a href="{{ object.get_absolute_url }}">{{ object }}</ a > </ li > {% empty %} < li class="list-group-item">You have no lists</ li > {% endfor %} </ ul > </ div > < div class="col-sm-6"> < p >< a href="#" class="btn">Create a new list</ a ></ p >

1.5. TalkList Views 13

< p >Talks go here</ p > </ div >

< div class="col-sm-6"> < p >< a href="{% url 'talks:lists:list' %}">Back to lists</ a ></ p > </ div > </ div > {% endblock %}

A pretty standard Django template. We already have the URL so this should be completely wired up now.

RestrictToUserMixin

We had to override get_queryset in both of our views, which is kind of annoying. It would be nice to not have to

do that, especially two different ways both times. Let’s write a custom mixin to do this work for us.

class RestrictToUserMixin (views.LoginRequiredMixin): def get_queryset(self): queryset = super(RestrictToOwnerMixin, self).get_queryset() queryset = queryset.filter(user=self.request.user) return queryset

This does the same work as our get_queryset in TalkListDetailView. Let’s use it. In both views, add

RestrictToUserMixin and take out the views.LoginRequiredMixin from django-braces since our

new mixin provides that functionality too. Also remove the overrides of get_queryset from both views.

TalkListCreateView

We want to be able to create a new TalkList, of course, so let’s create a CreateView for that. In views.py,

still, add a new class:

1 [...]

2 import .forms 3 4 class TalkListCreateView ( 5 views.LoginRequiredMixin, 6 views.SetHeadlineMixin, 7 generic.CreateView 8 ): 9 form_class = forms.TalkListForm 10 headline = 'Create' 11 model = models.TalkList 12 13 def form_valid(self, form): 14 self.object = form.save(commit=False) 15 self.object.user = self.request.user 16 self.object.save() 17 return super(TalkListCreateView, self).form_valid(form)

This view has a form_class that we haven’t created yet, so we’ll need to do that soon. Also, we override

form_valid, which is called when the submitted form passes validation, and in there we create an instance in

memory, assign the current user to the model instance, and then save for real and call super() on the method.

This view also brings in the SetHeadlineMixin and provides the headline attribute. We do this because we’ll

be using the same template for both create and update views and we don’t want them to both have the same title and

1.5. TalkList Views 15

headline. This way we can control that from the view instead of having to create new templates all the time.

Let’s create the form now.

TalkListForm

We don’t yet have a talks/forms.py so go ahead and create it with the following content:

1 from future import absolute_import 2 3 from django import forms 4 5 from crispy_forms.helper import FormHelper 6 from crispy_forms.layout import Layout, ButtonHolder, Submit 7 8 from. import models 9 10 11 class TalkListForm (forms.ModelForm): 12 class Meta : 13 fields = ('name',) 14 model = models.TalkList 15 16 def init(self, (^) *args, *kwargs): 17 super(TalkListForm, self).init(args, **kwargs) 18 self.helper = FormHelper() 19 self.helper.layout = Layout( 20 'name', 21 ButtonHolder( 22 Submit('create', 'Create', css_class='btn-primary') 23 ) 24 )

Nothing really different here from our earlier forms except for line 13 which restricts the fields that the form cares

about to just the name field.

URL

As with all of our other views, we need to make an URL for creating lists. In talks/urls.py, add the following

line:

url(r'^create/$', views.TalkListCreateView.as_view(), name='create'),

Template

And, again, we didn’t specify a specific template name so Django expects talks/talklist_form.html to exist.

Django will use this form for both CreateView and UpdateView views that use the TalkList model unless we

tell it otherwise.

{% extends '_layouts/base.html' %} {% load crispy_forms_tags %}

{% block title %}{{ headline }} | Lists | {{ block.super }}{% endblock title %}

{% block headline %}

16 Chapter 1. Prerequisites: