This guide covers the basic Django project set, making sure Django admin is enabled, creating models and registering them in the Django Admin.
cd ~/tutorials
mkdir customadmin
cd customadmin
python3 -m venv env
source env/bin/activate
pip install --upgrade pip
pip install django django-environ whitenoise
django-admin startproject project .
pip freeze > requirements.txt
./manage.py startapp myapp
Modify settings for Django Admin¶
Ensure the admin app and mayapp
are in INSTALLED_APPS
in project/settings.py
:
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp',
]
For future admin template customizations create a directory templates
in the root of the project.
mkdir templates
Make sure your TEMPLATES
settings is configured to have the following context_processors
:
# settings.py
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
The MIDDLEWARE
setting must include: django.contrib.sessions.middleware.SessionMiddleware
, django.contrib.auth.middleware.AuthenticationMiddleware
, and django.contrib.messages.middleware.MessageMiddleware
.
# settings.py
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
The WhiteNoise is a library for serving static files in production. It is not required for the admin panel to work, but it is recommended for production use.
Configure URLs¶
Set up the admin URLs in your project:
# urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
You can further protect the admin panel by adding a custom URL:
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('myadmin__321/', admin.site.urls),
]
Or using a value from environment variable to set a custom URL:
from django.contrib import admin
from django.urls import path
import environ
env = environ.Env()
urlpatterns = [
path(env('ADMIN_URL', default='admin/'), admin.site.urls),
]
Create Superuser¶
Create an admin account to access the interface:
DJANGO_SUPERUSER_PASSWORD=somethingsupersecret123 python manage.py createsuperuser --username=admin --email=admin@example.com --noinput
This command allows unattended creation of the superuser.
Define Models¶
You need to define models that represent the data structure of your application and which you want to manage through the admin interface.
Since we'll need to showcase a lot of interesting cases for Django Admin features and customizations, let's create models structure similar to e-commerce like project to make it complex enough.
Here is an example of a simple model structure:
# models.py
from django.db import models
from django.utils import timezone
from myapp.tuples import ORDER_STATUSES_CHOICES, ORDER_STATUSES
class Category(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
is_active = models.BooleanField(default=True, db_index=True)
class Meta:
ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
category = models.ManyToManyField(Category, related_name='products')
is_active = models.BooleanField(default=True, db_index=True)
class Meta:
ordering = ('name',)
verbose_name = 'product'
verbose_name_plural = 'products'
def __str__(self):
return self.name
class Customer(models.Model):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
phone = models.CharField(max_length=200, unique=True)
class Meta:
ordering = ('first_name',)
verbose_name = 'customer'
verbose_name_plural = 'customers'
def __str__(self):
return ' '.join([self.first_name, self.last_name])
class Order(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
created_dt = models.DateTimeField(auto_now_add=True)
completed_dt = models.DateTimeField(null=True, blank=True)
status = models.IntegerField(default=ORDER_STATUSES.new, choices=ORDER_STATUSES_CHOICES)
class Meta:
ordering = ('-created_dt',)
verbose_name = 'order'
verbose_name_plural = 'orders'
def __str__(self):
return str(self.id)
def save(self, *args, **kwargs):
if self.completed_dt:
self.status = ORDER_STATUSES.complete
if self.status == ORDER_STATUSES.complete and not self.completed_dt:
self.completed_dt = timezone.now()
super().save(*args, **kwargs)
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
class Meta:
verbose_name = 'order item'
verbose_name_plural = 'order items'
unique_together = ('order', 'product')
def __str__(self):
return self.product.name
```
Make sure to specify the `__str__` method for each model to display the object's name in the admin interface.
You can also customize the model's metadata by setting the `Meta` class attributes.
For example:
Meta class attributes `verbose_name` and `verbose_name_plural` are used to set the human-readable name of the model in the admin interface.
The Meta class attribute `ordering` attribute is used to specify the default ordering of objects in the admin interface.
The models `get_absolute_url` method can be used to return the canonical URL of the object.
Create a file `myapp/tuples.py` and put this in it.
from collections import namedtuple
ORDER_STATUSES = namedtuple('ORDER_STATUSES', 'new processing shipped complete canceled')._make(range(5))
ORDER_STATUSES_CHOICES = ( (ORDER_STATUSES.new, 'New'), (ORDER_STATUSES.processing, 'Processing'), (ORDER_STATUSES.shipped, 'Shipped'), (ORDER_STATUSES.complete, 'Complete'), (ORDER_STATUSES.canceled, 'Canceled'), )
Now create new migrations and apply them:
python manage.py makemigrations python manage.py migrate
Now let's run the development server and open our admin panel:
python manage.py runserver
The output should be like this:
Watching for file changes with StatReloader Performing system checks...
System check identified no issues (0 silenced). July 09, 2023 - 07:18:13 Django version 4.2.3, using settings 'project.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
Open your browser at this URL: (http://127.0.0.1:8000/notadmin123/)[http://127.0.0.1:8000/notadmin123/] because that's where our admin is located.
The credentials are `admin` / `somethingsupersecret123`
You will see this basic admin panel without our models in there.
![image](https://appliku-serverless-static.s3.eu-central-1.amazonaws.com/imgs/f716092aae3f0edd52517f87a09f980a.png)
Let's fix that.
## How to register Django model in the admin site
Let's see some examples how you can register models in Django admin.
### Basic Registration
The simplest way to register a model without creating ModelAdmin classes:
```python
# admin.py
from django.contrib import admin
from .models import Book, Author
admin.site.register(Book)
admin.site.register(Author)
ModelAdmin Registration¶
For customized admin interfaces:
# admin.py
from django.contrib import admin
from .models import Book
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'publication_date')
list_filter = ('author', 'publication_date')
search_fields = ('title', 'author__name')
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
admin.site.register(Book, BookAdmin)
Decorator Registration¶
Alternative registration using decorators:
# admin.py
from django.contrib import admin
from .models import Book
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'publication_date')
With the theory and examples out of the way, let's register our e-commerce like models.
Open the myapp/admin.py
file and let's create very basic admin models.
from django.contrib import admin
from .models import Category, Product, Customer, Order
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'slug', 'is_active', 'id',)
admin.site.register(Category, CategoryAdmin)
class ProductAdmin(admin.ModelAdmin):
list_display = ('name', 'slug', 'is_active', 'id',)
admin.site.register(Product, ProductAdmin)
class CustomerAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'phone', 'id',)
admin.site.register(Customer, CustomerAdmin)
class OrderAdmin(admin.ModelAdmin):
list_display = ('customer', 'created_dt', 'completed_dt', 'status', 'id',)
admin.site.register(Order, OrderAdmin)
Refresh the admin page and you should see the "MYAPP" block appear and our 4 models listed.
In this step, we have registered models in the admin panel. Thanks to the list_display
attribute, we tell the Django admin site what fields to display in the table for each model.
I will create a few records in every table so we can see how our admin modifications affect the admin site.