Предварительно заполняемые поля и наборы полей в админке Django

Я использую Django 3.2

У меня следующая модель:

class Foo(models.Model):
    title = models.CharField()
    slug = models.SlugField()
    # remaining fields listed below

В Admin manager у меня есть модель:

class FooAdmin(admin.ModelAdmin):
    fieldsets = [
        ('General', {
            'fields': [
                        'title',
                        'description',
                        'information',
                        'cover_photo',
                        'gallery', 
            ]

        }),
        ('Details', {
            'fields' : [
            'is_cancelled',
            'start',
            'finish',
            'video_url',
            ]

        }),
    ]
    
    prepopulated_fields = {'slug': ('title',), }


admin.site.register(Foo, FooAdmin)

Когда я пытаюсь создать новый Foo в менеджере администраторов, я получаю ошибку:

KeyError: "Ключ 'slug' не найден в 'FooForm'. Возможные варианты: cover_photo, description, finish, gallery, information, is_cancelled, start, title, video_url."

.

Когда я добавляю поле slug в список наборов полей, это работает, но затем поле slug отображается на форме (чего я не хочу).

Я знаю, что могу решить эту проблему, переопределив Foo.save(), но я хочу, чтобы менеджер администраторов обрабатывал это за меня.

Итак, как мне использовать наборы полей с предварительно заполненными полями, которые не должны появляться на форме?

Вы можете переопределить форму администратора:

from django.utils.text import slugify  


class FooAdmin(admin.ModelAdmin):
    fieldsets = [
        ('General', {
            'fields': [
                        'title',
                        'description',
                        'information',
                        'cover_photo',
                        'gallery', 
            ]
        }),
        ('Details', {
            'fields' : [
            'is_cancelled',
            'start',
            'finish',
            'video_url',
            ]
        }),
    ]
   
    def get_form(self, request, obj=None, change=False, **kwargs):
       OriginalForm = super().get_form(request, obj, change, **kwargs)

       class Form(OriginalForm):
          def save(self, commit=True):
             instance = super().save(commit=False)
             instance.slug = slugify(instance.title)  # Here
             if commit:
                instance.save()
             return instance

       return Form

Как вы сказали, лучшим (и самым простым) решением будет переопределение метода save модели.

Вы не можете исключить slug из полей администратора, поскольку вы включаете его в prepopulated_fields. Однако вы можете переопределить метод сохранения вашей модели, как показано ниже, чтобы генерировать уникальный slug. Вы можете создать общую BaseModel с этим методом сохранения и расширять свои модели полями slug каждый раз:

from django.utils.text import slugify


def generate_unique_slug(klass, field):
    """
    return unique slug if origin slug is exist.
    eg: `foo-bar` => `foo-bar-1`

    :param `klass` is Class model.
    :param `field` is specific field for title.
    """
    origin_slug = slugify(field)
    unique_slug = origin_slug
    numb = 1
    while klass.objects.filter(slug=unique_slug).exists():
        unique_slug = "%s-%d" % (origin_slug, numb)
        numb += 1
    return unique_slug


class BaseModel(models.Model):
    class Meta:
        abstact = True

    def save(self, *args, **kwargs):
        if hasattr(self, "slug") and hasattr(self, "title"):
            if self.slug:
                if slugify(self.title) != self.slug:
                    self.slug = generate_unique_slug(self.__class__, self.title)
            else:  # create
                self.slug = generate_unique_slug(self.__class__, self.title)

        super().save(*args, **kwargs)


class Foo(BaseModel):
    title = models.CharField()
    slug = models.SlugField()
    # remaining fields listed below


class AnotherModelWithSlug(BaseModel):
    title = models.CharField()
    slug = models.SlugField()
Вернуться на верх