Спрощуйте свій проєкт на Django за допомогою власних адміністративних дій.

текст перекладу
Одним із багатьох способів, яким Django полегшує адміністрування наших проєктів, є надання порталу Django Admin. Кожен користувач Django хоча б раз стикався з ним — це візуальна точка входу в базу даних. У більшості проєктів Django, над якими я працював, ми використовували сайт адміністратора Django як місце, де адміністратори можуть виконувати дії, що потребують підвищених привілеїв, і мати доступ до більшої кількості полів, ніж у звичайному інтерфейсі.

Багато хто, хто знайомий із сайтом адміністратора Django, впізнає випадаюче меню вгорі кожної сторінки моделі з однією дією: «видалити вибрані елементи». Чи знаєте ви, що дуже легко налаштувати це випадаюче меню і заповнити його додатковими користувацькими діями, які будуть виконуватись над вибраними елементами?

У цій статті я покажу, як створити нові користувацькі дії, щоб додати їх до цього випадаючого меню.

Як створити користувацьку дію адміністратора

Для цієї статті я буду використовувати модель нижче як наш приклад (яку ми також використовували в попередній статті), яка зберігає інформацію про випуски програмного забезпечення.

# models.py  
from django.db import models  

class Release(models.Model):  
 name = models.CharField(max_length=128)  
 release_date = models.DateField(auto_now_add=True)  
 version = models.PositiveIntegerField()  
 enabled = models.BooleanField(default=True)

Маючи визначену модель, давайте створимо дуже простий адміністративний клас, який допоможе нам управляти нею, нижче:

# admin.py  
from django.contrib import admin  
from .models import Release  

@admin.register(Release)  
class ReleaseAdmin(admin.ModelAdmin):  
 list_display = ('name', 'enabled', 'release_date', 'version')  
 list_filter = ('enabled',)  
 search_fields = ('name', 'version')  
 ordering = ('release_date',)

З нашим простим налаштуванням моделі та адміністратора, ми можемо тепер управляти нашими випусками через сайт адміністратора Django:

pic

Сайт адміністратора Django для моделі Release

Тепер, коли ми налаштували сайт адміністратора Django з нашою моделлю, ми хочемо розширити його функціональність, щоб адміністратор міг увімкнути та вимкнути ці випуски оптом. Ми досягнемо цього, використовуючи користувацьку дію адміністратора.

Як і з більшістю речей у Django, налаштування користувацьких дій надзвичайно просте. Перший крок — створити новий метод класу всередині нашого існуючого адміністративного класу, який приймає об'єкт request та queryset. Об'єкт request представляє HTTP-запит користувача, подібно до того, як ми звикли бачити в усіх наших CBV (Class-Based Views). Параметр queryset представляє всі записи, які користувач вибрав і на яких потрібно виконати користувацьку дію.

Створимо наш перший метод enable нижче. Він увімкне всі вибрані випуски одразу:

def enable(self, request, queryset):  
 queryset.update(enabled=True)

Метод досить просто використовує існуючий queryset і застосовує оновлення до всіх записів у ньому, щоб встановити поле enabled на значення True.

Ми також хочемо надати адміністратору зворотний зв'язок про операцію:

def enable(self, request, queryset):  
 queryset.update(enabled=True)  
 self.message_user(request, f'{queryset.count()} releases enabled.')

Ця зміна передає об'єкт request, який був переданий, і використовує його для зв'язку з системою повідомлень Django — яка потім виводить повідомлення про успіх назад користувачу.

Наступний крок — найважливіший: нам потрібно повідомити наш існуючий адміністративний клас про нову користувацьку дію. Для цього ми додаємо ім'я нашого методу до поля списку actions:

class ReleaseAdmin(admin.ModelAdmin):  
 actions = ['enable']

