Home > OS >  Django admin prepopulated fields and fieldsets
Django admin prepopulated fields and fieldsets

Time:04-08

I am using Django 3.2

I have the following model:

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

In Admin manager, I have the model:

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)

When I attempt to create a new Foo in the admin manager, I get the error:

KeyError: "Key 'slug' not found in 'FooForm'. Choices are: cover_photo, description, finish, gallery, information, is_cancelled, start, title, video_url."

When I add the slug field to the list of fieldsets, it works, but then the slug field is shown on the form (which I don't want).

I know that I can solve this by overriding Foo.save(), but I want the admin manager to handle this for me.

So, how do I use fieldsets with prepopulated fields that are not supposed to appear on the form?

CodePudding user response:

You can override the form of the admin:

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

As you said the best (and simplest) solution would be to override the save method of the model.

CodePudding user response:

You cannot exclude slug from admin fields since you are including it in prepopulated_fields. However, you can override your model's save method like below to generate a unique slug. You can make general BaseModel with this save method and extend your models with slug fields everytime:

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()
  • Related