Reputation: 359
I want to make the slug field as read_only depending on the other field value like "lock_slug".
Means There will be Two conditions.
1) When value of "lock_slug" is false then the slug field directly prepopulated from the field "title".
prepopulated_fields = {"slug": ("title",),}
2) When value of "lock_slug" is true then the slug field make as readonly.
def get_readonly_fields(self, request, obj = None):
if obj and obj.lock_slug == True:
return ('slug',) + self.readonly_fields
return self.readonly_fields
These two works fine independently, but problematic when used both.
Means when I try to add get_readonly_fields() on edit then it gives error because of prepopulated_fields.These two crashes with each other.
There will be any solution for working both on admin side.
I also refer below links
Making a field readonly in Django Admin, based on another field's value
django admin make a field read-only when modifying obj but required when adding new obj
But not working these two at the same time.
Thanks,
Meenakshi
Upvotes: 5
Views: 5438
Reputation: 1
I have a simple solution using HTML5's readonly
input element attribute. Here's an example:
models.py:
from django.db import models
class Example(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField()
def __str__(self):
return self.name
forms.py:
from django import forms
from .models import Example
class ExampleModelForm(forms.ModelForm):
class Meta:
model = Example
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["slug"].widget.attrs.update({"readonly": "1"})
admin.py:
from django.contrib import admin
from .forms import ExampleModelForm
from .models import Example
@admin.register(Example)
class ExampleAdmin(admin.ModelAdmin):
form = ExampleModelForm
prepopulated_fields = {"slug": ("name",)}
Upvotes: 0
Reputation: 1
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:
Upvotes: 0
Reputation: 4287
As @Rexford pointed out, the most voted answer doesn't work for recent django versions, since you can't make readonly a prepopulated field.
By the way, you can still get what you want, just override also the get_prepopulated_fields
method using the same logic, i.e:
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 {}
Upvotes: 2
Reputation: 3133
Here is another way:
class PostAdmin(admin.ModelAdmin):
list_display = (
'title',
'slug',
)
prepopulated_fields = {'slug': ('title',)}
def get_readonly_fields(self, request, obj=None):
if obj:
self.prepopulated_fields = {}
return self.readonly_fields + ('slug',)
return self.readonly_fields
Upvotes: 8
Reputation: 359
We can not make the prepoluted field as read_only. So I create the new field which is not prepopulated and perform the action on that field and my problem get solved.
Upvotes: 0