Issue with updating read-only fields in Django admin: flags not saving
I’m working on a Django admin interface where certain fields (specifically, review status flags such as is_pending_review_section_title_en and is_pending_review_section_title_uk) should be marked as read-only in the admin interface, but still be updated programmatically when content changes.
I’ve implemented the logic to dynamically determine the fields as read-only using the get_readonly_fields method inside an inline admin class (SectionInline). Although the fields correctly display as read-only in the interface, the problem arises when I try to update these fields in the save_model method of the PageAdmin class (or even in forms). The flags appear to be set correctly in the code, but they do not persist in the database after saving.
Here is a summary of what I’ve tried:
1. Marked fields as read-only using get_readonly_fields — This works for the admin UI.
2. Tried updating the fields programmatically inside save_model in the PageAdmin class and verified the values through logging. The flags are set to True, but after saving, the values in the database remain unchanged.
3. Commented out form save logic to check if it’s interfering with saving, but the issue persists.
4. Ensured flags are not present in forms to avoid user interaction.
What could be causing the fields to not save correctly, even though they are updated programmatically in the code? Are there any specific considerations for updating read-only fields in the admin?
class SectionInline(nested_admin.NestedStackedInline):
model = Section
extra = 0
inlines = [ContentInline]
fields = (
"section_title_en",
"section_title_uk",
"is_pending_review_section_title_en",
"is_pending_review_section_title_uk",
)
def get_readonly_fields(self, request, obj=None):
readonly_fields = super().get_readonly_fields(request, obj)
readonly_fields += (
"is_pending_review_section_title_en",
"is_pending_review_section_title_uk",
)
return readonly_fields
class PageAdmin(nested_admin.NestedModelAdmin):
list_display = ("menu_title", "slug", "created_at")
list_filter = ("created_at",)
search_fields = ("menu_title", "slug")
inlines = [SectionInline]
fields = ("menu_title_en", "menu_title_uk", "created_at")
def save_model(self, request, obj, form, change):
try:
page_snapshot = PageSnapshot.objects.get(original_page=obj)
logger.debug(f"Snapshot exists for Page - {obj.menu_title}")
except PageSnapshot.DoesNotExist:
page_snapshot = None
if not page_snapshot:
logger.debug(f"Snapshot DOES NOT exist for {obj.menu_title}")
self.create_snapshot(obj)
for section in obj.sections.all():
section_snapshot = None
if page_snapshot:
try:
section_snapshot = SectionSnapshot.objects.get(
original_section=section, page_snapshot=page_snapshot
)
logger.debug(f"section_snapshot found for {section.section_title}")
except SectionSnapshot.DoesNotExist:
pass
if section_snapshot:
if section.section_title_en != section_snapshot.section_title_en:
section.is_pending_review_section_title_en = True
if section.section_title_uk != section_snapshot.section_title_uk:
section.is_pending_review_section_title_uk = True
else:
if section.section_title_en:
logger.debug(f"Setting section_title_en review flag")
section.is_pending_review_section_title_en = True
if section.section_title_uk:
section.is_pending_review_section_title_uk = True
section.save()
logger.debug(
f"After saving: section.is_pending_review_section_title_en for {section.section_title_en}: {section.is_pending_review_section_title_en}"
)
super().save_model(request, obj, form, change)
def create_snapshot(self, page_obj):
if PageSnapshot.objects.filter(original_page=page_obj).exists():
return
page_snapshot = PageSnapshot.objects.create(
original_page=page_obj,
menu_title_en=page_obj.menu_title_en,
menu_title_uk=page_obj.menu_title_uk,
slug=page_obj.slug,
)
for section in page_obj.sections.all():
section_snapshot = SectionSnapshot.objects.create(
original_section=section,
page_snapshot=page_snapshot,
section_title_en=section.section_title_en,
section_title_uk=section.section_title_uk,
is_published=section.is_published,
)
for content in section.contents.all():
content_snapshot = None
if section_snapshot:
try:
content_snapshot = ContentSnapshot.objects.get(
original_content=content, section_snapshot=section_snapshot
)
except ContentSnapshot.DoesNotExist:
pass
if content_snapshot:
if content.text_en != content_snapshot.text_en:
content.is_pending_review_text_en = True
if content.text_uk != content_snapshot.text_uk:
content.is_pending_review_text_uk = True
if content.char_en != content_snapshot.char_en:
content.is_pending_review_char_en = True
if content.char_uk != content_snapshot.char_uk:
content.is_pending_review_char_uk = True
if content.url != content_snapshot.url:
content.is_pending_review_url = True
if content.image != content_snapshot.image:
content.is_pending_review_image = True
if content.file != content_snapshot.file:
content.is_pending_review_file = True
else:
if content.text_en:
content.is_pending_review_text_en = True
if content.text_uk:
content.is_pending_review_text_uk = True
if content.char_en:
content.is_pending_review_char_en = True
if content.char_uk:
content.is_pending_review_char_uk = True
if content.url:
content.is_pending_review_url = True
if content.image:
content.is_pending_review_image = True
if content.file:
content.is_pending_review_file = True
content.save()
admin.site.register(Page, PageAdmin)