Нарешті, ми декоруємо метод за допомогою декоратора @admin.action, щоб встановити опис для дії (текст, який буде відображатись у випадаючому меню дій) і визначити, які класи дозволів можуть виконувати цю дію.
текст перекладу
Ось як тепер виглядає наш повний адміністративний клас:

from django.contrib import admin  
from .models import Release  

@admin.register(Release)  
class ReleaseAdmin(admin.ModelAdmin):  
 list_display = ('name', 'enabled', 'release_date', 'version')  
 list_filter = ('enabled',)  
 search_fields = ('name', 'version')  
 ordering = ('release_date',)  
 actions = ['enable']  

 @admin.action(description='Enable selected releases', permissions=['change'])  
 def enable(self, request, queryset):  
 queryset.update(enabled=True)  
 self.message_user(request, f'{queryset.count()} releases enabled.')

Якщо ми хочемо додати обробку помилок, ми можемо відправити будь-які попередження чи помилки назад користувачу за допомогою тієї ж системи повідомлень, наприклад:

self.message_user(request, f'{queryset.count()} releases enabled.')  
self.message_user(request, 'This is a sample error', level='ERROR')  
self.message_user(request, 'This is a sample warning', level='WARNING')

Ці повідомлення дадуть наступний результат на сторінці:

pic

Приклад повідомлень на панелі адміністратора

Звісно, можна також додати користувацьку дію disable, яка виконуватиме те саме, але в зворотному напрямку:

# admin.py  
from django.contrib import admin  
from .models import Release  

@admin.register(Release)  
class ReleaseAdmin(admin.ModelAdmin):  
 list_display = ('name', 'enabled', 'release_date', 'version')  
 list_filter = ('enabled',)  
 search_fields = ('name', 'version')  
 ordering = ('release_date',)  
 actions = ['enable', 'disable']  


 @admin.action(description='Enable selected releases', permissions=['change'])  
 def enable(self, request, queryset):  
 queryset.update(enabled=True)  
 self.message_user(request, f'{queryset.count()} releases enabled.')  

 @admin.action(description='Disable selected releases', permissions=['change'])  
 def disable(self, request, queryset):  
 queryset.update(enabled=False)  
 self.message_user(request, f'{queryset.count()} releases disabled.')

З цими змінами, тепер адміністратори бачать такі доступні дії на сайті адміністратора Django:

pic

Нові дії для “enable” та “disable” з'являються у випадаючому меню дій.

