
Chapter 5. Working with Models
The website we just created contains only static data; however, what we want to do is store data so as to automate all the tasks. That's why there are models; they will put a link between our views and the database.
Django, like many frameworks, proposes database access with an abstraction layer. This abstraction layer is called object-relational mapping (ORM). This allows you to use the Python implementation object in order to access the data without worrying about using a database. With this ORM, we do not need to use the SQL query for simple and slightly complex actions. This ORM belongs to Django, but there are others such as SQLAlchemy, which is a quality ORM used especially in the Python TurboGears framework.
A model is an object that inherits from the Model
class. The Model
class is a Django class that is specifically designed for data persistence.
We define fields in models. These properties allow us to organize data within a model. To make a connection between databases and SQL, we can say that a model is represented by a table in the database, and a model property is represented by a field in the table.
In this chapter, we will explain:
- How to set up access to the database
- How to install South for the database migrations
- How to create simple models
- How to create a relationship between models
- How to extend our models
- How to use the administration module
Databases and Django
Django can interface with many databases. However, during the development of our application, we use SQLite libraries that are included in Django.
We will modify settings.py
to set our connection to the database:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(PROJECT_ROOT, 'database.db'), 'USER': '', 'PASSWORD': '', 'HOST': '', 'PORT': '', } }
The following is the description of the properties mentioned in the preceding code:
- The
ENGINE
property specifies the type of database to be used. - The
NAME
property defines the path and final name of the SQLite database. We use a syntax usingos.path.join
to our code, and it is compatible with all operating systems. The file's database will be contained in the project directory. - The other properties are useful when we use a database server, but as we will use SQLite, we do not need to define them.
Migrations with South
South is a very useful extension of Django. It facilitates the migration of the database when changing fields. It also keeps a history of the changes in the structure of the database.
We talk about it now because it must be installed before the creation of the database to work correctly.
Django 1.7 incorporates a migration system. You will not need to use South anymore to make the migration of a Django application. You can find more information about the migration systems integrated into Django 1.7 at https://docs.djangoproject.com/en/dev/topics/migrations/.
Installing South
To install South, we use the pip
command. We have already used it to install Django. To do this, run the following command:
pip install South
Before actually using South, we must change the settings.py
file for South to be well integrated in Django. To do this, you must go to INSTALLED_APPS
and add the following lines (depending on the version, it is possible that the installation of South added the line):
'south', 'TasksManager',
Using the South extension
Before we make our first migrations and generate our database, we also have to create the schema migration. To do this, we must run the following command:
manage.py schemamigration TasksManager --initial
Then, we must perform an initial migration:
manage.py syncdb --migrate
Django asks us to first create an account. This account will be a superuser. Remember the login and password that you enter; you will need this information later.
South is now fully operational. Each time we need to modify the models, we will make a migration. However, for the migration to be made correctly, you must keep the following things in mind:
- Never perform the Django
syncdb
command. After runningsyncdb --migrate
for the first time, never run it again. Usemigrate
afterwards. - Always put a default value in the new fields; otherwise, we will be asked to assign a value.
- Each time we finish editing our models, we must execute the following two commands in the correct order:
manage.py schemamigration TasksManager –auto manage.py migrate TasksManager
Creating simple models
To create models, we must have already studied the application in depth. Models are the basis of any application because they will store all the data. Therefore, we must prepare them carefully.
Concerning our Tasksmanager
application, we need a user who saves tasks performed on a project. We'll create two models: User
_django
and Project
.
We need to store our models in the models.py
file. We will edit the models.py
file in the TasksManager
folder. We do not need to modify the configuration file, because when you need the model, we will have to import it.
The file already exists and has a line. The following line allows you to import the base model of Django:
from django.db import models
The UserProfile model
To create the UserProfile
model, we ask ourselves the question, "what data about the user do we need to keep?". We will need the following data:
- The user's real name
- A nickname that will identify each user
- A password that will be useful for user authentication
- Phone number
- Date of birth (this is not essential, but we must study the dates!)
- The date and time of the last connection
- E-mail address
- Age (in years)
- The creation date of the user account
- A specialization, if it is supervisor
- The type of user
- A supervisor, if you are a developer
The model that is needed is as follows:
class UserProfile(models.Model): name = models.CharField(max_length=50, verbose_name="Name") login = models.CharField(max_length=25, verbose_name="Login") password = models.CharField(max_length=100, verbose_name="Password") phone = models.CharField(max_length=20, verbose_name="Phone number" , null=True, default=None, blank=True) born_date = models.DateField(verbose_name="Born date" , null=True, default=None, blank=True) last_connection = models.DateTimeField(verbose_name="Date of last connection" , null=True, default=None, blank=True) email = models.EmailField(verbose_name="Email") years_seniority = models.IntegerField(verbose_name="Seniority", default=0) date_created = models.DateField(verbose_name="Date of Birthday", auto_now_add=True)
We have not defined the specialization, type of user, and supervisor, because these points will be seen in the next part.
In the preceding code, we can see that Django_user
inherits from the Model
class. This Model
class has all the methods that we will need to manipulate the models. We can also override these methods to customize the use of models.
Within this class, we added our fields by adding an attribute in which we specified the values. For example, the first name field is a character string type with a maximum length of 50 characters. The verbose_name
property will be the label that defines our field in forms. The following is a list of the commonly used field types:
CharField
: This is a character string with a limited number of charactersTextField
: This is a character string with unlimited charactersIntegerField
: This is an integer fieldDateField
: This is a date fieldDateTimeField
: This field consists of the date as well as the time in hours, minutes, and secondsDecimalField
: This is a decimal number that can be defined precisely
Tip
Django automatically saves an id
field in auto increment. Therefore, we do not need to define a primary key.
The Project model
To save our projects, we will need the following data:
- Title
- Description
- Client name
These factors allow us to define the following model:
class Project(models.Model): title = models.CharField(max_length=50, verbose_name="Title") description = models.CharField(max_length=1000, verbose_name="Description") client_name = models.CharField(max_length=1000, verbose_name="Client name")
To comply with good practices, we would not have had to define a text field for the customer, but define a relationship to a client table. To simplify our first model, we define a text field for the client name.
The relationship between the models
Relationships are elements that join our models. For example, in the case of this application, a task is linked to a project. Indeed, the developer performs a task for a particular project unless it is a more general task, but it's out of the scope of our project. We define the one-to-many type of relationship in order to denote that a task always concerns a single project but a project can be connected to many tasks.
There are two other kinds of relationships:
- The one-to-one relationship sets apart a model in two parts. The resulting database will create two tables linked by a relationship. We will see an example in the chapter on the authentication module.
- The many-to-many relationship defines relationships with many records in one model can be connected to many records of the several other models of the same type. For example, an author can publish several books and a book may have several authors.
Creating the task model with relationships
For the task model, we need the following elements:
- A way to define the task in a few words
- A description for more details about the task
- A past life
- Its importance
- The project to which it is attached
- The developer who has created it
This allows us to write the following model:
class Task(models.Model): title = models.CharField(max_length=50, verbose_name="Title") description = models.CharField(max_length=1000, verbose_name="Description") time_elapsed = models.IntegerField(verbose_name="Elapsed time" , null=True, default=None, blank=True) importance = models.IntegerField(verbose_name="Importance") project = models.ForeignKey(Project, verbose_name="Project" , null=True, default=None, blank=True) app_user = models.ForeignKey(UserProfile, verbose_name="User")
In this model, we have defined two foreign key field types: project
and app_user
. In the database, these fields contain the login details of the record to which they are attached in the other table.
The project
field that defines the relationship with the Project
model has two additional attributes:
Null
: This decides whether the element can be defined as null. The fact that this attribute is in theproject
field means that a task is not necessarily related to a project.Default
: This sets the default value that the field will have. That is, if we do not specify the value of the project before saving the model, the task will not be connected to a domain.
Extending models
The inheritance model allows the use of common fields for two different models. For example, in our App_user
model, we cannot determine whether a random recording will be a developer or supervisor.
One solution would be to create two different models, but we would have to duplicate all the common fields such as name, username, and password, as follows:
class Supervisor(models.Model): # Duplicated common fields specialisation = models.CharField(max_length=50, verbose_name="Specialisation") class Developer(models.Model): # Duplicated common fields supervisor = models.ForeignKey(Supervisor, verbose_name="Supervisor")
It would be a shame to duplicate the code, but it is the principle that Django and DRY have to follow. That is why there is an inheritance model.
Indeed, the legacy model is used to define a master model (or supermodel), which contains the common fields to several models. Children models automatically inherit the fields of the supermodel.
Nothing is more explicit than an example; we will modify our classes, Developer
and Supervisor
, to make them inherit App_user
:
class Supervisor(UserProfile): specialisation = models.CharField(max_length=50, verbose_name="Specialisation") class Developer(UserProfile): supervisor = models.ForeignKey(Supervisor, verbose_name="Supervisor")
The result of the legacy database allows us to create three tables:
- A table for the
App_user
model that contains the fields for the properties of the model - A table for the
Supervisor
model, with a text field for specialization and a field that has a foreign key relationship with theApp_user
table - A
Developer
table with two fields: a field in liaison with theSupervisor
table and a field that links to theApp_user
table
Now that we have separated our two types of users, we will modify the relationship with App_user
because only the developer will record his or her tasks. In the Tasks
model, we have the following line:
app_user = models.ForeignKey(App_user, verbose_name="User")
This code is transformed as follows:
developer = models.ForeignKey(Developer, verbose_name="User")
For the generation of the database order to work, we must put models in the correct order. Indeed, if we define a relationship with a model that is not yet defined, Python will raise an exception. For the moment, the models will need to be defined in the order described. Later, we shall see how to work around this limitation.
In the next chapter, we will perform queries on the model. This requires the database to be synchronized with the models. We must first migrate South before starting the next chapter.
To perform the migration, we must use the commands we've seen at the beginning of the chapter. To simplify the migration, we can also create a batch file in the Python folder, where we will put the following lines:
manage.py schemamigration TasksManager --auto manage.py migrate pause
The following is a bash script you can create in the Work_manager
folder that can perform the same thing on Debian Linux:
#!/bin/bash manage.py runserver 127.0.0.1:8000
This way, when you migrate South, it will execute this file. The pause
command allows you to look at the results or errors displayed without closing the window.
The admin module
The administration module is very convenient and is included by default with Django. It is a module that will maintain the content of the database without difficulty. This is not a database manager because it cannot maintain the structure of the database.
One question that you may ask is, "what does it have other than a management tool database?". The answer is that the administration module is fully integrated with Django and uses these models.
The following are its advantages:
- It manages the relationships between models. This means that if we want to save a new developer, the module will propose a list of all the supervisors. In this way, it will not create a non-existent relationship.
- It manages Django permissions. You can set permissions for users according to models and CRUD operations.
- It is quickly established.
Being based on Django models and not on the database, this module allows the user to edit the recorded data.
Installing the module
To implement the administration module, edit the settings.py
file. In the INSTALLED_APPS
setting, you need to add or uncomment the following line:
'django.contrib.admin'
You also have to edit the urls.py
file by adding or uncommenting the following lines:
from django.contrib import admin admin.autodiscover() url (r'^admin', include(admin.site.urls)),
The line that imports the administration module has to be at the beginning of the file with other imports. The line that runs the autodiscover()
method must be found after the imports and before the urlpatterns
definition. Finally, the last line is a URL that should be in urlpatterns
.
We also have to create an admin.py
file in the TasksManager
folder in which we will define the styles we want to integrate into the management module:
from django.contrib import admin from TasksManager.models import UserProfile, Project, Task , Supervisor , Developer admin.site.register(UserProfile) admin.site.register(Project) admin.site.register(Task) admin.site.register(Supervisor) admin.site.register(Developer)
Now that we have configured the administration module, we can easily manage our data.
Using the module
To use the administration module, we must connect to the URL that we have just defined: http://localhost:8000/admin/
.
We must connect with the logins defined when creating the database:
- Once we are connected, the model list appears.
- If we click on the Supervisor model link, we arrive at a page where we can add a supervisor by using the button at the top-right corner of the window:
- By clicking on this button, we load a page consisting of a form. This form automatically provides practical tools to manage dates and times:
Let's add a new supervisor and then add a developer. When you want to choose the supervisor, you can see the one we have just created in a combobox. The green cross on the right-hand side allows you to quickly create a supervisor.
In the following chapter, we will define the str
method for our models. This will improve the display lists of objects in this management module.
Advanced usage of models
We studied the basics of the models that allow us to create simple applications. Sometimes, it is necessary to define more complex structures.
Using two relationships for the same model
Sometimes, it is useful to store two foreign keys (or more) in a single model. For example, if we want two developers to work in parallel on the same task, we must use the related_name
property in our models. For example, our Task
model contains two relationships with the following lines:
developer1 = models.ForeignKey (Developer , verbose_name = "User" , related_name = "dev1" ) developer2 = models.ForeignKey (Developer , verbose_name = "User" , related_name = "dev2" )
Further in this book, we will not use these two relationships. To effectively follow this book, we must return to our previously defined Task
model.
Note
Here, we define two developers on the same task. Best practices advise us to create a many-to-many relationship in the Task
model. The thorough argument allows you to specify an intermediate table to store additional data. This is an optional step. An example of this type of relationship is as follows:
#Relationship to add to the Task model developers = models.ManyToManyField(Developer , through="DeveloperWorkTask") class DeveloperWorkTask(models.Model): developer = models.ForeignKey(Developer) task = models.ForeignKey(Task) time_elapsed_dev = models.IntegerField(verbose_name="Time elapsed", null=True, default=None, blank=True)
Defining the str method
As already mentioned in the section on the use of the admin module, the __str__()
method will allow a better view of our models. This method will set the string that will be used to display our model instance. When Django was not compatible with Python 3, this method was replaced by the __unicode__()
method.
For example, when we added a developer, the drop-down list that defines a supervisor showed us the "Supervisor object" lines. It would be more helpful to display the name of the supervisor. In order to do this, change our App_user
class and add the str()
method:
class UserProfile ( models.Model ) : # Fields... def __str__ (self): return self.name
This method will return the name of the supervisor for the display and allows you to manage the administration easily:

Summary
In this chapter, we learned migration with South. We also learned how to create simple models and relationships between the models. Furthermore, we learned how to install and use the admin module. In the next chapter, we will learn how to manipulate our data. We will learn how to use four main operations on the data: adding, reading (and research), modification, and deletion.