We are going to continue the series of beginner posts which point to this basic-django repo.
In the previous post we have setup up pre-commit for automatic code checking and formatting.
In this lesson we will create out first "app" where we will store most of our code and will set up a reusable BaseModel
that we will use for all our models going forward.
You can see all the code that I wrote for this post in this PR.
Let's get the discussion of "apps" out of the way (and other small things).
When it comes to structuring your app there are people who like to create a separate app for each part of your application. For example, you have a SaaS that automates Twitter posting for your users. You can have a separate "app" that deals with auth, separate "app" for the blog part of your application, and a separate app for the "core" functionality.
I was one of those people. It felt much nicer semantically, that each part of the application, sits in a separate app. But I was recently converted.
I tried working on codebases that had most logic in a single "core" app, I realized that having all the code in one dir has it's advantages. No need to jump between files, since everything is contained in one location.
There are disadvantages of course... For example, as your project grows, the files will get huge.
As with all things in life there are tradeoffs. And when it comes to programming specifically, there are a gazillion ways of doing one thing.
All of this to say, is that I picked the latter approach for this blog post. If you thing there are better ways, feel free to email me or comment down below. I'm open to all suggestions.
With all this out of the way, let's get into it
poetry run python manage.py startapp core
. Note: If you are wondering about why you have to add
poetry run
to this command, read this post.
This will create the following folder:
...
├── core
│ ├── __init__.py
│ ├── __pycache__
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ └── views.py
...
base_models.py
file with a BaseModel
class:import uuid
from django.db import models
class BaseModel(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
Please note, that models.Model
class that we are inheriting will create an id
field automatically. That reason I'm not adding primary_key=True
to the uuid field. I still want Django to autogenerate id
field for me.
And the reason I wanted to create uuid
field is that I would rather for this to be used for urls and other such things, since id
field is very easy to guess for potential attackers/hackers.
This is not something you need to get into right now. It is also not a requirement, just something I like to do now. If you want to know more about this, let me know, I'll do a separate post about this.
Note: if you don't add the
Meta
class withabstract = True
, this will bite us in the ass later down the line. This block tells django not to create a table called base_model, but only use that as (you guessed it) an abstract class.I once made that mistake in another app, was not able to undo it in prod ever since 😢
core/models.py
. Let's say we want to let users create blog posts, the most generic/basic example out there. The usage of the BaseModel will look like this:
from django.db import models
from .base_models import BaseModel
class BlogPost(BaseModel):
user = models.ForeignKey('users.CustomUser', on_delete=models.CASCADE)
title = models.CharField(max_length=255)
content = models.TextField()
core/admin.py
so that we can see the effect in the Admin Panel:from django.contrib import admin
from .models import BlogPost
class BlogPostAdmin(admin.ModelAdmin):
list_display = ("id", "uuid", "created_at", "updated_at", "title", "content")
admin.site.register(BlogPost, BlogPostAdmin)
Note: that we are adding fields that are not directly written on the BlogPost model.
core
to INSTALLED_APPS
in your settings.py
poetry run python manage.py makemigrations
and then poetry run python manage.py migrate
to create new tables in your DB.poetry run python manage.py runserver
and head over to http://127.0.0.1:8000/admin/
You should see this new part:
Click on it.
Now you will see an almost empty page. Let's create a new post by clicking here:
Enter some details and and click save:
Note: that we are only asked to enter the details that we added on the BlogPost model directly, and the ones from BaseModel are not required. This is because all the fields we have added on BaseModel are assigned automatically. If there was a field that required user input, it would also show up.
Now you can reuse that BaseModel in all the other models, knowing that all the necessary fields that need to be on each model will be there.
The main advantage of this approach is that we don't have to write the same boilerplate code every single time, thus our code will be a little more DRY.
If you have any questions please let me know by emailing me or down in the comments below.
OSIG - Ad
Automatically create beautiful OG images for your site.
Automatically create beautiful OG images for your site.
Ad