Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Django Models

In this lesson, you’re going to start looking at databases and Django models. When you created your app before, you created a file called The models are where you do the database interactions.

This is what a Django model looks like:

# Create your models here.
class Project(models.Model):
    title = models.CharField(max_length=100)
    description = models.TextField()
    technology = models.CharField(max_length=20)
    image = models.FilePathField(path='/img/')

00:00 Okay, let’s go ahead and dive into the database part of this and look at Django models. So, when we created our app before, it also created this one file called

00:13 The models are where we’re doing the database interactions. I’m just going to copy-paste this for now to give you a preview, and we’re going to build it out later,

00:23 but this is essentially how a Django model looks like. This would be our model for our Project class. This might look very confusing if you’ve never seen this kind of stuff before.

00:34 So, let’s take a step back and talk a tiny bit about databases and relational databases and ORMs.

00:43 So, the first thing I want to talk about is the Django ORM. What’s an ORM? It’s an object-relational mapper. What this word already has, in itself, is it tells us that it makes a connection between objects and relational tables.

00:58 So essentially, the ORM gives us a way to interact between Python and SQL—SQL as a relational database management system. We have these objects in Python that we’re creating, that are going to be translated to tables, more or less, in SQL—and the other way around.

01:18 Okay, so the ORM allows us this communication between these two different ways of representing data. Now, you might be wondering, “What’s a relational database?” You’ve probably heard of SQL before. SQL stands for Structured Query Language, and that’s a way of interacting with relational databases.

01:35 If you’re wondering what a relational database is, I have a very, very short intro to what this is. I hope it’s going to look familiar. One of the basic concepts about it are tables and tables interacting with each other. You’ve probably seen a table before if you use Google Sheets, or if you use Excel. It’s a common way of structuring data.

01:56 We have the data in there and we have some column names. Let’s take a closer look at this, actually. So, we have rows. A table consists of rows that contain data.

02:05 So, we have three rows in this table, and then we also have columns. This table has one, two, three, four, five columns, right?

02:15 That’s the way that you can essentially access every piece of information inside of the table. It’s just a way of storing data. Now, let’s look back at this Python code that we saw for a moment before. This is our rendering of a model class in Python.

02:32 You can see, maybe, that these things look familiar, right? So title, description, technology, image are some variables that we’re assigning here.

02:40 And those correspond to the column names, with one exception. You can see that id isn’t here, and that’s simply because Django does that automatically for us. id, usually, is just an auto-incrementing number, and Django just takes away from us the need to create this. Okay, so inside of our model class, we’re defining the different columns.

03:01 And what we’re doing here on the right side is we’re telling it what type of data can be inside of this column. So, looking back to this, if we look at the column image, we’re telling it, “Okay, this type of data,” in our case, it’s going to be a FilePathField. See right here? “That’s what’s allowed to be in here, nothing else.” So, id is going to be an auto-incrementing integer. Our table, here, isn’t that diverse.

03:26 We’re just going to have different types of texts or characters in there, where you can see we’re limiting the title field to a max_length (maximum length) of 100 and the

03:36 technology field max_length of 20, while the TextField is another way of strings, characters, but we’re leaving it open, so it’s open-ended. Inside of our database representation, you’re often going to see something like that, but here, this is the model of the database on the more SQL-side of things, right?

03:54 So, we see the id is going to be of data type integer. Again, that gets created automatically. And then we see here, title has this data type here of varchar(100), and that’s the SQL representation of what we’re making here. Again, this ORM allows us to translate between these two different languages,

04:13 but you can inspect the data and you can see that this is what it results into. Eventually, it’s going to result into this: a table that looks very familiar to the table that we looked at before.

04:24 It has columns that are named the same as how we named the columns in here. And then later, when we’re going to put some data into this, the rows are going to be filled up with these different textual items, this data.

04:37 Let’s take a look, actually: how does this look like in our app? If you remember, this is what it results into, okay? So, we’re going to have the image path that results into displaying the image, we’re going to have the title that shows us the title here.

04:52 We have a description. In this view, we don’t see the technology, but if you click on this Read More, we also have the technology displayed here.

05:00 So, that’s essentially what I want you to think about when you look at this code over here that we’re writing. Django Model, all it does is it makes it easier for us, makes it possible for us to write Python code that then translates into a table inside of a relational database.

05:19 Okay, let’s go ahead and write this code.

