Writing your first Django app, part 7¶
This tutorial begins where Tutorial 6 left off. We’re
continuing the web-poll application and will focus on customizing Django’s
automatically-generated admin site that we first explored in Tutorial 2.
Where to get help:
If you’re having trouble going through this tutorial, please head over to
the Getting Help section of the FAQ.
Customize the admin form¶
By registering the Question
model with admin.site.register(Question)
,
Django was able to construct a default form representation. Often, you’ll want
to customize how the admin form looks and works. You’ll do this by telling
Django the options you want when you register the object.
Let’s see how this works by reordering the fields on the edit form. Replace
the admin.site.register(Question)
line with:
polls/admin.py
¶
from django.contrib import admin from .models import Question class QuestionAdmin(admin.ModelAdmin): fields = ['pub_date', 'question_text'] admin.site.register(Question, QuestionAdmin)
You’ll follow this pattern – create a model admin class, then pass it as the
second argument to admin.site.register()
– any time you need to change the
admin options for a model.
This particular change above makes the “Publication date” come before the
“Question” field:
This isn’t impressive with only two fields, but for admin forms with dozens
of fields, choosing an intuitive order is an important usability detail.
And speaking of forms with dozens of fields, you might want to split the form
up into fieldsets:
polls/admin.py
¶
from django.contrib import admin from .models import Question class QuestionAdmin(admin.ModelAdmin): fieldsets = [ (None, {'fields': ['question_text']}), ('Date information', {'fields': ['pub_date']}), ] admin.site.register(Question, QuestionAdmin)
The first element of each tuple in
fieldsets
is the title of the fieldset.
Here’s what our form looks like now:
Customize the admin change list¶
Now that the Question admin page is looking good, let’s make some tweaks to the
“change list” page – the one that displays all the questions in the system.
Here’s what it looks like at this point:
By default, Django displays the str()
of each object. But sometimes it’d be
more helpful if we could display individual fields. To do that, use the
list_display
admin option, which is a
tuple of field names to display, as columns, on the change list page for the
object:
polls/admin.py
¶
class QuestionAdmin(admin.ModelAdmin): # ... list_display = ('question_text', 'pub_date')
For good measure, let’s also include the was_published_recently()
method
from Tutorial 2:
polls/admin.py
¶
class QuestionAdmin(admin.ModelAdmin): # ... list_display = ('question_text', 'pub_date', 'was_published_recently')
Now the question change list page looks like this:
You can click on the column headers to sort by those values – except in the
case of the was_published_recently
header, because sorting by the output
of an arbitrary method is not supported. Also note that the column header for
was_published_recently
is, by default, the name of the method (with
underscores replaced with spaces), and that each line contains the string
representation of the output.
You can improve that by using the display()
decorator on that method (in polls/models.py
), as follows:
polls/models.py
¶
from django.contrib import admin class Question(models.Model): # ... @admin.display( boolean=True, ordering='pub_date', description='Published recently?', ) def was_published_recently(self): now = timezone.now() return now - datetime.timedelta(days=1) <= self.pub_date <= now
For more information on the properties configurable via the decorator, see
list_display
.
Edit your polls/admin.py
file again and add an improvement to the
Question
change list page: filters using the
list_filter
. Add the following line to
QuestionAdmin
:
list_filter = ['pub_date']
That adds a “Filter” sidebar that lets people filter the change list by the
pub_date
field:
The type of filter displayed depends on the type of field you’re filtering on.
Because pub_date
is a DateTimeField
, Django
knows to give appropriate filter options: “Any date”, “Today”, “Past 7 days”,
“This month”, “This year”.
This is shaping up well. Let’s add some search capability:
search_fields = ['question_text']
That adds a search box at the top of the change list. When somebody enters
search terms, Django will search the question_text
field. You can use as many
fields as you’d like – although because it uses a LIKE
query behind the
scenes, limiting the number of search fields to a reasonable number will make
it easier for your database to do the search.
Now’s also a good time to note that change lists give you free pagination. The
default is to display 100 items per page. Change list pagination
, search boxes
, filters
, date-hierarchies
, and
column-header-ordering
all work together like you think they should.
Customize the admin look and feel¶
Clearly, having “Django administration” at the top of each admin page is
ridiculous. It’s just placeholder text.
You can change it, though, using Django’s template system. The Django admin is
powered by Django itself, and its interfaces use Django’s own template system.
Customizing your project’s templates¶
Create a templates
directory in your project directory (the one that
contains manage.py
). Templates can live anywhere on your filesystem that
Django can access. (Django runs as whatever user your server runs.) However,
keeping your templates within the project is a good convention to follow.
Open your settings file (mysite/settings.py
, remember) and add a
DIRS
option in the TEMPLATES
setting:
mysite/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', ], }, }, ]
DIRS
is a list of filesystem directories to check
when loading Django templates; it’s a search path.
Organizing templates
Just like the static files, we could have all our templates together, in
one big templates directory, and it would work perfectly well. However,
templates that belong to a particular application should be placed in that
application’s template directory (e.g. polls/templates
) rather than the
project’s (templates
). We’ll discuss in more detail in the
reusable apps tutorial why we do this.
Now create a directory called admin
inside templates
, and copy the
template admin/base_site.html
from within the default Django admin
template directory in the source code of Django itself
(django/contrib/admin/templates
) into that directory.
Where are the Django source files?
If you have difficulty finding where the Django source files are located
on your system, run the following command:
/
$ python -c "import django; print(django.__path__)"
...> py -c "import django; print(django.__path__)"
Then, edit the file and replace
{{ site_header|default:_('Django administration') }}
(including the curly
braces) with your own site’s name as you see fit. You should end up with
a section of code like:
{% block branding %} <h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1> {% endblock %}
We use this approach to teach you how to override templates. In an actual
project, you would probably use
the django.contrib.admin.AdminSite.site_header
attribute to more easily
make this particular customization.
This template file contains lots of text like {% block branding %}
and {{ title }}
. The {%
and {{
tags are part of Django’s
template language. When Django renders admin/base_site.html
, this
template language will be evaluated to produce the final HTML page, just like
we saw in Tutorial 3.
Note that any of Django’s default admin templates can be overridden. To
override a template, do the same thing you did with base_site.html
– copy
it from the default directory into your custom directory, and make changes.
Customizing your application’s templates¶
Astute readers will ask: But if DIRS
was empty by
default, how was Django finding the default admin templates? The answer is
that, since APP_DIRS
is set to True
,
Django automatically looks for a templates/
subdirectory within each
application package, for use as a fallback (don’t forget that
django.contrib.admin
is an application).
Our poll application is not very complex and doesn’t need custom admin
templates. But if it grew more sophisticated and required modification of
Django’s standard admin templates for some of its functionality, it would be
more sensible to modify the application’s templates, rather than those in the
project. That way, you could include the polls application in any new project
and be assured that it would find the custom templates it needed.
See the template loading documentation for more
information about how Django finds its templates.
Customize the admin index page¶
On a similar note, you might want to customize the look and feel of the Django
admin index page.
By default, it displays all the apps in INSTALLED_APPS
that have been
registered with the admin application, in alphabetical order. You may want to
make significant changes to the layout. After all, the index is probably the
most important page of the admin, and it should be easy to use.
The template to customize is admin/index.html
. (Do the same as with
admin/base_site.html
in the previous section – copy it from the default
directory to your custom template directory). Edit the file, and you’ll see it
uses a template variable called app_list
. That variable contains every
installed Django app. Instead of using that, you can hard-code links to
object-specific admin pages in whatever way you think is best.
Содержание
- Предварительные условия
- Настройка Django Admin
- Кастомизация Django Admin
- Изменение Change List с помощью list_display
- Добавление ссылок на страницы других объектов
- Добавление фильтров
- Добавление поиска
- Изменение способа редактирования моделей
- Переопределение шаблонов Django Admin
- Заключение
Фреймворк Django поставляется с мощным инструментом администрирования под названием admin. Вы можете использовать его прямо из коробки, чтобы быстро добавлять, удалять или редактировать любую модель базы данных из веб-интерфейса. А можете, написав немного кода, самостоятельно настроить Django admin и вывести свои административные возможности на новый уровень.
В этом руководстве вы узнаете, как:
- Добавить столбцы атрибутов в список объектов модели.
- Добавить ссылки на страницы других объектов.
- Добавить фильтры в список объектов модели.
- Добавить поиск в список объектов модели.
- Изменить форму редактирования объекта.
- Переопределить шаблоны Django admin.
Предварительные условия
Чтобы получить максимум от этого руководства, вы должны быть знакомы с Django, особенно с моделями. Поскольку Django не является частью стандартной библиотеки Python, то вы также должны быть знакомы с pip и pyenv (или аналогичным инструментом виртуального окружения). Чтобы узнать больше об этих темах, можете ознакомиться со следующими ресурсами:
- Get Started With Django Part 1: Build a Portfolio App
- What is Pip? A Guide for New Pythonistas
- Managing Multiple Python Versions With pyenv
- What Virtual Environments Are Good For
Фрагменты кода в этом руководстве были протестированы на Django 3.0.7. Они должны работать в любой версии, которую вы используете, но могут быть незначительные различия.
Настройка Django Admin
Django admin предоставляет веб-интерфейс для создания и управления объектами модели базы данных. Чтобы увидеть его в действии, вам сначала понадобится проект Django и несколько моделей. Установите Django в чистое виртуальное окружение:
$ python -m pip install django $ django-admin startproject School $ cd School $ ./manage.py startapp core $ ./manage.py migrate $ ./manage.py createsuperuser Username: admin Email address: admin@example.com Password: Password (again):
Сначала создайте новый проект Django под названием School
с приложением под названием core
. Затем выполните команду migrate
и создайте администратора. Доступ к админ-панели Django разрешен только пользователям с флагами staff
или superuser
, поэтому для создания суперпользователя используется команда createsuperuser
.
Вам также необходимо внести изменения в файл School/settings.py, чтобы включить новое приложение с именем core
:
# School/settings.py # ... INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", "core", # Add this line ]
Каталог приложения core
будет содержать следующие файлы:
core/ │ ├── migrations/ │ └── __init__.py │ ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py └── views.py
Из этих файлов нас интересуют два:
- models.py, в котором определяются модели.
- admin.py, в котором модели регистрируются в Django admin.
Чтобы продемонстрировать кастомизацию Django admin, вам понадобятся несколько моделей. Отредактируйте core/models.py следующим образом:
from django.core.validators import MinValueValidator, MaxValueValidator from django.db import models class Person(models.Model): last_name = models.TextField() first_name = models.TextField() courses = models.ManyToManyField("Course", blank=True) class Meta: verbose_name_plural = "People" class Course(models.Model): name = models.TextField() year = models.IntegerField() class Meta: unique_together = ("name", "year", ) class Grade(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) grade = models.PositiveSmallIntegerField( validators=[MinValueValidator(0), MaxValueValidator(100)]) course = models.ForeignKey(Course, on_delete=models.CASCADE)
Эти модели представляют учащихся, проходящих курсы в школе. У модели Course
есть два поля: название курса и год, в котором он был начат. Модель Person
имеет три поля: имя и фамилия учащегося и курсы, которые он проходит (ноль или более). Grade
содержит оценку, полученную учащимся (Person
) на курсе (Course
).
Вот диаграмма, показывающая отношения между объектами:
Имена таблиц в базе данных немного отличаются от этих, но они связаны с моделями, показанными выше.
Каждую модель, которую вы хотите отображать в админ-панели Django, необходимо зарегистрировать. Делается это в файле admin.py. Модели из core/models.py регистрируются в соответствующем файле core/admin.py:
from django.contrib import admin from core.models import Person, Course, Grade @admin.register(Person) class PersonAdmin(admin.ModelAdmin): pass @admin.register(Course) class CourseAdmin(admin.ModelAdmin): pass @admin.register(Grade) class GradeAdmin(admin.ModelAdmin): pass
Вы почти готовы к работе. После выполнения миграции вы можете запустить сервер разработки Django и увидеть следующие результаты:
$ ./manage.py makemigrations $ ./manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, core, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK ... Applying core.0001_initial... OK Applying core.0002_auto_20200609_2120... OK Applying sessions.0001_initial... OK $ ./manage.py runserver Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). Django version 3.0.7, using settings 'School.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
Теперь перейдите по адресу http://127.0.0.1:8000/admin, чтобы увидеть интерфейс администратора. Вам будет предложено войти в систему. Используйте учетные данные, созданные с помощью команды createsuperuser
.
На главной странице Django admin перечислены все зарегистрированные модели:
Теперь вы можете использовать админ-панель для создания объектов в своей базе данных. Щелкнув по названию модели, вы увидите список всех объектов в базе данных для этой модели. Вот, например, список людей (Person
):
Сначала список пустой, как и ваша база данных. Нажав ADD PERSON, вы можете создать человека в базе данных. После сохранения вы вернетесь к списку объектов Person
:
Хорошая новость в том, что у вас есть объект. Плохая новость в том, что Person object (1)
сообщает вам id объекта и ничего больше. По умолчанию, Django admin отображает каждый объект, вызывая функцию str()
. Вы можете сделать эту страницу более полезной, добавив метод .__str__()
в класс Person
в core/models.py:
class Person(models.Model): last_name = models.TextField() first_name = models.TextField() courses = models.ManyToManyField("Course", blank=True) def __str__(self): return f"{self.last_name}, {self.first_name}"
Добавление метода Person.__str__()
изменяет отображение: теперь вместо id отображаются имя и фамилия человека. Вы можете обновить страницу, чтобы увидеть изменения:
Так-то лучше! Теперь вы можете увидеть некоторую информацию об объекте Person
. Рекомендуется добавить подобные методы как к объектам Course
, так и к объектам Grade
:
class Course(models.Model): # ... def __str__(self): return f"{self.name}, {self.year}" class Grade(models.Model): # ... def __str__(self): return f"{self.grade}, {self.person}, {self.course}"
Чтобы увидеть полный эффект от вашей кастомизации, вам нужно иметь некоторые данные в своей базе данных. Вы можете создать свои собственные данные прямо сейчас, или можете пропустить этот шаг и использовать фикстуры (fixture).
Django позволяет загружать данные в базу данных и выгружать из нее с помощью файлов, называемых фикстурами. Скопируйте следующее в файл с именем core/fixtures/school.json:
[ { "model": "core.person", "pk": 1, "fields": { "last_name": "Harris", "first_name": "Xander", "courses": [ 1, 3 ] } }, { "model": "core.person", "pk": 3, "fields": { "last_name": "Rosenberg", "first_name": "Willow", "courses": [ 1, 2, 3 ] } }, { "model": "core.person", "pk": 16, "fields": { "last_name": "Summers", "first_name": "Buffy", "courses": [ 1, 2, 3 ] } }, { "model": "core.course", "pk": 1, "fields": { "name": "CompSci G11", "year": 1998 } }, { "model": "core.course", "pk": 2, "fields": { "name": "Psych 101", "year": 1999 } }, { "model": "core.course", "pk": 3, "fields": { "name": "Library Science G10", "year": 1997 } }, { "model": "core.grade", "pk": 1, "fields": { "person": 16, "grade": 68, "course": 1 } }, { "model": "core.grade", "pk": 2, "fields": { "person": 16, "grade": 87, "course": 2 } }, { "model": "core.grade", "pk": 3, "fields": { "person": 16, "grade": 76, "course": 3 } }, { "model": "core.grade", "pk": 4, "fields": { "person": 1, "grade": 58, "course": 1 } }, { "model": "core.grade", "pk": 5, "fields": { "person": 1, "grade": 58, "course": 3 } }, { "model": "core.grade", "pk": 6, "fields": { "person": 3, "grade": 98, "course": 3 } }, { "model": "core.grade", "pk": 7, "fields": { "person": 3, "grade": 97, "course": 2 } } ]
Создав файл, вы можете использовать команду loaddata
, чтобы загрузить его в свою базу данных:
$ ./manage.py loaddata school Installed 13 object(s) from 1 fixture(s)
Теперь в вашей базе данных есть несколько объектов Person
, Course
и Grade
.
А сейчас, когда у вас есть некоторые данные для работы, вы готовы приступить к кастомизации админ-панели Django.
Кастомизация Django Admin
Умные люди, создавшие фреймворк Django, не только создали админку, но и сделали это таким образом, чтобы вы могли кастомизировать ее для своих проектов. Когда вы ранее регистрировали объект PersonAdmin
, он наследовался от admin.ModelAdmin
. Большая часть настроек, которые вы можете сделать с помощью Django admin, выполняется путем модификации класса ModelAdmin
, и вы, конечно, можете изменять его!
ModelAdmin
имеет более тридцати атрибутов и почти пятьдесят методов. Вы можете использовать каждый из них для настройки админ-панели и управления интерфейсами ваших объектов. Каждый из этих вариантов подробно описан в документации.
В довершение ко всему, админка построена с использованием шаблонов Django. Механизм шаблонов Django позволяет вам переопределять существующие шаблоны, а поскольку Django admin — это просто еще один набор шаблонов, это означает, что вы можете полностью изменить его HTML-код.
Хотя это выходит за рамки данного руководства, вы даже можете создать несколько сайтов администрирования. Это может показаться излишним, но это позволяет вам фантазировать и делать разные сайты для пользователей с разными разрешениями.
Django admin разделен на три основные области:
- App index
- Change lists
- Change forms
В app index перечислены ваши зарегистрированные модели. Change list автоматически создается для каждой зарегистрированной модели и содержит список объектов для этой модели. Когда вы добавляете или редактируете один из этих объектов, вы делаете это с помощью change form.
В предыдущем примере app index отображал объекты Person
, Course
и Grade
. При нажатии на People
отображаются change list’ы для объектов Person
. На странице change list, щелкнув по объекту Buffy Summers, вы попадете на страницу change form, чтобы отредактировать данные о Buffy Summers.
Изменение Change List с помощью list_display
Реализация метода .__str__()
— это быстрый способ изменить представление объекта Person
с бессмысленной строки на понятные данные. Поскольку это представление также будет отображаться в раскрывающихся списках со множественным выбором, вы определенно захотите сделать его максимально простым и понятным.
Вы можете кастомизировать страницу change list гораздо большим количеством способов, чем просто изменить строковое представление объекта. Атрибут list_display
объекта admin.ModelAdmin
указывает, какие столбцы будут отображаться в change list’e. Это значение представляет собой кортеж атрибутов моделируемого объекта. Например, в core/admin.py измените PersonAdmin
следующим образом:
@admin.register(Person) class PersonAdmin(admin.ModelAdmin): list_display = ("last_name", "first_name")
Приведенный выше код модифицирует ваш Person
change list таким образом, что теперь будут отображаться атрибуты last_name
и first_name
для каждого объекта Person
. Каждый атрибут отображается в столбце на странице:
Эти два столбца кликабельны, что позволяет сортировать страницу по данным столбца. Django admin также учитывает атрибут ordering
в Meta-классе:
class Person(models.Model): # ... class Meta: ordering = ("last_name", "first_name") # ...
Добавление атрибута ordering
приведет к тому, что по умолчанию все запросы к Person
будут упорядочены по last_name
, а затем по first_name
. Django будет соблюдать этот порядок по умолчанию как в админке, так и при получении объектов.
Кортеж list_display
может ссылаться на любой атрибут объекта. Он также может ссылаться на метод в самом admin.ModelAdmin
. Снова изменим PersonAdmin
:
@admin.register(Person) class PersonAdmin(admin.ModelAdmin): list_display = ("last_name", "first_name", "show_average") def show_average(self, obj): from django.db.models import Avg result = Grade.objects.filter(person=obj).aggregate(Avg("grade")) return result["grade__avg"]
В приведенном выше коде вы добавляете столбец, в котором будет отображаться средний бал каждого учащегося. show_average()
вызывается один раз для каждого объекта, отображаемого в списке.
Параметр obj
— это объект для отображаемой строки. В этом случае вы используете его для запроса соответствующих объектов Grade
для учащегося, затем получаете среднее значение Grade.grade
. Вы можете увидеть результаты здесь:
Имейте в виду, что средний бал на самом деле нужно рассчитывать в объекте модели Person
. Скорее всего, вам понадобятся данные, находящиеся где-нибудь еще, а не только в Django admin. Если бы у вас был такой метод, вы могли бы добавить его в атрибут list_display
. Приведенный пример показывает, что вы можете делать в объекте ModelAdmin
, но, вероятно, это не лучший выбор для вашего кода.
По умолчанию сортируются только те столбцы, которые являются атрибутами объекта. show_average()
— нет. Это связано с тем, что сортировка выполняется с помощью QuerySet. В некоторых случаях есть способы сортировки этих столбцов, но это выходит за рамки нашего руководства.
Заголовок столбца основан на имени метода. Вы можете изменить заголовок, добавив атрибут к методу:
def show_average(self, obj): result = Grade.objects.filter(person=obj).aggregate(Avg("grade")) return result["grade__avg"] show_average.short_description = "Average Grade"
По умолчанию Django защищает вас от HTML в строках, если строка является результатом пользовательского ввода. Чтобы отображение включало HTML, вы должны использовать format_html()
:
def show_average(self, obj): from django.utils.html import format_html result = Grade.objects.filter(person=obj).aggregate(Avg("grade")) return format_html("<b><i>{}</i></b>", result["grade__avg"]) show_average.short_description = "Average"
Теперь столбец show_average()
имеет кастомное название «Average» и выделен курсивом:
К сожалению, в Django еще не добавлена поддержка f-строк для format_html()
, поэтому используется синтаксис str.format()
.
Добавление ссылок на страницы других объектов
Объекты довольно часто ссылаются на другие объекты с помощью внешних ключей (foreign key). Вы можете указать в list_display
метод, возвращающий ссылку HTML. Внутри core/admin.py измените класс CourseAdmin
следующим образом:
from django.urls import reverse from django.utils.http import urlencode @admin.register(Course) class CourseAdmin(admin.ModelAdmin): list_display = ("name", "year", "view_students_link") def view_students_link(self, obj): count = obj.person_set.count() url = ( reverse("admin:core_person_changelist") + "?" + urlencode({"courses__id": f"{obj.id}"}) ) return format_html('<a href="{}">{} Students</a>', url, count) view_students_link.short_description = "Students"
Теперь Course
change list состоит из трех столбцов:
- название курса.
- год, в котором был начат курс.
- ссылка, отображающая количество учащихся, проходящих курс.
Вы можете увидеть результат на следующем скриншоте:
Когда вы нажимаете по ссылке 2 Students, вы попадаете на страницу Person
change list с примененным фильтром. На отфильтрованной странице показаны только ученики из Psych 101: Buffy и Willow. Xander не поступил в университет.
В примере кода для поиска URL-адреса в админке используется функция reverse()
. Вы можете найти любую страницу Django admin, используя следующее соглашение об именах:
"admin:%(app)s_%(model)s_%(page)"
Эта структура имени разбивается следующим образом:
- admin: — это пространство имен.
- app — это название приложения.
- model — это объект модели.
- page — это тип страницы Django admin.
В приведенном выше примере view_students_link()
вы используете admin:core_person_changelist
, чтобы получить ссылку на страницу change list объекта Person
в приложении core
.
Вот доступные URL-адреса:
Страница |
URL |
Цель |
Change list |
%(app)s_%(model)s_changelist |
Список страниц объектов модели |
Add |
%(app)s_%(model)s_add |
Страница создания объекта |
History |
%(app)s_%(model)s_history |
Страница истории изменений объекта Принимает object_id в качестве параметра |
Delete |
%(app)s_%(model)s_delete |
Страница удаления объекта Принимает object_id в качестве параметра |
Change |
%(app)s_%(model)s_change |
Страница редактирования объекта Принимает object_id в качестве параметра |
Вы можете отфильтровать страницу change list, добавив к URL-адресу строку запроса. Эта строка запроса изменяет QuerySet, используемый для заполнения страницы. В приведенном выше примере строка запроса "?courses__id={obj.id}"
фильтрует список Person
по id курса (отображаются только те объекты, которые имеют соответствующее значение Person.course).
Эти фильтры поддерживают поиск полей QuerySet с использованием двойного подчеркивания (__
). Вы можете получить доступ к атрибутам связанных объектов, а также использовать модификаторы фильтра, такие как __exact
и __startswith
.
Полную информацию о том, чего вы можете достичь с помощью атрибута list_display
, вы можете найти в документации Django admin.
Добавление фильтров
Помимо фильтрации данных в change list через URL-адрес, вы также можете фильтровать с помощью встроенного виджета. Добавьте атрибут list_filter
к объекту CourseAdmin
в core/admin.py:
@admin.register(Course) class CourseAdmin(admin.ModelAdmin): list_display = ("name", "year", "view_students_link") list_filter = ("year", ) # ...
list_filter
отобразит новый раздел на странице со списком ссылок. В этом случае ссылки фильтруют страницу по годам. Список фильтров автоматически заполняется значениями лет, используемыми объектами Course
в базе данных:
Если щелкнуть по году справа, в список будут включены только объекты курса с этим значением года. Вы также можете фильтровать по атрибутам связанных объектов, используя синтаксис поиска поля __
. Например, вы можете отфильтровать объекты GradeAdmin
по course__year
, показывая объекты Grade
только для курсов определенного года.
Если вам нужен больший контроль над фильтрацией, вы даже можете создать объекты фильтра, которые определяют атрибуты поиска и соответствующий QuerySet.
Добавление поиска
Фильтры — не единственный способ уменьшить объем данных на экране. Django admin также поддерживает поиск с помощью опции search_fields
, которая добавляет на экран поле поиска. Вы устанавливаете его с помощью кортежа, содержащего имена полей, которые будут использоваться для построения поискового запроса в базе данных.
Все, что пользователь вводит в поле поиска, используется в условии OR полей, фильтрующих QuerySet. По умолчанию каждый параметр поиска окружен знаками %. То есть, если вы ищете r, то в результатах появится любое слово с r внутри. Вы можете быть более точными, указав модификатор __
в поле поиска.
Отредактируйте PersonAdmin
в core/admin.py следующим образом:
@admin.register(Person) class PersonAdmin(admin.ModelAdmin): search_fields = ("last_name__startswith", )
В приведенном выше коде поиск основан на фамилии. Модификатор __startswith
ограничивает поиск фамилиями, которые начинаются с параметра поиска. Поиск по R дает следующие результаты:
Каждый раз, когда выполняется поиск на странице change list, Django admin вызывает метод get_search_results()
вашего объекта admin.ModelAdmin
. Он возвращает QuerySet с результатами поиска. Вы можете точно настроить поиск, перегрузив метод и изменив QuerySet. Более подробную информацию можно найти в документации.
Изменение способа редактирования моделей
Вы можете кастомизировать не только страницу change list. Страницы, используемые для добавления или изменения объекта, основаны на ModelForm
. Django автоматически генерирует форму на основе редактируемой модели.
Вы можете контролировать, какие поля включены, а также их порядок. Измените свой объект PersonAdmin
, добавив атрибут fields
:
@admin.register(Person) class PersonAdmin(admin.ModelAdmin): fields = ("first_name", "last_name", "courses") # ...
Страницы добавления и изменения для Person
теперь помещают атрибут first_name
перед атрибутом last_name
, хотя сама модель указывает обратное:
ModelAdmin.get_form()
отвечает за создание ModelForm
для вашего объекта. Вы можете переопределить этот метод, чтобы изменить форму. Добавьте в PersonAdmin
следующий метод:
def get_form(self, request, obj=None, **kwargs): form = super().get_form(request, obj, **kwargs) form.base_fields["first_name"].label = "First Name (Humans only!):" return form
Теперь, когда отображается страница добавления или изменения, метка поля first_name
будет изменена.
Изменения метки может быть недостаточно, чтобы помешать вампирам зарегистрироваться в качестве студентов. Если вам не нравится ModelForm
, созданная для вас Django admin, вы можете использовать атрибут form
для регистрации пользовательской формы. Внесите следующие дополнения и изменения в core/admin.py:
from django import forms class PersonAdminForm(forms.ModelForm): class Meta: model = Person fields = "__all__" def clean_first_name(self): if self.cleaned_data["first_name"] == "Spike": raise forms.ValidationError("No Vampires") return self.cleaned_data["first_name"] @admin.register(Person) class PersonAdmin(admin.ModelAdmin): form = PersonAdminForm # ...
Приведенный выше код обеспечивает дополнительную проверку на страницах добавления и изменения человека (Person
). Объекты ModelForm
имеют богатый механизм проверки. В этом случае поле first_name
проверяется на соответствие имени «Spike». ValidationError
не позволяет студентам с этим именем зарегистрироваться:
Изменяя или заменяя объект ModelForm
, вы можете полностью контролировать внешний вид и проверку страниц, которые вы используете для добавления или изменения объекта.
Переопределение шаблонов Django Admin
Разработчики Django реализовали админку с помощью механизма шаблонов Django. Это сделало их работу немного проще, но это также принесет вам пользу, позволив вам переопределить шаблоны. Вы можете полностью кастомизировать админку, изменив шаблоны, используемые для отображения страниц.
Вы можете увидеть все шаблоны, используемые в админке, заглянув в пакет Django в своем виртуальном окружении:
.../site-packages/django/contrib/admin/templates/ │ ├── admin/ │ │ │ ├── auth/ │ │ └── user/ │ │ ├── add_form.html │ │ └── change_password.html │ │ │ ├── edit_inline/ │ │ ├── stacked.html │ │ └── tabular.html │ │ │ ├── includes/ │ │ ├── fieldset.html │ │ └── object_delete_summary.html │ │ │ ├── widgets/ │ │ ├── clearable_file_input.html │ │ ├── foreign_key_raw_id.html │ │ ├── many_to_many_raw_id.html │ │ ├── radio.html │ │ ├── related_widget_wrapper.html │ │ ├── split_datetime.html │ │ └── url.html │ │ │ ├── 404.html │ ├── 500.html │ ├── actions.html │ ├── app_index.html │ ├── base.html │ ├── base_site.html │ ├── change_form.html │ ├── change_form_object_tools.html │ ├── change_list.html │ ├── change_list_object_tools.html │ ├── change_list_results.html │ ├── date_hierarchy.html │ ├── delete_confirmation.html │ ├── delete_selected_confirmation.html │ ├── filter.html │ ├── index.html │ ├── invalid_setup.html │ ├── login.html │ ├── object_history.html │ ├── pagination.html │ ├── popup_response.html │ ├── prepopulated_fields_js.html │ ├── search_form.html │ └── submit_line.html │ └── registration/ ├── logged_out.html ├── password_change_done.html ├── password_change_form.html ├── password_reset_complete.html ├── password_reset_confirm.html ├── password_reset_done.html ├── password_reset_email.html └── password_reset_form.html
У шаблонизатора Django есть определенный порядок загрузки шаблонов. При загрузке шаблона он использует первый шаблон, соответствующий названию. Вы можете переопределить шаблоны админ-панели, используя ту же структуру каталогов и имена файлов.
Шаблоны Django admin находятся в двух каталогах:
- admin — предназначен для страниц объекта модели.
- registration — предназначен для смены пароля, входа и выхода.
Чтобы кастомизировать страницу выхода, вам необходимо переопределить правильный файл. Относительный путь, ведущий к файлу, должен совпадать с тем, который был переопределен. Нас интересует файл registration/logged_out.html. Начнем с создания каталога в проекте School
:
$ mkdir -p templates/registration
Теперь сообщите Django о вашем новом каталоге шаблонов в файле School/settings.py. Найдите TEMPLATES
и добавьте папку в список DIR
:
# School/settings.py # ... TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", # Добавление папки "DIRS": [os.path.join(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", ], }, }, ]
Шаблонизатор ищет каталоги в параметре DIR
перед каталогами приложений, поэтому вместо этого будет загружено все, что имеет то же имя, что и шаблон админ-панели. Чтобы увидеть это в действии, скопируйте файл logged_out.html в каталог templates/registration, затем измените его:
{% extends "admin/base_site.html" %} {% load i18n %} {% block breadcrumbs %}<div class="breadcrumbs"><a href="{% url 'admin:index' %}">{% trans 'Home' %}</a></div>{% endblock %} {% block content %} <p>You are now leaving Sunnydale</p> <p><a href="{% url 'admin:index' %}">{% trans 'Log in again' %}</a></p> {% endblock %}
Вы кастомизировали страницу выхода. Если вы нажмете LOG OUT, то увидите кастомное сообщение:
Шаблоны Django admin отличаются глубокой вложенностью и не очень интуитивно понятны, но вы можете полностью контролировать их представление, если вам это нужно. Некоторые пакеты, включая Grappelli и Django Admin Bootstrap, полностью заменили шаблоны Django admin, изменив их внешний вид.
Django Admin Bootstrap еще не совместим с Django 3, а Grappelli только недавно добавил поддержку, так что у него все еще могут быть некоторые проблемы. Но если вы хотите увидеть силу переопределения шаблонов админки, ознакомьтесь с этими проектами!
Заключение
Django admin — это мощный встроенный инструмент, дающий вам возможность создавать, обновлять и удалять объекты в вашей базе данных с помощью веб-интерфейса. Вы можете настроить Django admin для выполнения практически всего, что захотите.
В этом руководстве вы узнали, как:
- Зарегистрировать свои модели в Django admin.
- Добавить атрибуты в виде столбцов в change list.
- Создать значения столбцов с вычисляемым содержимым.
- Сделать перекрестные ссылки на страницы админки.
- Фильтровать страницы change list.
- Добавлять поиск на страницы change list.
- Кастомизировать объект ModelForm.
- Изменять HTML в шаблонах Django admin.
Это руководство охватывает только некоторые аспекты. Количество настроек, которые вы можете кастомизировать в Django admin, ошеломляет. Вы можете глубже погрузиться в документацию, чтобы изучить такие темы, как встроенные формы, несколько сайтов администрирования, массовое редактирование, автозаполнение и многое другое. Удачного программирования!
Often the default Django admin page is great and it is all we need for our projects but sometimes we may want to expand what default admin interface can do. We have a lot of tools in Django to customize it to our own needs and in this article, we’ll learn how to customize the admin interface and add multiple features:
Let’s setup your project, add models into models.py and register your models.
models.py
Run following commands –
Python manage.py makemigrations Python manage.py migrate
Now we have created following models, Here is how they look like in Admin panel –
Default Django admin
Django Admin – Redesign and Customization
Le’s check how we can add multiple features in Django admin panel. Here is a list of customizations –
1. Changing order of Fields
By default, the admin will display fields in the detail view in the same order as defined in the model. But we can change that by making a few edits to the admin.py file without going to models.py and changing the order of different fields.
Movie model: title length release_year
title=['release_year', 'title', 'length']
Movie_Model: release_year title length
admin.py
Python
from
django.contrib
import
admin
from
.models
import
Movie, Customer
class
MovieAdmin(admin.ModelAdmin):
title
=
[
'release_year'
,
'title'
,
'length'
]
admin.site.register(Movie, MovieAdmin)
admin.site.register(Customer)
Changed order of fields
2. Adding Search and Filters
Currently, we have only a few entries in our models, but what if the entries increase to hundreds or thousands due to more number of users? To get data of a particular entry will become tedious if we go by looking at each entry. Therefore we need to add a search bar or a filter feature to allow entries to be accessed easily.
Search by the following elements: search_fields = ['title', 'length', 'release_year'] search_fields = ['first_name', 'last_name', 'phone']
Filter by the following elements: list_filter = ['release_year'] list_filter = ['last_name']
app_folder / admin.py
Python
from
django.contrib
import
admin
from
.models
import
Movie, Customer
class
MovieAdmin(admin.ModelAdmin):
search_fields
=
[
'title'
,
'length'
,
'release_year'
]
list_filter
=
[
'release_year'
]
class
CustomerAdmin(admin.ModelAdmin):
search_fields
=
[
'first_name'
,
'last_name'
,
'phone'
]
list_filter
=
[
'last_name'
]
admin.site.register(Movie, MovieAdmin)
admin.site.register(Customer, CustomerAdmin)
Movies Model showing Filter and Search
Customers Model showing Search and Filter
3. Viewing Additional Fields
In admin interface, normally we see only one field of our models in the list view. We can add more fields to view with list_display.
list_display=['title', 'release_year'] list_display=['first_name', 'last_name', 'phone']
admin.py
Python
from
django.contrib
import
admin
from
.models
import
Movie, Customer
class
MovieAdmin(admin.ModelAdmin):
list_display
=
[
'title'
,
'release_year'
]
class
customerAdmin(admin.ModelAdmin):
list_display
=
[
'first_name'
,
'last_name'
,
'phone'
]
admin.site.register(Movie, MovieAdmin)
admin.site.register(Customer, CustomerAdmin)
title and release year Fields in Movie Model
first name, last name and phone number Fields in Customer Model
4. Editing List View
You can add the ability to edit attribute values directly from the list view instead of going to the detail view
editable_list = ['phone']
admin.py
Python
from
django.contrib
import
admin
from
.models
import
Movie, Customer
class
CustomerAdmin(admin.ModelAdmin):
editable_list
=
[
'phone'
]
admin.site.register(Movie)
admin.site.register(Customer, CustomerAdmin)
Phone Number is editable here in Customer Class
5. Admin Template
Sometimes you may want to change the layout and user interface of admin interface for which you need to add your own templates into the project which will then be reflected to your django admin interface.
Create a folder templates in the root folder and inside it, create another folder admin. Now add your html file inside the admin folder.
templates—->admin—->base_site.html
HTML
{% extends "admin/base.html" %}
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block branding %}
<
h1
id
=
"site-name"
style
=
"font-family: cursive;"
><
a
href
=
"{% url 'admin:index' %}"
>Video Rental Administration</
a
></
h1
>
{% endblock %}
{% block nav-global %}{% endblock %}
Heading is changed
Here, the heading in the admin interface has changed due to the addition of html file. Sometimes template doesn’t get applied to the interface, to avoid that, make sure you’ve done correct template configuration in the settings.py file.
import os TEMPLATES=[ 'DIRS': [os.path.join(BASE_DIR, 'templates')], ]
This how you can customize the admin template in Django as per your requirements.
Often the default Django admin page is great and it is all we need for our projects but sometimes we may want to expand what default admin interface can do. We have a lot of tools in Django to customize it to our own needs and in this article, we’ll learn how to customize the admin interface and add multiple features:
Let’s setup your project, add models into models.py and register your models.
models.py
Run following commands –
Python manage.py makemigrations Python manage.py migrate
Now we have created following models, Here is how they look like in Admin panel –
Default Django admin
Django Admin – Redesign and Customization
Le’s check how we can add multiple features in Django admin panel. Here is a list of customizations –
1. Changing order of Fields
By default, the admin will display fields in the detail view in the same order as defined in the model. But we can change that by making a few edits to the admin.py file without going to models.py and changing the order of different fields.
Movie model: title length release_year
title=['release_year', 'title', 'length']
Movie_Model: release_year title length
admin.py
Python
from
django.contrib
import
admin
from
.models
import
Movie, Customer
class
MovieAdmin(admin.ModelAdmin):
title
=
[
'release_year'
,
'title'
,
'length'
]
admin.site.register(Movie, MovieAdmin)
admin.site.register(Customer)
Changed order of fields
2. Adding Search and Filters
Currently, we have only a few entries in our models, but what if the entries increase to hundreds or thousands due to more number of users? To get data of a particular entry will become tedious if we go by looking at each entry. Therefore we need to add a search bar or a filter feature to allow entries to be accessed easily.
Search by the following elements: search_fields = ['title', 'length', 'release_year'] search_fields = ['first_name', 'last_name', 'phone']
Filter by the following elements: list_filter = ['release_year'] list_filter = ['last_name']
app_folder / admin.py
Python
from
django.contrib
import
admin
from
.models
import
Movie, Customer
class
MovieAdmin(admin.ModelAdmin):
search_fields
=
[
'title'
,
'length'
,
'release_year'
]
list_filter
=
[
'release_year'
]
class
CustomerAdmin(admin.ModelAdmin):
search_fields
=
[
'first_name'
,
'last_name'
,
'phone'
]
list_filter
=
[
'last_name'
]
admin.site.register(Movie, MovieAdmin)
admin.site.register(Customer, CustomerAdmin)
Movies Model showing Filter and Search
Customers Model showing Search and Filter
3. Viewing Additional Fields
In admin interface, normally we see only one field of our models in the list view. We can add more fields to view with list_display.
list_display=['title', 'release_year'] list_display=['first_name', 'last_name', 'phone']
admin.py
Python
from
django.contrib
import
admin
from
.models
import
Movie, Customer
class
MovieAdmin(admin.ModelAdmin):
list_display
=
[
'title'
,
'release_year'
]
class
customerAdmin(admin.ModelAdmin):
list_display
=
[
'first_name'
,
'last_name'
,
'phone'
]
admin.site.register(Movie, MovieAdmin)
admin.site.register(Customer, CustomerAdmin)
title and release year Fields in Movie Model
first name, last name and phone number Fields in Customer Model
4. Editing List View
You can add the ability to edit attribute values directly from the list view instead of going to the detail view
editable_list = ['phone']
admin.py
Python
from
django.contrib
import
admin
from
.models
import
Movie, Customer
class
CustomerAdmin(admin.ModelAdmin):
editable_list
=
[
'phone'
]
admin.site.register(Movie)
admin.site.register(Customer, CustomerAdmin)
Phone Number is editable here in Customer Class
5. Admin Template
Sometimes you may want to change the layout and user interface of admin interface for which you need to add your own templates into the project which will then be reflected to your django admin interface.
Create a folder templates in the root folder and inside it, create another folder admin. Now add your html file inside the admin folder.
templates—->admin—->base_site.html
HTML
{% extends "admin/base.html" %}
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block branding %}
<
h1
id
=
"site-name"
style
=
"font-family: cursive;"
><
a
href
=
"{% url 'admin:index' %}"
>Video Rental Administration</
a
></
h1
>
{% endblock %}
{% block nav-global %}{% endblock %}
Heading is changed
Here, the heading in the admin interface has changed due to the addition of html file. Sometimes template doesn’t get applied to the interface, to avoid that, make sure you’ve done correct template configuration in the settings.py file.
import os TEMPLATES=[ 'DIRS': [os.path.join(BASE_DIR, 'templates')], ]
This how you can customize the admin template in Django as per your requirements.