What is Django?
Django is a web framework written in Python and follows MVC (Model-View-Controller). For a quick review on MVC, check out my post on ASP.NET.
A web framework is a piece of code that is designed to help make building web apps easier. For example, a web framework will probably have libraries that help you easily access a database or help you manage sessions.
I will also assume you already have pip. If not, go here.
To install the official Django release, open the command prompt and type:
pip install Django
Verify the Installation
Open the Python command line and type:
import django print(django.get_version())
If everything worked correctly, you should see the current version number (for me, it was 1.9.7).
Initial Django Setup
The rest of this tutorial is based off of the official Django tutorial (which you can find here).
Before your first project, we’ll want to generate some code that will establish a project that takes care of database configuration and other options.
Open the command prompt in the window where you’d like this code to live. Then type:
django-admin startproject mysite
Where mysite is your site name (for me, this will be pantrio).
In total, this created the following files and folders:
mysite/ manage.py mysite/ __init__.py settings.py urls.py wsgi.py
The outermost folder is just a container and the name doesn’t matter.
manage.py is a command line utility that will let us interact with the project.
The innermost folder is the Python package for the project. The name of this folder will be important to us later.
The __init__.py file is empty now but we’ll think of it as a Python package later on.
settings.py is where the Django settings will be held.
urls.py is the ‘table of contents’ for our site.
Finally, wsgi.py is the entry point for WSGI-compatible web servers.
Verify The Server
Move into the outer mysite folder and run the command
python manage.py runserver
You should see something similar to this as an output:
Performing system checks... System check identified no issues (0 silenced). You have unapplied migrations; your app may not work properly until they are applied. Run 'python manage.py migrate' to apply them. June 05, 2016 - 16:11:53 Django version 1.9.7, using settings 'pantrio.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CTRL-BREAK.
This command started a development server. This is a lightweight server written entirely in Python (which is really cool!). This server is intended only for developing. Don’t use this as a production server, it is just for developing!
Lets check out the page. With your web browser, go to http://127.0.0.1:8000/. You should see a white and blue page that says something like ‘It Worked! Congratulations on your first Django-powered page.’
Create a Polls App
A web app is application that does something (such as takes a poll). This is different than a project. A project is a collection of configurations and apps for a specific website. Either can contain multiples of the other.
To create the app (which we’ll name polls), make sure you are in the same directory as the manage.py file. Type the command:
python manage.py startapp polls
This creates a folder named polls with the contents:
polls/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py views.py
Make a View
Remember MVC? Here, we’ll create our first View.
Open the file polls/views.py in a text editor and add the following code:
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.")
The file should already have the line:
from django.shortcuts import render
Make sure you leave it.
Now, we need to map this view to a URL. Make a urls.py file inside the polls folder and put the following code into it:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.index, name='index'), ]
Now we need to point the URLconf to the module we just created (polls.url). Go to the mysite/url.py file and add ‘include’ after ‘import url’ on line 16 and add url(r’^polls/’, include(‘polls.urls’)), to the urlpatterns variable. The file entire file should look like this:
from django.conf.urls import include,url from django.contrib import admin urlpatterns = [ url(r'^polls/', include('polls.urls')), url(r'^admin/', admin.site.urls), ]
Restart your server (remember, python manage.py runserver) and navigate to http://localhost:8000/polls/. You should simply see:
Hello, world. You’re at the polls index.
Setup the Database
Open mysite/settings.py. This is a Python module. The variables represent settings within Django.
Django by default uses SQLite (though it is compatible with more robust databases like PostgreSQL). We will leave it with SQLite for this tutorial. However, if you think you’ll need a more robust database in the future, it is far better to move over to the other database now than it will be later.
First, we should update the TIME_ZONE variable. This should be around line 109. By default, this is set to UTC. I’ll change mine to US/Eastern, since I’m in the Eastern timezone.
Near the top of the file, there is a variable called INSTALLED_APPS. These are provided by default because they are common and convenient. However, some of these require at least one database table and we don’t currently have any. To create the tables, go to your mysite directory in the command line and run:
python manage.py migrate
The migrate command here looks at the values in the INSTALLED_APPS variable of the settings.py file and will automatically create any of the database tables needed for those installed apps. It even does this according to the database settings within the settings.py file, so it is really convenient.
Think of a model as a layout. It contains the behavior for the data that you are storing. We want to define these behaviors in one place.
For our poll, we will have two models: question and choice. The question model will have a question and a publication date. The choice will have the text of the choice and the vote tally. Obviously, each choice will be associated with a question.
We will create each module as Python class. Put the following code the polls/models.py file.
class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') class Choice(models.Model): question = models.ForeignKey(Question, on_delete = models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default = 0)
If you’ve been following along with my database posts, these two classes will probably feel very familiar to you. Though the syntax isn’t similar to SQL, the actual language is very similar. The Choice class has a Foreign Key attribute that links it to the Question class. Each item within each class has its type specified (char, datetime, integer, etc). These look so familiar to databases because each class variable represents a database field in the model!
These ‘class items’ (question_text, pub_date) are actually called field instances. These will ultimately end up as columns in our database. Django is able to create the database tables and create a Python database-access API for accessing the objects. Next, we need to tell it that the polls app is installed.
Add the following line to the INSTALLED_APPS variable in the mysite/settings.py file:
In the end, this variable should look like this:
INSTALLED_APPS = [ 'polls.apps.PollsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
To make this migration, run the command:
python manage.py makemigrations polls
The makemigrations command tells Django that we have changed the models (we added new ones) and it knows then to store these changes as a migration.
In Django, a migration is how model changes (and therefore our database schema) are stored. These are just files. You can see this most recent migration at polls/migrations/0001_intial.py.
You should get an output that looks similar to this:
Migrations for 'polls': 0001_initial.py: - Create model Choice - Create model Question - Add field question to choice
Next, we need to apply those changes to the database by using the command:
python manage.py migrate
Using the API
Django comes with a free API and shell. To start that up, run the command:
python manage.py shell
This opens up a normal instance of Python with a specific addition. The manage.py file sets the DJANGO_SETTINGS_MODULE environment variable, which gives Django the Python import path to your settings.py file, which lets you use the stuff we’ve been setting up.
We can import the classes that we wrote by typing:
from polls.models import Question, Choice
We can see all of the Question objects by doing:
We haven’t added any questions to the database, so this will return an empty list.
To add a question, we can use the commands:
from django.utils import timezone q = Question(question_text="What's new?", pub_date=timezone.now()) q.save()
Notice that the Question function has the same attributes as our field instances from our class (which are also the columns for the table in the database).
We can get the ID for this by using:
We can see the question text and publication dates by using:
If you want to change the question text, you can do that with the line:
q.question_text = "What's up?" q.save()
We can now see all of the objects in Question by using the same command as before:
This output of this is not so helpful:
[<Question: Question object>]
We can fix how this appears by editing the polls/models.py file. Add __str__() methods to both Question and Choice, such that the models.py file now looks like this:
class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') def __str__(self): return self.question_text class Choice(models.Model): question = models.ForeignKey(Question, on_delete = models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default = 0) def __str__(self): return self.choice_text
Now, when we use the Python shell and check Question.objects.all(), we get a much more meaningful output:
[<Question: What's up?>]
We will want to add __str__() methods to all of our models because it is convenient for us (as shown above) but also because the objects’ representations are automatically generated with Django and they will be more meaningful with the __str__() method.
We can add custom methods to our models as well. Add the following imports and method, so that your models.py file looks like:
from __future__ import unicode_literals from django.db import models import datetime from django.utils import timezone # Create your models here. class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') def __str__(self): return self.question_text def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1) class Choice(models.Model): question = models.ForeignKey(Question, on_delete = models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default = 0) def __str__(self): return self.choice_text
Below are some comments and activities to test out (from the official Django tutorial):
>>> from polls.models import Question, Choice # Make sure our __str__() addition worked. >>> Question.objects.all() [<Question: What's up?>] # Django provides a rich database lookup API that's entirely driven by # keyword arguments. >>> Question.objects.filter(id=1) [<Question: What's up?>] >>> Question.objects.filter(question_text__startswith='What') [<Question: What's up?>] # Get the question that was published this year. >>> from django.utils import timezone >>> current_year = timezone.now().year >>> Question.objects.get(pub_date__year=current_year) <Question: What's up?> # Request an ID that doesn't exist, this will raise an exception. >>> Question.objects.get(id=2) Traceback (most recent call last): ... DoesNotExist: Question matching query does not exist. # Lookup by a primary key is the most common case, so Django provides a # shortcut for primary-key exact lookups. # The following is identical to Question.objects.get(id=1). >>> Question.objects.get(pk=1) <Question: What's up?> # Make sure our custom method worked. >>> q = Question.objects.get(pk=1) >>> q.was_published_recently() True # Give the Question a couple of Choices. The create call constructs a new # Choice object, does the INSERT statement, adds the choice to the set # of available choices and returns the new Choice object. Django creates # a set to hold the "other side" of a ForeignKey relation # (e.g. a question's choice) which can be accessed via the API. >>> q = Question.objects.get(pk=1) # Display any choices from the related object set -- none so far. >>> q.choice_set.all()  # Create three choices. >>> q.choice_set.create(choice_text='Not much', votes=0) <Choice: Not much> >>> q.choice_set.create(choice_text='The sky', votes=0) <Choice: The sky> >>> c = q.choice_set.create(choice_text='Just hacking again', votes=0) # Choice objects have API access to their related Question objects. >>> c.question <Question: What's up?> # And vice versa: Question objects get access to Choice objects. >>> q.choice_set.all() [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>] >>> q.choice_set.count() 3 # The API automatically follows relationships as far as you need. # Use double underscores to separate relationships. # This works as many levels deep as you want; there's no limit. # Find all Choices for any question whose pub_date is in this year >>> Choice.objects.filter(question__pub_date__year=2016) [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>] # Let's delete one of the choices. Use delete() for that. >>> c = q.choice_set.filter(choice_text__startswith='Just hacking') >>> c.delete()
That’s it for this introduction. Next time, we’ll check out the built in admin tool, write more meaningful views, and look at error handling.
I feel like Django is going to be a harder subject for me to grasp. I’ve tried to go through Django tutorials in the past and I have always given up. I am grasping it much more this time around, thanks mostly to my knowledge with RDBMS and in general a better understanding of programming, MVC, and Python.
Have questions or suggestions? Please feel free to comment below or contact me.