Варто зазначити, що в разі, якщо користувацька дія має складнішу логіку, слід розглянути можливість інкапсуляції її в метод моделі чи менеджер моделі і викликати його безпосередньо з користувацької дії. Це дозволить дотримуватись принципу DRY (Don't Repeat Yourself), якщо ця сама логіка повинна бути використана в іншому місці проєкту.

Юніт-тестування користувацьких дій адміністратора

З реалізованою користувацькою логікою адміністратора, більшість розробників природно запитають, як юніт-тестувати цю нову функціональність. Ми знову можемо використати вбудовану архітектуру юніт-тестів Django для виконання всіх необхідних кроків.

Як і з будь-яким тестом REST, ми ініціалізуємо клієнта та виконуємо вхід, переконавшись, що використовуємо суперкористувача, який може отримати доступ до сайту адміністратора. Наш метод налаштування виглядатиме так:

from django.test import TestCase, Client  

class CustomDjangoAdmin(TestCase):  
 def setUp(self):  
 # Створюємо адміністратора і входимо під цим користувачем:  
 self.client = Client()  
 self.admin_user = User.objects.create_superuser(username='admin', password='admin', email='[email protected]')  
 self.client.login(username='admin', password='admin')

Створивши адміністратора та увійшовши під ним, у нашому тестовому випадку ми можемо зробити HTTP-запит безпосередньо до користувацької дії, передавши PK (primary key) записів, з якими ми хочемо працювати.
текст перекладу
Наприклад:

from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME  

def test_enable(self):  
 data = {  
 'action': 'enable',  
 ACTION_CHECKBOX_NAME: [PKS_GO_HERE],  
 }  
 response = self.client.post(reverse('admin:app_release_changelist'), data)  
 self.assertEqual(response.status_code, 302)

Щоб забезпечити сумісність у майбутньому, ми імпортуємо ім'я поля для чекбокса як ключ, а значенням є список PK записів.

Далі ми виконуємо зворотний пошук URL-адреси для сайту адміністратора для відповідної моделі. Формат цього URL є app_model_changelist. Очікуваний код відповіді — HTTP 302 при успіху, оскільки Django перенаправляє клієнта на сторінку зі списком адміністратора.

Тепер ми можемо створити кілька тестових об'єктів Release і об'єднати все це в єдиний тест, наприклад:

# tests/test_custom_admin.py  
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME  
from django.contrib.auth.models import User  
from django.test import TestCase, Client  
from django.urls import reverse  
from .models import Release  


class CustomDjangoAdmin(TestCase):  
 def setUp(self):  
 # Створюємо адміністратора і входимо під цим користувачем:  
 self.client = Client()  
 self.admin_user = User.objects.create_superuser(username='admin', password='admin', email='[email protected]')  
 self.client.login(username='admin', password='admin')  
 # Створюємо кілька релізів для тестування:  
 self.release1 = Release.objects.create(name='Release 1', version=1_002_123, enabled=False)  
 self.release2 = Release.objects.create(name='Release 2', version=2_003_000, enabled=False)  
 self.release3 = Release.objects.create(name='Release 3', version=3_005_007, enabled=False)  

 def test_enable(self):  
 # Викликаємо користувацьку команду адміністратора:  
 data = {  
 'action': 'enable',  
 ACTION_CHECKBOX_NAME: [self.release1.pk, self.release2.pk],  
 }  
 response = self.client.post(reverse('admin:app_release_changelist'), data)  
 self.assertEqual(response.status_code, 302)  
 # Перевіряємо, що прапорець enabled був правильно змінений:  
 self.release1.refresh_from_db()  
 self.release2.refresh_from_db()  
 self.release3.refresh_from_db()  
 self.assertTrue(self.release1.enabled)  
 self.assertTrue(self.release2.enabled)  
 self.assertFalse(self.release3.enabled)  

 def test_disable(self):  
 # Встановлюємо всі релізи як включені:  
 Release.objects.all().update(enabled=True)  
 # Викликаємо користувацьку команду адміністратора:  
 data = {  
 'action': 'disable',  
 ACTION_CHECKBOX_NAME: [self.release1.pk, self.release2.pk],  
 }  
 response = self.client.post(reverse('admin:app_release_changelist'), data)  
 self.assertEqual(response.status_code, 302)  
 # Перевіряємо, що прапорець enabled був правильно змінений:  
 self.release1.refresh_from_db()  
 self.release2.refresh_from_db()  
 self.release3.refresh_from_db()  
 self.assertFalse(self.release1.enabled)  
 self.assertFalse(self.release2.enabled)  
 self.assertTrue(self.release3.enabled)

Ось і все! Це все, що потрібно для створення користувацьких дій на сайті адміністратора Django! Завдяки вбудованій системі Django, ми додали ще одну корисну функцію до нашого проєкту.

Щоб швидко підсумувати, три прості кроки, які ми виконали:

  1. Створили новий метод у класі ModelAdmin, який приймає об'єкти request і queryset для вибраних записів. Цей метод виконує логіку користувацької дії.
  2. Прикрасили новий метод за допомогою декоратора @admin.action, щоб задати опис і контролювати дозволи.
    3.
    текст перекладу
    Додайте нову назву методу до поля actions як рядок.

Для більш детального вивчення цієї теми рекомендую ознайомитись з ресурсами, наведеними в наступному розділі.

Рекомендована література

Перекладено з: Simplify your Django project with custom admin actions

Leave a Reply

Your email address will not be published. Required fields are marked *