Opened 5 years ago

Closed 5 years ago

#14095 closed (invalid)

Objects not saved when readonly_fields is set for inline admin

Reported by: simon@… Owned by: nobody
Component: contrib.admin Version: 1.2
Severity: Keywords: TabularInline, inline, readonly_fields, sprintdec2010
Cc: simon@… Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

Code

models.py:

class BalanceCode(models.Model):
    created_at = models.DateField(auto_now_add=True)
    code = models.CharField(max_length=BALANCE_CODE_LENGTH, unique=True, 
            default=generate_balance_code, help_text=code_help)
    value = models.PositiveIntegerField(default=BALANCE_CODE_DEFAULT_VALUE)
    refill_series = models.ForeignKey(RefillSeries)
    used_by = models.ForeignKey(Student, null=True, blank=True)
    used_at = models.DateField(blank=True, null=True)
class RefillSeries(models.Model):
    issued = models.DateField(default=default_issued)
    least_valid_until = models.DateField(default=default_least_valid_until)

admin.py:

class BalanceCodeInline(admin.TabularInline):
    readonly_fields = ('code', )
    model = BalanceCode
    extra = SERIES_CODE_COUNT
    max_num = SERIES_CODE_COUNT
class BalanceCodeAdmin(admin.ModelAdmin):
    readonly_fields = ('code', )
class RefillSeriesAdmin(admin.ModelAdmin):
    inlines = (BalanceCodeInline,)

Problem Description

Expected

When a new RefillSeries is created, SERIES_CODE_COUNT BalanceCodes should
be created as well.

Actual

No BalanceCodes are created. When readonly_fields is unset,
SERIES_CODE_COUNT objects are created, as expected.

Creating new BalanceCodes directly (not inlined in the RefillSeries admin
interface) works as expected when readonly_fields is set.

Change History (2)

comment:1 Changed 5 years ago by simon@…

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

Addendum: The inlined BalanceCodes show up in the admin interface, but they are not saved to database.

comment:2 Changed 5 years ago by julien

  • Keywords sprintdec2010 added
  • Resolution set to invalid
  • Status changed from new to closed

Thank you for the report, Simon. It is true that Django is behaving oddly here. However, this is caused by the way inlines are currently designed to work. If you make the code field readonly, then there will not be any input added to the form for it, which means that Django has no way of detecting that anything has changed in the form, and therefore cannot know that an object should be created.

I've discussed this at length with russelm and DrMeers (both core committers), and the answer is that this cannot be fixed. To achieve what you want you'd probably have to not make the code field readonly, and then use a bit of javascript to prevent the user from modifying it.

For that reason I'm closing this ticket as invalid. I also invite you to look into ticket #14832, which has in fact emerged after looking at this one and which discusses a related issue.

Finally, for the record, here's the code I've used to study this issue (some elements like generate_balance_code or SERIES_CODE_COUNT were missing in your description):

Models:

import datetime, hashlib

from django.db import models
from django.contrib.auth.models import User

def generate_balance_code():
    return hashlib.md5(str(datetime.datetime.now())).hexdigest()


class RefillSeries(models.Model):
    issued = models.DateField(default=datetime.datetime.today)
    least_valid_until = models.DateField(default=datetime.datetime.today() + datetime.timedelta(days=10000))

class BalanceCode(models.Model):
    created_at = models.DateField(auto_now_add=True)
    code = models.CharField(max_length=100, unique=True, default=generate_balance_code,)
    value = models.PositiveIntegerField(default=10)
    refill_series = models.ForeignKey(RefillSeries)
    used_by = models.ForeignKey(User, null=True, blank=True)
    used_at = models.DateField(blank=True, null=True)

Admin:

from django.contrib import admin

from .models import BalanceCode, RefillSeries

class BalanceCodeAdmin(admin.ModelAdmin):
    readonly_fields = ('code', )

class BalanceCodeInline(admin.TabularInline):
    readonly_fields = ('code', )
    model = BalanceCode
    extra = 2
    max_num = 5
    
class RefillSeriesAdmin(admin.ModelAdmin):
    inlines = [BalanceCodeInline]

admin.site.register(BalanceCode, BalanceCodeAdmin)
admin.site.register(RefillSeries, RefillSeriesAdmin)
Note: See TracTickets for help on using tickets.
Back to Top