Администрирование Django - Настройка внешнего вида списка и выбора ключей ManyToManyField

У меня есть база данных с 1000+ песнями. У меня есть пользовательская модель "Расписание", которая принимает песни в качестве поля.

models.py

from django.db import models

class Song(models.Model):
    title = models.CharField(max_length=255)
    words = models.TextField()
    slug = models.SlugField()
    date = models.DateTimeField(auto_now_add=True)
    snippet = models.CharField(max_length=50)

    def __str__(self):
        return self.title

class Schedule(models.Model):
    songs = models.ManyToManyField(Song)
    date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return str(self.date)

admin.py

from django.contrib import admin
from .models import Song, Schedule

@admin.register(Song)
class SongModel(admin.ModelAdmin):
    search_fields = ('title',)
    list_display = ('title',)
    list_per_page = 100

@admin.register(Schedule)
class ScheduleModel(admin.ModelAdmin):
    search_fields = ('date',)
    list_display = ('date',)
    list_per_page = 100

Я хочу иметь возможность добавить любую песню, которую я хочу, в расписание, но это трудно сделать через список по умолчанию в Django-Administration, который выглядит так. Мне приходится прокручивать и CTRL+выбирать каждую из них, а затем добавлять их.

Хотелось бы чего-то более практичного, где можно выбирать, искать и т.д.

Какие у меня есть варианты? Я не знаю, с чего начать поиски.

Вариант 1

Это удобно, только если у вас очень мало связанных элементов (несколько песен в расписании). Но это супер просто и будет лучше, чем то, что вы имеете сейчас. (django.contrib.admin поставляется со встроенным select2.)

@admin.register(Schedule)
class ScheduleAdmin(admin.ModelAdmin):
    ...
    autocomplete_fields = ("songs",)

Вариант 2

(upd: черт, забыл сначала, это тоже супер просто и довольно эффективно)

Выглядит неплохо. Полезно. Не особенно удобно. Но лучше, чем нажимать на клавишу ctrl.

@admin.register(Schedule)
class ScheduleAdmin(admin.ModelAdmin):
    ...
    filter_horizontal = ('songs',)

Вариант 3

Если вам нужен удобный пользовательский интерфейс без внедрения пользовательских страниц или действий (которые, к сожалению, являются полным беспорядком), вам следует использовать StackedInline admin.

Хотя это гораздо сложнее.

Сначала вам понадобится сквозная модель. (Я не думаю, что inlines возможен с автоматически генерируемыми моделями "многие-ко-многим".) В основном, это many-to-many между двумя моделями. Что-то вроде этого:

class ScheduleSongNM(models.Model):
    song = models.ForeignKey("Song", null=False)
    schedule = models.ForeignKey("Schedule", null=False)

Укажите вашей модели Schedule использовать вашу пользовательскую сквозную модель:

class Schedule(models.Model):
    songs = models.ManyToManyField(Song, through="ScheduleSongNM")

Теперь создайте встроенного администратора для ScheduleSongNM:

class ScheduleSongInline(admin.StackedInline):
    model = ScheduleSongNM
    fields = ["song"]
    autocomplete_fields = ["song"]  # select2 works here too

Наконец, скажите вашему Schedule администратору, что у него теперь есть инлайн:

@admin.register(Schedule)
class ScheduleAdmin(admin.ModelAdmin):
    ...
    inlines = [ScheduleSongInline]
    ...

Возможно, я что-то упустил (очевидно, я не тестировал это), но я думаю, что вы поняли общую идею. В итоге вы получаете поле внутри вашей админки Schedule, которое выглядит примерно так (плюс автозавершение для названий песен):

screenshot

Вернуться на верх