Skip to content
Home » Django Portfolio Project – Part 3 – The Models

Django Portfolio Project – Part 3 – The Models

Spread the love

In the previous part of the series we created the skeleton of the Portfolio website. Now it’s time to create the models.

Models in Django

But what are models in the first place? In Django, models are just Python classes that represent the data. Each model defines its structure in such a way as to be understood by the underlying database. This means it defines the field types and constraints like maximum size, default values, etc., so that the database knows how to store the data. This said, a model is independent of any particular concrete database and can work with all.    

Data and Relationships

When talking about models, we have to consider data and relationships. Here’s a UML diagram of the models we’re going to use in our app:

UML diagram

Each rectangle represents a model class and each line represents a relationship.

Let’s start with the models. Each rectangle is divided into three parts. We have the name of the class in the top part. In the middle part are the fields along with their types. In the bottom part are the methods with their return types. We won’t need any fancy methods for our models, so let’s limit ourselves to just two: __str__ and get_absolute_url. Each model will redefine it. Besides, we don’t need the second of the aforementioned methods in the Link model at all.

The main model class in our app is Project. Each project will have a name and description. These two fields are defined as strings. We’ll also be able to add an image for each project, hence the image field of type ImageField.

In order to use the ImageField, we have to install Pillow, so let’s do just that. Type the following command in the terminal:

We’ll also specify the date when the project was added to the Portfolio. To this end, we’ll need a date_added field of type DateField. Each project will belong to exactly one category of type Category. This type is defined here as well, with just a single field. Each project will use one or more technologies of type Technology, which is also defined here.

Let’s have a look at the lines now to see the relationships between the models. The numbers above the lines represent their multiplicities.

So, there is a line between Project and Category, which means these two models are related. The number 1 near the Category means a project must belong to exactly one category. The range 0..* next to the Project means there may be 0 or more projects in a category.

Likewise, a Project must use 1 or more technologies (1..*), and each technology may be used in 0 or more projects (0..*).

Finally, each project will have 0 or more (0..*) links (to Github, YouTube, Amazon, etc.) of type Link. Each link must belong to exactly one project (1). You can see this type in the diagram as well. It has three string fields and a field of type Project that will be used to associate the link with the book it belongs to.

The Project Model

Now that we know what our models will look like, let’s implement them. Models are usually implemented in the models.py file. Let’s start with the Project model and see on this example how models are defined in general. We’re going to put the code in the models.py folder inside the catalog folder. So, here’s the code:

And now, let’s have a closer look at what we have here. Generally, there are some fields, some methods and a nested class.

Field Types

As you can see, the fields are of specific types that will make it easier to store them in the database. Actually, each field corresponds to one column in the database table. We also specify the validation criteria for each field. Let’s have a look at the particular fields one by one.

So, the name field is of type CharField. This type is used to define strings of an up to moderate length. It has a max_length attribute set to 200 characters. This should be enough for a name of a project:

The description field is of type TextField. This type is for longer strings of arbitrary length. Here we’re limiting the length of the string to 5000 characters and setting a text label that will be visible as a hint in the admin site:

Next is the image field of type ImageField. It has an upload_to attribute set to the directory where the uploaded images will be put:

The date_added field is of type DateField. This type is used to store dates. There’s also the DateTimeField available in case we need to store both the date and the time, which isn’t the case in our app, though. The field has a couple interesting attributes.

The first one is null. It’s set to True, which means the database will store a Null value if no category is assigned.

The second attribute is blank. It’s set to True, which means blank values will be allowed in the forms.

The verbose_name attribute is used to provide a name for the field labels. By default the names are inferred from the field name, so in our case the label for this field would read Date Added. We use the attribute if we want it to be displayed differently:

The category field is of type ForeignKey. This type is used to define a one-to-many relationship. In our case Category is on the “one” side and Project is on the “many” side, which means a category may have many projects, but a project may belong to just one category. At least this is how we implement this relationship in our app.

