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, ошеломляет. Вы можете глубже погрузиться в документацию, чтобы изучить такие темы, как встроенные формы, несколько сайтов администрирования, массовое редактирование, автозаполнение и многое другое. Удачного программирования!
Архив проекта: lesson-24-coolsite.zip
На этом занятии
мы сделаем некоторые изменения в админ-панели, настроим ее под свой
разрабатываемый сайт. Если перейти в документацию по Django, то на
официальной странице:
https://docs.djangoproject.com/en/3.1/
есть раздел «Admin» и ссылка «Admin site», где подробно
объясняются различные нюансы настройки админ-панели. Это огромная тема,
поэтому, мы коснемся лишь некоторых базовых вещей.
Меняем стили оформления
Вначале давайте
посмотрим, как можно поменять стили оформления админ-панели. Это бывает
необходимо, чтобы админка визуально выглядела в тех же тонах, что и основной
сайт. Для этого, мы с помощью панели Debug Toolbar посмотрим какие
используются шаблоны для формирования этой страницы:
- admin/index.html
D:\Python\django\djsite\venv\lib\site-packages\django\contrib\admin\templates\admin\index.html
- admin/base_site.html
D:\Python\django\djsite\venv\lib\site-packages\django\contrib\admin\templates\admin\base_site.html
- admin/base.html
D:\Python\django\djsite\venv\lib\site-packages\django\contrib\admin\templates\admin\base.html
- admin/app_list.html
D:\Python\django\djsite\venv\lib\site-packages\django\contrib\admin\templates\admin\app_list.html
Здесь нам
показываются четыре шаблона и по названию можно понять, что базовым для страниц
сайта является шаблон base_site.html. Также под ним прописан путь, где он
располагается.
Давайте, откроем
его и по идее, конечно, можно было бы внести изменения прямо здесь. Но это не
лучшая практика. Все дополнительные файлы, которые были сформированы при
установке Django, лучше
оставлять без изменений. А переопределение делать уже непосредственно в нашем
проекте. В частности, если добавить в каталог coolsite/templates подкаталог admin и там
разместить файл с тем же именем base_site.html, то Django, сканируя
первым этот каталог, найдет первым файл admin/base_site.html и именно его
будет использовать.
Создадим такой
файл. Перейдем в браузер, обновим страницу админ-панели и не увидим никаких
изменений. Мало того, если посмотреть в Debug Toolbar, то путь к
шаблону base_site.html остался прежним.
Что не так? Дело в том, что Django по умолчанию ищет шаблоны
исключительно в папках приложений. У нас же каталог templates находится
непосредственно в корне проекта, что необходимо, чтобы произошло
переопределение шаблона base_site.html. Этот новый,
нестандартный путь следует указать в файле конфигурации settings.py в коллекции TEMPLATES:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], ... }, ]
Теперь, после
перезапуска веб-сервера и обновлении админ-панели увидим пустую страницу, т.к.
именно наш шаблон сейчас был взят. Скопируем в него содержимое из аналогичного
файла админки и при обновлении видим все ту же админку.
В файле base_site.html видим три
блока:
- block title – отвечает за
отображение заголовка во вкладке; - block branding – за
отображение ссылки «Администрирование Django» в верхней панели; - block nav-global
– глобальный блок навигации.
Сейчас мы хотим
прописать свои собственные стили оформления. Как это сделать? Для этого откроем
шаблон более верхнего уровня base.html и видим
специальный блок:
{% block
extrastyle %}{% endblock %}
который служит
для добавления дополнительных стилей оформления. Пропишем его в файле base_site.html и в нем укажем
свой файл оформления:
{% block extrastyle %} <link rel="stylesheet" href="{% static 'css/admin.css' %}"> {% endblock %}
Соответственно,
выше загрузим тег static:
и сформируем
файл admin.css в нашем
приложении в каталоге static/css:
#header { background: #5e3a00; }
Почему мы
прописали именно такой селектор для изменения цвета верхней панели? Очень
просто. Если перейти в браузер и проинспектировать этот элемент, то увидим тег div с id=»header». Значит,
для назначения свойств, можно использовать этот идентификатор. По аналогии
можно переобозначать стили для всех других элементов админ-панели. Например,
такой же стиль пропишем и для заголовков блоков:
#header, .module caption { background: #5e3a00; }
Я думаю, общий
принцип понятен. Этим способом можно переопределить все стили и настроить вид
админки под дизайн, цветовую схему вашего сайта.
Если же вам
нужно изменить сам заголовок «Администрирование Django», то это лучше
делать через модуль admin (файл women/admin.py), в котором,
следует определить два таких атрибута:
admin.site.site_title = 'Админ-панель сайта о женщинах' admin.site.site_header = 'Админ-панель сайта о женщинах'
Полный список
подобных атрибутов можно посмотреть на странице документации:
https://docs.djangoproject.com/en/3.1/ref/contrib/admin/
Добавляем отображение изображений для постов в списке
Следующим шагом
добавим отображение миниатюр изображений, связанных с нашими постами,
непосредственно в списке. Сейчас у нас происходит отображение пути к
изображению в колонке «Фото». Мы же хотим показывать непосредственно
изображение, а не путь. Как это сделать?
Для этого в
классе WomenAdmin (файл women/admin.py) следует
прописать метод, который бы возвращал HTML-код. И этот HTML-фрагмент,
затем, подставим вместо путей. Имя метода мы придумываем сами, например, так:
def get_html_photo(self, object): return mark_safe(f"<img src='{object.photo.url}' width=50>")
Этот метод будет
принимать второй параметр object – ссылку на
текущую запись (на текущий пост) и, затем, возвращаем фрагмент HTML, но помеченный
фильтром safe, чтобы теги
воспринимались как теги и не экранировались, то есть, не заменялись бы
спецсимволами. Именно для этого и используется функция mark_safe(). Все, если
теперь вместо поля photo (в списке list_display) указать метод get_html_photo:
list_display = ('id', 'title', 'time_create', 'get_html_photo', 'is_published')
должны увидеть
вместо маршрутов картинки, связанные с постами. Однако, у нас отображается
ошибка с исключением ValueError, так как не все посты имеют
изображения. Поэтому в методе get_html_photo следует
прописать проверку:
def get_html_photo(self, object): if object.photo: return mark_safe(f"<img src='{object.photo.url}' width=50>")
Причем, по else можно ничего не
возвращать (будет формироваться значение None), тогда Django автоматически
поставит дефис там, где фотография отсутствует. Если же прописать иначе:
def get_html_photo(self, object): if object.photo: return mark_safe(f"<img src='{object.photo.url}' width=50>") else: return "Нет фото"
То вместо дефиса
увидим нашу строку «Нет фото». Наконец, чтобы поменять название столбца (вместо
«GET HTML PHOTO»), нужно у объекта-метода get_html_photo определить
специальный атрибут:
get_html_photo.short_description = "Миниатюра"
Этот пример с
выводом фотографии показывает, как можно заменять стандартный вывод полей
списка на свой собственный, определяя дополнительные специальные методы и
указывая их в списке list_display. То есть,
следуя этому принципу, мы можем формировать самую разную информацию для
отображения в списках.
Наконец, мы
можем сделать вывод фотографии и непосредственно на странице редактирования
поста. Для этого, опять же в классе WomenAdmin (файл women/admin.py) пропишем еще
один атрибут:
fields = ('title', 'slug', 'cat', 'content', 'photo', 'is_published')
Этот атрибут
содержит порядок и список редактируемых полей, которые следует отображать в
форме редактирования. Если же дополнительно нужно показывать не редактируемые
поля, то дополнительно нужно прописать атрибут:
readonly_fields = ('time_create', 'time_update')
И только после
этого, их можно добавить в коллекцию fields:
fields = ('title', 'slug', 'cat', 'content', 'photo', 'is_published', 'time_create', 'time_update')
Отлично, это мы
сделали, но как отобразить миниатюру? Для этого достаточно указать ссылку на
наш метод get_html_photo() в этих
атрибутах:
fields = ('title', 'slug', 'cat', 'content', 'photo', 'get_html_photo', 'is_published', 'time_create', 'time_update') readonly_fields = ('time_create', 'time_update', 'get_html_photo')
Все, теперь мы
видим изображение загруженной фотографии в форме редактирования.
Чтобы узнать о
других возможностях настройки формы редактирования, на странице документации
можно посмотреть список атрибутов для класса ModelAdmin. В частности,
установка вот такого атрибута:
добавляет верхнюю
панель для управления записью, что бывает очень удобно.
Вот так, можно
настраивать стили и отображение содержимого админ-панели.
Видео по теме
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.
I want to change certain css in admin django like base.css. Is it better to change directly in the django library? How can I override it in the best way?
asked Sep 9, 2011 at 4:06
rajan sthapitrajan sthapit
3,98610 gold badges41 silver badges63 bronze badges
0
It depends a lot of what you want to do. Though first of all: do not overwrite it in the Django admin directly. You got two options I think are reasonable:
- If you want to change the appearance of the admin in general you should override admin templates. This is covered in details here: Overriding admin templates. Sometimes you can just extend the original admin file and then overwrite a block like
{% block extrastyle %}{% endblock %}
indjango/contrib/admin/templates/admin/base.html
as an example. - If your style is model specific you can add additional styles via the
Media
meta class in youradmin.py
. See an example here:
class MyModelAdmin(admin.ModelAdmin): class Media: js = ('js/admin/my_own_admin.js',) css = { 'all': ('css/admin/my_own_admin.css',) }
answered Sep 9, 2011 at 4:38
7
- In
settings.py
, make sure your app is listed before admin in theINSTALLED_APPS
. - Create
(your-app)/templates/admin/base_site.html
and put the<style>
block into the{% block extrahead %}
Example:
{% extends "admin/base_site.html" %}
{% block extrahead %}
<style>
.field-__str__ {
font-family: Consolas, monospace;
}
</style>
{% endblock %}
answered Jan 30, 2018 at 16:25
3
This solution will work for the admin site, I think it’s the cleanest way because it overrides base_site.html
which doesn’t change when upgrading django.
Create in your templates directory a folder called admin
in it create a file named base_site.html
.
Create in your static directory under css
a file called admin-extra.css
.
Write in it all the custom css you want for your forms like: body{background: #000;}
.
Paste this in the base_site.html
:
{% extends "admin/base.html" %}
{% load static from staticfiles %} # This might be just {% load static %} in your ENV
{% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "css/admin-extra.css" %}" />{% endblock %}
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1>
{% endblock %}
{% block nav-global %}{% endblock %}
As mentioned in the comments:
make sure your app is before the admin app in INSTALLED_APPS, otherwise your template doesn’t override django’s
That’s It! you’re done
answered May 19, 2016 at 8:08
elad silverelad silver
8,8624 gold badges43 silver badges66 bronze badges
7
I just extended admin/base.html to include a reference to my own css file — at the end. The beauty of css is that you don’t have to touch existing definitions, just re-define.
answered Sep 9, 2011 at 5:33
Danny W. AdairDanny W. Adair
12.2k4 gold badges42 silver badges49 bronze badges
0
In your static directory, create a static/admin/css/base.css
file.
Paste in Django’s default Admin CSS first, then add your customizations at the bottom.
answered Sep 6, 2013 at 2:26
Ryan AllenRyan Allen
5,1254 gold badges25 silver badges33 bronze badges
4
If you want a global scope and you don’t want to think about overriding templates a mixin works really well for this. Put this code wherever you want:
class CSSAdminMixin(object):
class Media:
css = {
'all': ('css/admin.css',),
}
Then, make a CSS file called admin.css
with your overrides, for example:
select[multiple] {
resize: vertical;
}
Then, in whatever models you want, do:
class MyModelAdmin(admin.ModelAdmin, CSSAdminMixin):
And you’ll be all set.
answered Jul 26, 2017 at 16:51
mlissnermlissner
16.9k17 gold badges98 silver badges168 bronze badges
2
Have admin/css/changelists.css
inside a folder in STATICFILES_DIRS
, and it will user that changelists.css instead of the default admin one.
answered May 12, 2015 at 12:53
belteshazzarbelteshazzar
2,1332 gold badges20 silver badges30 bronze badges
It just so happens that using <style>
tag inside {% block extrastyle %}{% endblock %}
did not work for me when I wanted to override css. Theming support provides the correct way. All I was missing is {{ block.super }}
:-
{% extends 'admin/base.html' %}
{% block extrastyle %}{{ block.super }}
<style>
--- your style ---
--- properties here ---
</style>
{% endblock %}
answered Aug 26, 2022 at 10:56
Aseem YadavAseem Yadav
7187 silver badges15 bronze badges
6 августа 2021
11 871
0
Время чтения ≈ 30 минут
Содержание:
- Определение цифровой трансформации
- Установка Django
- Загрузка фикстур в Django
- Кастомизация Django Admin
- Модификация списка объектов с помощью list_display
- Гиперссылки на страницы других объектов
- Добавление фильтров на экран объектов
- Изменение способа редактирования моделей
- Переопределение шаблонов Django admin
- Заключение
Высокоуровневый фреймворк Python Django поставляется с панелью администратора, которая также называется Django admin. Это мощный инструмент управления, позволяющий сразу после установки фреймворка добавлять, удалять или редактировать любую модель базы данных через веб-интерфейс.
Но добавив немного кода, можно настроить Django admin под себя и вывести возможности администрирования на новый уровень.
Благодаря этому руководству вы научитесь:
- Добавлять столбцы атрибутов в списки объектов модели.
- Создавать связи между объектами моделей.
- Добавлять фильтры к списку объектов модели.
- Добавлять поиск для списков объектов моделей.
- Изменять формы редактирования объекта.
- Переопределять административные шаблоны Django.
Предварительные требования
Чтобы пользоваться данным руководством наиболее эффективно, вы должны быть немного знакомы с Django, в частности с объектами моделей (model objects). Поскольку Django не часть стандартной библиотеки Python, будет лучше, если вы предварительно освоите «pip», «pyenv» или аналогичные инструменты виртуальной среды.
Это руководство было протестировано на Django 3.0.7. Но основные принципы неизменны со времён Django 2.0, так что всё должно работать на любой используемой версии ПО, хотя возможны небольшие отличия.
Панель администратора Django (Django admin panel) предоставляет веб-интерфейс для создания и управления объектами модели базы данных. Чтобы увидеть его в действии, нам сначала понадобится проект 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». После этого мы выполняем миграцию таблиц аутентификации и создаём пользователя с правами администратора.
Доступ к экранам (screens) Django admin предоставляется пользователями с флажками «staff» или «superuser». Поэтому для создания суперпользователя используем команду управления createsuperuser.
Также нужно изменить файл «School/settings.py», включив новое приложение Django под именем «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", # Добавьте эту строку ]
В каталоге приложения «core» появятся файлы:
Из этих файлов нас интересуют два:
- «models.py» — определяет наши модели баз данных.
- «admin.py» — регистрирует эти модели с помощью Django admin.
Чтобы показать результат кастомизации админ панели Django, понадобится несколько моделей. Отредактируем файл «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») имеет название («name») и год («year»), в котором его предлагают.
Каждый ученик («Person») имеет имя («first_name») и фамилию («last_name»). Он может посещать «0» или более курсов («courses»). Оценка («Grade») хранит процентные баллы, полученные учеником на курсе.
Диаграмма модели иллюстрирует отношения между объектами:
Соответствующие имена таблиц в базе данных слегка отличаются, но соотносятся с приведённой моделью.
Каждую модель, которую 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».
На начальной странице панели администратора приведены все зарегистрированные модели баз данных:
Теперь можно воспользоваться интерфейсом для создания объектов в нашей базе данных. Если кликнуть на имя модели, появится экран со списком всех объектов её базы данных. Вот список «Person»:
В начале список пуст, как и вся наша база данных Django. Внести ученика в базу данных можно, кликнув на элемент «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__()» изменяет интерфейс так, чтобы для каждого объекта «Person» отображались имя и фамилия. Чтобы увидеть изменения, можно обновить экран:
Стало гораздо лучше! Теперь мы видим об объекте «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}"
Чтобы по достоинству оценить результат такой кастомизации, надо внести в базу данных больше информации. Можно попробовать создать свои данные или воспользоваться готовой фикстурой (тестовыми данными).
Загрузка фикстур в Django
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 не просто сделали для него панель администратора, но и заложили возможность тонкой настройки проектов на Django через неё.
Регистрируя ранее объект «PersonAdmin», мы унаследовали его из класса «admin.ModelAdmin». Большая часть кастомизации Django admin выполняется через изменение объектов «ModelAdmin», которые специально предназначены для этого.
У класса «ModelAdmin» больше тридцати атрибутов и почти пятьдесят методов. Для тонкой настройки отображения панели администратора и управления объектами интерфейса можно использовать каждый из них. В документации подробно описаны все эти опции.
В довершение ко всему, панель администратора создана с использованием шаблонов Django. Механизм шаблонов Django позволяет переопределить существующие шаблоны. И, поскольку панель администратора — просто очередной набор шаблонов, её HTML-код полностью открыт для изменений.
Этот механизм позволяет даже создавать множественные административные панели к одному сайту. Хотя это может показаться уже избыточном функционалом, но именно так можно дать волю воображению, создавая разные сайты для пользователей с разными правами.
Django admin состоит из трёх основных частей:
- «Индекс приложения» (app index) отображает перечень зарегистрированных моделей.
- «Списки объектов» (change lists) автоматически создаются для каждой зарегистрированной модели и перечисляют её объекты.
- «Формы изменений» (change forms) позволяют добавлять или изменять эти объекты.
В примере выше индекс приложения показал объекты «Person», «Course» и «Grade». Кликнув на элемент «People», мы видим списки объектов «Person». На странице списка объектов «Person» можно кликнуть на имя Баффи Саммерс (Buffy Summers) и попасть на форму изменения для редактирования данных.
Модификация списка объектов с помощью list_display
Определение функции «.__str__()» — быстрый способ поменять представление объектов «Person» с (на первый взгляд) бессмысленного набора символов на понятные данные. Это представление также используется для выпадающих списков и множественного выбора, поэтому лучше сделать его как можно понятнее и нагляднее.
Помимо изменения строки представления объекта, страницы списков объектов можно настроить и по другим параметрам. Атрибут «list_display» объекта «admin.ModelAdmin» указывает, какие столбцы отобразятся в списке объектов.
Это значение представляет собой кортеж атрибутов моделируемого объекта. Например, изменим в файле «core/admin.py» объект «PersonAdmin»:
@admin.register(Person) class PersonAdmin(admin.ModelAdmin): list_display = ("last_name", "first_name")
Эти команды модифицируют наш список объектов «Person» так, чтобы для каждого объекта «Person» отображались атрибуты «last_name» и «first_name».
Каждый атрибут показан на столбце страницы:
На оба столбца можно кликнуть для сортировки страницы по данным столбца. Django admin также учитывает атрибут «ordering» раздела «Meta»:
@admin.register(Person) class PersonAdmin(admin.ModelAdmin): list_display = ("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-кода в строках, на случай, если строка получена от пользователя. Чтобы его задействовать, нужно использовать функцию «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()».
Гиперссылки на страницы других объектов
Одни объекты часто ссылаются на другие, используя внешние ключи. Можно включить в кортеж «list_display» метод, возвращающий HTML-ссылку.
Изменим класс «CourseAdmin» в файле «core/admin.py» таким образом:
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): from django.utils.html import format_html 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» будет иметь три столбца:
- «NAME» — название курса.
- «YEAR» — год, в котором предлагается курс
- «STUDENTS» — гиперссылка с указанием числа учеников
Изменения можно увидеть на скриншоте:
Кликнув на ссылку «2 Students», мы попадём на страницу со списком объектов «Person», к которой применён фильтр. Отфильтрованная страница показывает только учеников курса «Psych 101» — Баффи (Buffy) и Уиллоу (Willow). Ксандер в университет не попал.
Приведённый код использует функцию «reverse()» для нахождения гиперссылки в Django admin. Также можно найти любую страницу панели администратора.
Используется следующий формат именования:
"admin:%(app)s_%(model)s_%(page)"
Он состоит из нескольких частей:
- «admin:» — ключевое слово.
- «app» — название приложения.
- «model»— объект модели.
- «page» — страница административной панели Django.
Вернёмся к примеру с функцией «view_students_link()». Там для получения ссылки на страницу со списком объектов «Person» приложения «core» использовался метод «admin:core_person_changelist».
Вот доступные URL-адреса:
Страница | URL-адрес | Назначение |
Список объектов | %(app)s_%(model)s_changelist | Страница со списком объектов модели. |
Добавление | %(app)s_%(model)s_add | Страница создания объектов |
История | %(app)s_%(model)s_history | Страница с историей изменения объекта. В качестве параметра принимает object_id. |
Удаление | %(app)s_%(model)s_delete | Страница удаления объекта. В качестве параметра принимает object_id. |
Изменение | %(app)s_%(model)s_change | Страница изменения объекта. В качестве параметра принимает object_id. |
Страницу объекта можно фильтровать, добавляя в URL запрос из строковой переменной. Он изменит выборку «QuerySet», используемую для формирования страницы.
В приведённом выше примере запрос из строковой переменной «?courses__id={obj.id}» фильтрует список «Person», оставляя лишь объекты с совпадающим значением «Person.course».
Для выборок «QuerySet» эти фильтры поддерживают запросы типа «field lookups», использующие двойные подчерки («__»). Можно получить доступ к атрибутам соответствующих объектов или использовать такие модификаторы фильтров, как «__exact» и «__startswith».
Подробности о том, чего можно достичь с помощью использования атрибута «list_display», можно найти в документации по панели администратора Django.
Добавление фильтров на экран объектов
Помимо фильтрации списка объекта через URL, можно добавить фильтр с помощью встроенного виджета. Добавим для объекта «CourseAdmin» атрибут «list_filter» в файле «core/admin.py»:
@admin.register(Course) class CourseAdmin(admin.ModelAdmin): list_display = ("name", "year", "view_students_link") list_filter = ("year", ) # ...
Атрибут «list_filter» отобразит на странице новый раздел со списком ссылок. В нашем случае это ссылки для фильтрации страниц по году. Список фильтра автоматически наполняется значениями «year» объектов «Course» из базы данных:
Кликая на год («year») справа страницы, мы изменим список, включив только объекты «Course» с заданным значением «year».
Фильтрация также возможна на основании атрибутов связанных объектов при использовании синтаксиса «field lookup» («__»). Например, можно отфильтровать объекты «GradeAdmin» с помощью «course__year». Это выведет только объекты «Grade» за определённый год.
Если нужны расширенные возможности фильтрации, то можно даже создавать объекты фильтра (filter objects), задающие атрибуты поиска и нужную выборку «QuerySet».
Добавление поиска на экран объектов
Фильтры — не единственный способ уменьшить объёмы данных на экране. Django admin также поддерживает поиск с помощью опции «search_fields», которая добавляет на экран поле поиска (search box). Его связывают с кортежем, содержащим имена полей, которые будут использованы для формирования строкового запроса к базе данных.
Всё, что пользователь введёт в поле поиска, будет использовано в качестве фильтра полей для выборки «QuerySet». Условие будет разделено операторами «OR». По умолчанию каждый поисковый параметр окружён знаками «%». Это означает, что если мы будем искать «r», то в результат попадёт любое слово, содержащее «r». Можно указать поле поиска и точнее, используя модификатор «__».
Отредактируем объект «PersonAdmin» в файле «core/admin.py» так:
@admin.register(Person) class PersonAdmin(admin.ModelAdmin): search_fields = ("last_name__startswith", )
В приведённом выше коде поиск основан на фамилии. Модификатор «__startswith» ограничивает поиск по фамилии теми вхождениями, которые начинаются на запрос. Поиск по «R» даст такой результат:
При каждом поиске на странице объектов, Django admin вызывает метод «get_search_results()» нашего объекта «admin.ModelAdmin». Этот метод возвращает выборку «QuerySet» с результатом поиска. В официальной документации также показано, как настроить поиск, перегружая метод и изменяя выборку «QuerySet».
Изменение способа редактирования моделей
Кастомизация возможна не только для страницы списка объектов. Экраны, используемые для добавления и удаления объектов, основаны на объекте «ModelForm». Django автоматически генерирует форму, основываясь на редактируемой модели.
Редактируя опцию «fields», можно управлять включением и порядком полей. Изменим наш объект «PersonAdmin», добавив атрибут «fields»:
@admin.register(Person) class PersonAdmin(admin.ModelAdmin): fields = ("first_name", "last_name", "courses") # ...
Теперь страницы добавления («Add») и изменения («Change») для объектов «Person» выводят атрибут имя («first_name») перед фамилией («last_name»), хотя в самой модели порядок обратный:
За создание нашего объекта «ModelForm» отвечает метод «ModelAdmin.get_form()». Для изменения формы этот метод можно переопределить.
Добавим в объект «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 создавали панель администратора, используя механизм шаблонов. Это упростило им работу, но и нам позволяет извлекать выгоды из возможности переопределять шаблоны. Изменяя шаблоны выдачи страниц, можно полностью кастомизировать панель администратора.
Можно увидеть все её шаблоны, заглянув в виртуальную среду пакета Django:
Движок шаблонов Django имеет определённый порядок их загрузки. Загружая какой-либо шаблон, он использует первый же, совпадающий по имени. Административные шаблоны можно переопределить, используя ту же структуру каталогов и имена файлов.
Административные шаблоны находятся в двух каталогах:
- admin — для страниц объектов модели.
- registration — для смены пароля, а также входа и выхода из системы.
Для тонкой настройки страницы выхода из системы («logout page»), нужно переназначить нужный файл. Относительный путь к файлу должен быть идентичен переназначаемому.
Нужный нам файл — «registration/logged_out.htm»l. Начнём с создания каталога в проекте «School»:
mkdir -p templates/registration
Теперь расскажем Django о нашем новом каталоге шаблонов при помощи файла «School/settings.py». Находим указатель «TEMPLATES» и добавляем каталог в список «DIR»:
# School/settings.py # ... TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", // Добавьте каталог с шаблонами через опцию DIR: "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 — мощный встроенный инструмент со множеством возможностей. Она позволяет создавать, обновлять и удалять объекты вашей базы данных с помощью веб-интерфейса.
В этом руководстве мы показали как:
- Регистрировать объекты модели с помощью Django admin.
- Добавлять атрибуты как столбцы списка объектов.
- Создавать значения столбцов с помощью автоматически вычисляемого содержимого (calculated content).
- Делать перекрёстные ссылки для административных страниц
- Фильтровать страницу списка объектов с помощью строкового запроса
- Добавлять поиск для ваших списков объектов
- Кастомизировать автоматический объект «ModelForm»
- Изменять HTML-код административных шаблонов Django
Этот обзор затрагивает только основы. Настойки конфигурации Django admin гораздо шире. Изучив официальную документацию, можно освоить такие возможности, как встроенные формы (inline forms), множественные сайты администратора (admin sites), массовое редактирование, автозаполнение и многое другое.
Нужна надёжная база для размещения сайта? Выбирайте виртуальные сервера от Eternalhost с технической поддержкой 24/7 и бесплатной защитой от DDoS!
Автор оригинала: Christopher Trudeau
Оцените материал:
[Всего голосов: 0 Средний: 0/5]
Админ-панель Django выглядит достаточно неплохо: не раздражает цвет, адаптивный дизайн, всё расположено
довольно логично… Тем не менее, многие не хотят видеть надпись «Django», да и а целом разукрасить
админку в «свои цвета».
Настраиваем отображение заголовка, вкладки
Самое первое, что бросается в глаза — страница логина, на которой открытым текстом говорится о фреймфорке,
первая страница после логина — также приветствие от Django, да и название вкладки намекает на нашего веб-друга.
Чтобы поменять всё это «безобразие», нужно в admin.py
сделать всего 3 настройки:
from django.contrib import admin
admin.site.site_header = "Лучшая админка"
admin.site.site_title = "Панель администрирования"
admin.site.index_title = "Добро пожаловать в админку"
Ну вот и готова наша BolgenCMS — можно уже продавать. Хотя и раньше можно было — лицензия позволяет.
Но если всё же вам важно изменить эти надписи — теперь можете изменить.
Убираем «ненужные» разделы админки
При первом входе в админ-панель Django можно увидеть два раздела для редактирования пользователей и групп.
Если они вам по какой-то причине мешают — их можно также просто убрать. Для этого «разрегистрируем» эти админки:
from django.contrib import admin
from django.contrib.auth.models import User, Group
admin.site.unregister(User)
admin.site.unregister(Group)
В предыдущей заметке о настройке Django
админки мы напротив — регистрировали свою модель в админку. Здесь же мы удаляем «ненужное».
Есть время создавать админки, а есть время их удалять…
Добавляем логотип к нашей админке
Понятно, что у лучшей админки должен быть как минимум свой логотип. Чтобы добавить логотип, нам нужно для начала
включить работу со статическими файлами. Поэтому создадим в корне проекта папку static
и зарегистрируем её
в settings.py
:
STATIC_URL = '/static/' # Вот под этой строчкой
STATICFILES_DIRS = [f'{BASE_DIR}/static/']
Добавим в нашу папку для статики файл логотипа pony.png
.
Теперь нам нужно переопределить шаблон для заголовка страницы — templates/admin/base_site.html
.
Для переопределения шаблонов я обычно завожу отдельную папку либо в главном приложении, либо в корне проекта.
Чтобы это сделать, немного отредактируем переменную TEMPLATES
в sessings.py
:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [f'{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',
],
},
},
]
Сам файл шаблона заголовка админ-панели создаём в файле templates/admin/base_site.html
:
{% extends "admin/base.html" %}
{% load static %}
{% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block branding %}
<h1 id="site-name">
<a href="{% url 'admin:index' %}">
<img src="{% static 'pony.png' %}" height="40px" />
{{ site_header }}
</a>
</h1>
{% endblock %}
{% block nav-global %}{% endblock %}
Не знаю как у вас, а у меня теперь рядом с заголовком красуется поняшка.
Аналогичным методом можно переопределить и другие шаблоны. Оригиналы можно найти в пакете django,
лежат они в директории django/contrib/admin/templates/admin
. Либо же, если просто хотите поменять
что-то в цветовой схеме — можете переопределить файл /static/admin/css/base.css
.