05:24 I’m going to take it away from here and write it step by step. So, we’re making a class, and I call it Project because that’s what we want to model. We want to model projects.

05:34 You want to keep that class always in singular. And now what we do always in here, is we inherit from the imported Django models module and

05:45 the Model class. That just gives us a lot of functionality that is going to be the same, and it’s going to allow us to interact and makes it possible for us to actually just write this very simple code, but still get a functional model. So, we want a title. Remember, we don’t need to create the id. Django does that for us.

06:07 And we said that’s going to be a CharField (character field), with a max_length of 100. I’m just defining this so that people don’t put in endless titles, and it saves with performance. title, then we have a description.

06:24 Now here, we want to allow them to type some more, so I’m choosing a different field. In this case, it’s a TextField.

06:32 There’s lots and lots of these fields that are possible. You can head over to the Django documentation, which has an excellent reference to all the possible fields that you can use. But most of those, you’re probably not going to need in a normal app that you’re building.

06:49 So, just know that there’s references for this, you can look them up, you don’t need to know them. The ones that you’re going to be using very often, they become familiar pretty soon. Then, we have technology,

07:02 which again, we want to limit in size by using a CharField and making the max_length even shorter. I’m going to give you 20. And then finally, we also have an image, and this is going to be

07:17 a FilePathField, which just tells us that it’s essentially going to be a string, but a string that has certain restrictions. Okay, I’m defining this to be '/img' (image). Now, here we are. That’s our model.

07:35 I hope you can see, now, the relation to how these ones are going to create the columns, and this is telling us what is allowed to be inside of the rows related to those columns, and how that—again—relates back to what are we actually going to see popping up in our app when we have it finished and when we have the database filled up with some data.

07:58 Okay, phew! That was quite a ride. Let’s, next, stick a bit longer with databases and take a look at migrations.

Avatar image for Pygator

Pygator on Oct. 13, 2019

I get what you’re saying about the ORM, but how can you have a class defined without any mention of “self” . Really interesting !

Avatar image for Martin Breuss

Martin Breuss RP Team on Oct. 16, 2019

There’s a lot going on under the hood, and I’m myself far from familiar with the whole Django source code. Good thing is that it isn’t necessary in order to get started building something–and great thing about Open Source is that you can always keep diving deeper into it, if you want to :)

But the important part here that makes this possible, is that all Django models you’ll define inherit from models.Model. A lot of logic is taken care of in there, which allows us to use a class in this unfamiliar way, without needing to define an __init__() method or use self at all.

Avatar image for Kevin Lao

Kevin Lao on Nov. 19, 2019

Hi Martin,

You probably will be hitting this topic somewhere down the line, but just in case I wanted to place this question somewhere. Let’s say for example, we currently have: class Project(models.Model): title = models.CharField(max_length = 100)

and for some business reason we want to get change the number of characters in the title fields. I’d normally assume that we would change the number of max characters, but in reality there are already customers who have 100 characters maximum for their titles. Is there a way for us to handle a situation to where say, we were to change the title max_length = 60 without doing damage to the database?

Avatar image for Martin Breuss

Martin Breuss RP Team on Nov. 23, 2019

It depends what you want to achieve. I think that Django enforces the max_length on the Python side, so changing this and propagating the changes with a database migration will ensure that all subsequent entries will be limited to only 60 characters.

If you would want to apply the character limit also to existing entries that have more than 60 chars, you’d have to decide what to do with the extra chars. You could e.g. write a script that truncates the existing entries.

I’d suggest you to look into our series of articles on Django Migrations, starting with Django Migrations: A Primer to dive deeper.

Avatar image for R morel

R morel on May 4, 2020

cool man

Avatar image for MrUzo

MrUzo on May 22, 2020

Hey Martin, I’m building a modified model, but i’m getting this error message: “users.models.Friend.DoesNotExist: Friend matching query does not exist” Hi everyone, i’m trying to build a django model that enables users to connect with each other, but I get the above error when I try to render the list of connected users.

My model:

#for users to have a profile image
class Profile(models.Model):
    user = models.OneToOneField(User, ondelete=models.CASCADE)
    image = models.ImageField(default='default.jpg', uploadto='profilepics')
    def str(self):
        return f'@{self.user.username}'
    def save(self, args, kwargs):
        img =
        if img.height > 300 or img.width > 300:
            outputsize = (300, 300)