If you look at the category field, it takes as its first argument the name of the model it’s related to. Here the model must be passed as a string because the Category class hasn’t been defined yet:

If it had, we could use the model class directly, like so:

The next attribute in the category field is on_delete=models.RESTRICT. This will prevent the category associated with the project from being deleted if it’s associated with any project.

Finally, there’s the technologies field of type ManyToManyField. As the name suggests, this type is used to define a many-to-many relationship. In our case, a Project may implement many technologies and a Technology may be used in many projects:

The Methods

We also define two methods in the model. The first method, __str__, is used to return a human-readable representation of the object. In particular, it will display the name of the project.

The second method is get_absolute_url. This method will return a URL to the detail view of this particular object and we’ll discuss it in more detail when we need it.

Metadada

We can define a nested class, class Meta, inside a class if we want to declare model-related metadata. There’s a whole bunch of stuff we can use in this class to impact the model class it’s defined in. For example, we can set the ordering of records that are returned when we query the model type. To do that, we just set the ordering attribute to the field or fields (enclosed in a list) we want to sort by.

Let’s say we want the projects to be ordered chronologically, so by date. This is how we do it:

Here, we’re ordering by just one field, but we could specify multiple fields in the list in the order we want to sort by. The minus sign before the field name means we want to sort in descending order. Dates are ordered chronologically, strings are ordered alphabetically.

The Meta class in the Project model is pretty simple. We’ll define a slightly more complicated Meta class in some of the other models. Speaking of which…

The Category Model

We’ll define all the models in the models.py file. Let’s start with the Category model. Here’s the code to add below the Project class definition:

As far as the two methods are concerned, __str__ and get_absolute_url, they are used for the same purposes as in the Project class.

The name field is of type CharField, so it’s going to be a string. Its max_length attribute is set to 200 characters, which seems more than enough for a category name. We also set the unique parameter to True to ensure there’s only one category with a given name in the entire database.

The icon field is also of type CharField and we’ll use it to store the name of the icon that will graphically represent the category.

Here we have a little more complex Meta class:

First of all, for this to work, we have to add the following import statements at the top of the file in the import section or directly above the class definition:

Here, we’re defining some constraints for the Category model. Although we set the unique attribute for the name field to True, it doesn’t take case into account. So, names like ‘data science’, ‘Data science’ and ‘Data Science’ are considered three different names and may occur side by side in the database. This isn’t what we want, so in the Meta class we specify the UniqueConstraint. By setting Lower('name') we ensure the lowercase version of the name is unique, so all three aforementioned names will be first converted to lowercase and then compared. This time, the uniqueness constraint will be violated and the error message the violation_error_message attribute is set to will be displayed.

We also set the verbose_name_plural attribute to the correct plural form ‘categories‘. If we don’t do that, the incorrect form ‘categorys‘ will be generated then in the admin site.

The Technology Model

We’re going to define the Technology model in a very similar way:

Reordering the Models

If you look at the models.py file, the models are defined in a specific order. In particular, we first defined the Project class, then Category, and finally Technology.

As Category and Technology are defined after the Project class, we can’t use their class names inside the Project class to define the fields. Instead, we have to use the names of the classes as strings:

Let’s move the definitions of Category and Technology to the top of the file so that the definition of Project follows them. Next, let’s replace the strings shown above with class names:

Finally, there’s one more model to define.

The Link Model

Let’s define the Link model as last, so after the definition of Project, because this way we’ll be able to use the class name of Project inside Link to define a field. Here’s the code:

The code is simple and requires no further explanation, I think.

The Database Migrations

In the previous part of the series we created the skeleton of the application and ran the database migrations to inform the database of its structure. Now that we’ve added all the model classes, we must let the database know about them. So, let’s re-run the migrations in the terminal:

Now that we have our models in place, we can feed some data to the database. To do that, we’ll use the admin site, which is the topic of the next part in the series.


Spread the love

Leave a Reply