Complete App
In this article we are going to create a complete project for a Company called Beasttool (this name was selected randomly, it is not a real case) using Django from zero, step by step.
This tutorial is done using Windows 11, some terminal instructions can change depending on the OS you use.
First steps: Installing the framework
Let's create the virtual enviroment, for this enter in the terminal:
path-to-the-project>python -m venv env
Activate the virtual enviroment and go back to the original directory
path-to-the-project>cd env/Scripts
path-to-the-project\env\Scripts>activate
(env) path-to-the-project\env\Scripts>cd../..
(env) path-to-the-project>
Install Django and create the project:
(env) path-to-the-project>pip install django
(env) path-to-the-project>django-admin startproject beasttool
Run the server:
(env) path-to-the-project>cd beasttool
(env) path-to-the-project\beasttool>python manage.py runserver
Now we know the project is running, the server is running at localhost:8000, you can open the web server and verify the Django's default page can be reached:
Create Blog application
For blog application we must create few apps:
1. Users: For managing the users who will manage the posts.
2. Posts: For managing the posts.
3. Categories: For ordering the posts in categories.
4. Comments: For managing the comments the users can leave on the posts.
Create apps
Let's create those 3 apps at once:
(env) path-to-the-project\beasttool>py manage.py startapp user
(env) path-to-the-project\beasttool>py manage.py startapp post
(env) path-to-the-project\beasttool>py manage.py startapp category
(env) path-to-the-project\beasttool>py manage.py startapp comment
Now, we must register them in "beasttool/settings.py"
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# APPS
'user',
'post',
'category',
'comment'
]
Create the models for each app
At "user/models.py" define the following model:
"""User"""
from django.contrib.auth.models import User
from django.db import models
class Profile(models.Model):
"""Profile model.
Proxy model that extends the base data with other
information.
"""
user = models.OneToOneField(User, on_delete=models.PROTECT)
website = models.URLField(max_length=200, blank=True)
photo = models.ImageField(
upload_to='user/picture',
blank=True,
null=True
)
date_modified = models.DateTimeField(auto_now=True)
def __str__(self):
"""Return username."""
return self.user.username
Some comments: We are importing the Django's User model, protect the users, deleting a user is not allowed to avoid loose the posts and comments which depend on users.
At "user/post.py" define the following model:
"""Post"""
# Django
from django.db import models
from django.utils.text import slugify
from django.contrib.auth.models import User
from ckeditor.fields import RichTextField
from category.models import Category
class Post(models.Model):
"""Post model."""
user = models.ForeignKey(User, on_delete=models.PROTECT)
profile = models.ForeignKey('user.Profile', on_delete=models.PROTECT)
title = models.CharField(max_length=255)
image_header = models.ImageField(upload_to='post/photo')
post = RichTextField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
is_draft = models.BooleanField(default=True)
url = models.SlugField(max_length=255, unique=True)
views = models.PositiveIntegerField(default=0)
category = models.ManyToManyField(Category)
class Meta:
ordering = ('title',)
def __str__(self):
"""Return title and username."""
return '{} by @{}'.format(self.title, self.user.username)
def save(self, *args, **kwargs):
self.url = slugify(self.title)
super(Post, self).save(*args, **kwargs)
In this model we are using "ckeditor", we need to install this library and Pillow:
(env) path-to-the-project\beasttool>pip install django-ckeditor
(env) path-to-the-project\beasttool>pip install Pillow
At "user/category.py" define the following model:
"""Category"""
from django.db import models
# Models
# Create your models here.
class Category(models.Model):
"""Category model."""
name = models.CharField(max_length=100,unique=True)
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
At "user/comment.py" define the following model:
"""Comment"""
from django.db import models
from django.contrib.auth.models import User
from post.models import Post
# Model
# Create your models here.
class Comment(models.Model):
"""Comment model."""
user = models.ForeignKey(User, on_delete=models.PROTECT)
profile = models.ForeignKey('user.Profile', on_delete=models.PROTECT)
post = models.ForeignKey(Post, on_delete=models.PROTECT)
comment = models.CharField(max_length=5000)
def __str__(self):
return self.comment
Set the database
Create a database MariaDB called "beasttool" and set the connection in "settings.py":
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'beasttool',
'USER': 'root',
'PASSWORD': '',
'HOST': 'localhost',
'PORT': '3306',
}
}
Install the mysql driver:
(env) path-to-the-project\beasttool>pip install mysqlclient
Now is time to execute the migrations:
(env) path-to-the-project\beasttool>py manage.py makemigrations
The console should answer something like:
Migrations for 'category':
category\migrations\0001_initial.py
- Create model Category
Migrations for 'user':
user\migrations\0001_initial.py
- Create model Profile
Migrations for 'post':
post\migrations\0001_initial.py
- Create model Post
Migrations for 'comment':
comment\migrations\0001_initial.py
- Create model Comment
Next, use the command "migrate":
(env) path-to-the-project\beasttool>py manage.py migrate
Set the admin section
Create a superuser:
(env) path-to-the-project>\beasttool>python manage.py createsuperuser
In this example, I'm using user:"admin" and password:"12345", but in a real case please use a secure password.
Now, after run the server, we can browse to "localhost:8000/admin" to access to the admin section:
At the moment, the admin section just offer to models: Groups and Users, that's the default behavor, now we are going to register our customizations.
At "user/admin.py" we can register the settings we need for the admin section respect user model. Later we will set the same in each app.
"""User admin classes."""
# Django
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib import admin
# Models
from django.contrib.auth.models import User
from user.models import Profile
@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
"""Profile admin."""
list_display = ('pk', 'user', 'photo')
list_display_links = ('pk', 'user',)
list_editable = ('photo',)
search_fields = (
'user__email',
'user__username',
'user__first_name',
'user__last_name',
)
list_filter = (
'user__is_active',
'user__is_staff',
'date_modified',
)
fieldsets = (
('Profile', {
'fields': (('user', 'photo', 'website'),),
}),
('Extra info', {
'fields': (('date_modified'),),
})
)
readonly_fields = ('date_modified',)
class ProfileInline(admin.StackedInline):
"""Profile in-line admin for users."""
model = Profile
can_delete = False
verbose_name_plural = 'profiles'
class UserAdmin(BaseUserAdmin):
"""Add profile admin to base user admin."""
inlines = (ProfileInline,)
list_display = (
'username',
'email',
'first_name',
'last_name',
'is_active',
'is_staff'
)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
At "post/admin.py":
from django.contrib import admin
# Register your models here.
from post.models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
"""Post admin."""
list_display = ('id', 'user', 'title', 'image_header')
search_fields = ('title', 'user__username', 'user__email')
list_filter = ('created', 'modified')
def get_form(self, request, obj=None, **kwargs):
self.exclude = ('url', )
form = super(PostAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['user'].initial = request.user
form.base_fields['profile'].initial = request.user.profile
return form
At "category/admin.py":
from django.contrib import admin
# Register your models here.
from category.models import Category
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
"""Category admin."""
list_display = ('id', 'name')
At "comment/admin.py":
from django.contrib import admin
# Register your models here.
from comment.models import Comment
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
"""Comment admin."""
list_display = ('id', 'user', 'post', 'comment')
After those changes we can refresh the admin section, and this should be the result:
Thanks for reading :)
I invite you to continue reading other entries and visiting us again soon.