Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
295 lines (235 sloc) 10.3 KB

Creating a Project with Django

Home Previous

Now that we know how to create virtual environments and use pip, we can begin building our project. In this article, we will create our first Django project, write tests, and start our development server.

Creating the Virtual Environment

First, let's create a new virtual environment for this project. (If you haven't already, deactivate the previous virtualenv by typing deactivate in the terminal). For more info on virtual environments and how to use them, visit this page.

Navigate to the directory where you want the Django project and type the following into the terminal:

mkvirtualenv taskplanner --python=/usr/bin/python3

You may have to change your Python path if it looks different than the one above.

The command line shell should now look like below, indicating you are in a virtual environment.

(taskplanner)[munsterberg@Lenovo ~/workspace] $

If it doesn't look like that, just type:

workon taskplanner

We can now install Django:

pip install Django

Create our Django Project

With Django installed we can create our project:

django-admin.py startproject taskplanner

Next, navigate into our new project by typing:

cd taskplanner

Before we do anything, let's set this directory as our working directory using virtualenvwrapper:

setvirtualenvproject

Sidenote: For a list of virtualenvwrapper commands, type virtualenvwrapper into your terminal.

Now, when we are in our virtual environment, we can type cdproject to navigate straight to our working directory.

Your project directory should look something like this:

taskplanner // our main working directory
|--- manage.py // similar to the django-admin script, you will see this used a
               // lot throughout our project
|--- taskplanner
    |--- __init__.py // this just tells python to treat this directory as a package
    |--- settings.py // main configuration file for our project
    |--- urls.py // we will use this to configure urls
    |--- wsgi.py // this is used for deploying our project to a production server

Functional Testing

Test-driven development is a widely used best practice in developing software. Bascially, we want to write a test first that is bound to fail, and then write the least amount of code possible to pass that test. With Django, our goal is to write both functional tests (also known as; integration tests, end-to-end tests, etc.), and unit tests throughout development. Don't sweat it, testing isn't as difficult as it seems!

But first, we need to create a new virtual environment dedicated to testing. Open a new tab in your terminal, navigate to your taskplanner project directory and type:

mkvirtualenv taskplanner_test --python=/usr/bin/python3

Now you should have 2 tabs open in your terminal, one in the (taskplanner) virtual environment, and the other in the (taskplanner_test) virtual environment.

If you type pip freeze in our new testing environment (taskplanner_test), you will notice nothing appears. This is because we have yet to install anything in our new environment.

So let's go ahead and install Django first into our testing environment (taskplanner_test):

pip install Django

To create our functional tests we are going to need a few things. First, we need to have the Firefox web browser installed in our machine. If you don't have Firefox, install that now.

Sidenote: You can use Chrome for integration testing, but you have to download the driver here and follow this stack overflow question. Firefox has had historically better performance than chrome when running integration tests, which is a very important consideration since compared to unit tests, integration tests are extremely slow.

This is because integration tests are testing the entire system, rather than 'units' (small components). In the real world, sometimes it's best to avoid integration tests because of the long development time to create them, slow run time, ambiguous errors, and other reasons you would discover in time.

However, they are still worth our consideration when developing a real world app, and can be very useful in terms of reliability despite the performance downsides.

Next, we need to install a package called Selenium. This package will provide us with a WebDriver so we can control a browser with our tests. Selenium is usually used to automate your browser.

pip install selenium

Now that we have that installed, we will need a directory to create our tests:

mkdir functional_tests

In the taskplanner directory you should now see the following:

taskplanner
|-- functional_tests
|--- manage.py
|--- taskplanner
    ...

We now need to create a few files in our functional_tests folder. We will create an __init__.py file (this will tell python to treat functional_tests like a package), and a test_all_users.py file to contain our tests.

Let's do that now:

touch functional_tests/__init__.py
touch functional_tests/test_all_users.py

Sidenote: __init__.py is almost always an empty file. For more info on what it's used for, see this stackoverflow answer.

We can finally start writing our first functional test! Functional tests are for testing chunks of functionality in our web application. TDD with Python describes functional tests as "how the application functions from the user’s point of view".

So let's open the test_all_users.py file in our text editor. First, we want to import selenium's webdriver, and to make this a lot easier, Django provides something known as StaticLiveServerTestCase for live testing. Let's import both of those now:

from selenium import webdriver
from django.contrib.staticfiles.testing import StaticLiveServerTestCase

Since we are testing from the users perspective, let's name these tests NewVisitorTest. Add the following:

class NewVisitorTest(StaticLiveServerTestCase):
    def setUp(self):
        self.browser = webdriver.Firefox()
        self.browser.implicitly_wait(2)

    def tearDown(self):
        self.browser.quit()

First, we create a StaticLiveServerTestCase class named NewVisitorTest, this will contain our tests that we want to run for a new visitor. Then, we have two methods named setUp and tearDown. The setUp method is initialized when we run our tests. So, for each test we run, we open Firefox and wait 2 seconds for the page to load. tearDown runs after each test is finished, this method closes the browser for us after each test.

Now we can write our first test, and have Firefox open and close automatically for us. Let's write our test now below the tearDown method.

    def test_home_title(self):
        self.browser.get('http://localhost:8000')
        self.assertIn('Welcome to Django', self.browser.title)

Our first test, how exciting! Let's walk through it. Every test we want to create must start with 'test'. For example, if I wanted to create a test for my css, I would call the method test_h2_css. So here, we named the test test_home_title. That's pretty self-explanatory, which is exactly what we want for our tests. The method first brings Firefox to the url 'http://localhost:8000', and then it checks if 'Welcome to Django' is in the html head tags title.

Let's run this test now and see what happens:

python manage.py test functional_tests

First, what exactly are we typing here? The manage.py script provides us with something called 'test', we will use this to run all of our tests. Here we are running it on our functional_tests package that we created with the __init__.py file.

After running this you should see something like the following in your terminal:

F
======================================================================
FAIL: test_home_title (functional_tests.test_all_users.NewVisitorTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/username/url/to/project/taskplanner/functional_tests/test_all_users.py", line 15, in test_home_title
    self.assertIn('Welcome to Django', self.browser.title)
AssertionError: 'Welcome to Django' not found in 'Problem loading page'

----------------------------------------------------------------------
Ran 1 test in 4.524s

FAILED (failures=1) 

So it failed, but it gave us some handy advice. First, the AssertionError. 'Welcome to Django' not found in 'Problem loading page'. So that means the title of 'http://localhost:8000' was 'Problem loading page'. If you navigate to the url, you will see that the web page was not available.

Let's try running our Django server to get the test to pass. Switch back to the terminal tab that is in the taskplanner virtual environment and run our server.

python manage.py runserver

You should see something like the following:

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.

March 06, 2016 - 20:53:38
Django version 1.9.4, using settings 'taskplanner.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Don't worry about the unapplied migrations message yet.

Now that we have a server running on http://localhost:8000, lets run our test again.

Go back to the other terminal tab that is in the taskplanner_test virtual environment and run the following once more:

python manage.py test functional_tests

You should see the following.

Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 4.033s

OK
Destroying test database for alias 'default'...

What We've Done So Far

Our first passing test!

We've covered a lot in this article. We created our first project, set up virtual environments for both development and testing purposes, wrote our first functional test, and followed the Test-driven development process by writing a failing test, and then making it making it pass.

Something went wrong with that request. Please try again.