#for users to be able to connect with eachother
class Friend(models.Model):
    connect = models.ManyToManyField(User)
    currentuser = models.ForeignKey(User, relatedname='owner', null=True, ondelete=models.DONOTHING)
    def makefriend(cls, currentuser, newfriend):
        friend, created = cls.objects.getorcreate(
    def losefriend(cls, currentuser, newfriend):
        friend, created = cls.objects.getorcreate(

View that lists the connected friends:

def profile(request): posts = users = User.objects.exclude( friend = Friend.objects.get( friends = friend.connect.all() context = { ‘posts’: posts, ‘users’: users, ‘friends’: friends } return render(request, ‘users/profile.html’, context) ```* Any help is appreciated. Thanks

Avatar image for Martin Breuss

Martin Breuss RP Team on May 23, 2020

Hi @MrUzo and awesome that you’re expanding on the project and building out your own implementation 👍

Your Code

The second part of your code got wrongly formatted, let me know if this is what you have:

def profile(request):
    posts =
    users = User.objects.exclude(
    friend = Friend.objects.get(
    friends = friend.connect.all()
    context = { 'posts': posts, 'users': users, 'friends': friends }
    return render(request, 'users/profile.html', context)

It might be easier if you share a GitHub repository, since there is some logic that you’re using that isn’t explained in the code you shared (e.g. how does your User model look like? Did you change it? You are requesting posts through the user object, and it seems you have an author attribute that isn’t part of the default Django User model, etc.)

Possible Errors

Here are some thoughts on what you can try out:

  • I think that this: posts = is missing the brackets for your method call on .all()
  • Not sure what you’re doing with the part of that call. Your Friend model doesn’t seem to have an instance attribute
  • Double-check (and maybe explain here) your logic of what you want to fetch with this call:
    • Friend.objects.get() is gonna return one instance, but you’re accessing that instance through the friend_set of the current user (which can have multiple friends)
    • Then you are getting all connected friends of that friend. Who is that first friend? Is it meant to be the current user? (If so, there are more direct ways of getting to their friends) Who else is it meant to refer to?

I hope these suggestions can help you on the way, but as I mentioned, there’s some code missing to be able to get the full picture. All the best and keep up the great work :)

Avatar image for Scretch

Scretch on June 17, 2020

Hello Martin,

I’m expanding the capabilities of my build from this tutorial and I was wanting to add an additional class object(if thats what i need) to the models file. The goal is to have a button or just the text of a link on each project page so I can link to projects I’m hosting elsewhere with heroku etc. I am currently able to add the neccessary HTML to have a link appear in detail.html but that same link shows up in each project now. Ideally it would be nice to have that field appear in the admin page with title, description, technology, and img so I can just paste a link on there.

Thanks. Im off to django docs to see if I missed something

Avatar image for Scretch

Scretch on June 17, 2020

Ah, I did miss it. If anyone else is trying something similar. The model field is URLField. I found more details here:

Tell me if I gave bad info please! I want to be sure I’m contributing to this great tutorial.

Avatar image for Martin Breuss

Martin Breuss RP Team on June 18, 2020

Hi @Scretch! Yes, you can add info fields to your models any time, just remember to run your two migration commands afterwards to propagate your model changes to your database structure.

As to which is the best field to use, I think you made the right choice with Django’s URLField. As it explains in the docs, it’s just a CharField with some URL-specific validation methods added. A URL is just some text, so any field that holds text can work for this, but sounds like a good idea to use what Django specifically suggests for URLs.

Avatar image for alnah

alnah on Oct. 3, 2023

Hi Martin. How are you? Thank you for the tutorial. I was wondering why one should limit the title and technology fields using CharField(max_length=<int>) instead of using TextField and write short titles or technologies without limitations?

Avatar image for Bartosz Zaczyński

Bartosz Zaczyński RP Team on Oct. 4, 2023

@alnah While TextField might seem easier because you don’t have to specify a length, it’s more efficient to use CharField for short strings like names, titles, or descriptions. CharField translates to a fixed-length column in the underlying database, which makes querying and indexing on that column more efficient.

Avatar image for Martin Breuss

Martin Breuss RP Team on Oct. 4, 2023

^^ this :) It’s for database efficiency reasons.

Avatar image for alnah

alnah on Oct. 4, 2023

Got it! Thank you Bartosz and Martin!

Become a Member to join the conversation.