Mar, 2022 Update:
You cannot use "prepopulated_fields" with "readonly_fields" or "exclude" setting the same field "slug" as shown below because there is a confliction between them, then you will get error.
"prepopulated_fields" with "readonly_fields" setting the same field "slug":
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ['title']} # "slug" field is set
readonly_fields = ['slug'] # "slug" field is set
"prepopulated_fields" with "exclude" setting the same field "slug":
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ['title']} # "slug" field is set
exclude = ['slug'] # "slug" field is set
Moreover, with "prepopulated_fields" set "slug" field, you cannot use "fields" not set "slug" field as shown below because there is also a confliction between them, then you will get error:
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ['title']} # "slug" field is set
fields = ['title'] # "slug" field is not set
Moreover again, you cannot use **"prepopulated_fields" set "slug" field in "admin.py" in this case you set "editable=False" to "slug" field in "models.py" because again, there is a confliction between them:
"prepopulated_fields" in "admin.py":
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ['title']} # "slug" field is set
"editable=False" in "models.py"
class Page(models.Model):
title = models.CharField(max_length=255) # "editable=False" is set
slug = models.SlugField(max_length=255, editable=False)
Moreover again and again, "prepopulated_fields" with "get_readonly_fields()" and "get_prepopulated_fields()" doesn't work:
from django.contrib import admin
from .models import (
Page,
)
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ['title']}
def get_readonly_fields(self, request, obj=None):
readonly_fields = super().get_readonly_fields(request, obj)
if request.user.is_superuser:
return readonly_fields
return list(readonly_fields) + ['slug']
def get_prepopulated_fields(self, request, obj=None):
prepopulated_fields = super().get_prepopulated_fields(request, obj)
if request.user.is_superuser:
return prepopulated_fields
else:
return {}
So finally, I found it harmful to use "prepopulated_fields" so I recommend not to use "prepopulated_fields". So for now, my best solution is generating "slug" automatically without using "prepopulated_fields" as shown below.
First, add "editable=False" to "slug" field in "Page" model in "models.py":
# "editable=False" is set
slug = models.SlugField(max_length=255, editable=False)
Then, add this code below to "Page" model in "models.py":
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Page, self).save(*args, **kwargs)
This is the full code of "Page" model in "models.py":
from django.db import models
from django.utils.text import slugify
class Page(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, editable=False)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Page, self).save(*args, **kwargs)
Then, as you can see below, "slug" field doesn't appear(is hidden) in Add page in the form:

But no worries!! When you fill "Title" field and click on "Save", "slug" is automatically generated:

You can check if "slug" is automatically generated or not by adding this code to "admin.py":
from django.contrib import admin
from .models import Page
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
list_display = ['title', 'slug']
Now, you can see the slug "this-is-my-page-1" is generated automatically:

In addition, if you remove "editable=False" from "slug" field in "Page" model in "models.py":
# "editable=False" is removed
slug = models.SlugField(max_length=255)
"slug" field appears:

Then, when you fill "Title" field and click on "Save", you are asked to fill "slug" field which means "slug" is not automatically generated:

So to automatically generate "slug", you need to hide "slug" field from the form by adding "editable=False" to "slug" field:
# Important to hide "slug" field
slug = models.SlugField(max_length=255, editable=False)
In addition, you can hide "slug" field from the form by adding "exclude = ['slug']" or "fields = ['title']" to "PageAdmin" class in "admin.py".
"exclude = ['slug']":
from django.contrib import admin
from .models import Page
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
list_display = ['title', 'slug']
exclude = ['slug'] # Here
"fields = ['title']":
from django.contrib import admin
from .models import Page
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
list_display = ['title', 'slug']
fields = ['title'] # Here
And in this case you add "exclude = ['slug']" or "fields = ['title']" to "PageAdmin" class in "admin.py", you can remove "editable=False" from "slug" field:
# "editable=False" is removed
slug = models.SlugField(max_length=255)
Finally, you can hide "slug" field from the form by adding "exclude = ['slug']" or "fields = ['title']" to "PageAdmin" class in "admin.py" without adding "editable=False" to "slug" field:

Then, you fill "Title" field and click on "Save":

Then, the slug "this-is-my-page-2" is automatically generated:
