Opened 12 years ago

Closed 11 years ago

#18168 closed Bug (fixed)

formfield_for_choice_field() doesn't validate choices updated in method

Reported by: Robert <weglarek.robert+djangotrac@…> Owned by: nobody
Component: contrib.admin Version: 1.3
Severity: Normal Keywords: admin, inlines
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Having field declared in model:

element = models.CharField(max_length=500, verbose_name=_('admin element'), choices=(('foo', 'bar'),))

and that function in admin:

class AdminModuleElementInline(admin.StackedInline):
    model = AdminModuleElement
    def formfield_for_choice_field(self, db_field, request=None, **kwargs):
        if db_field.name == 'element':
            kwargs['choices'] = (
                ('accepted', 'Accepted'),
                ('denied', 'Denied'),
            )
        return db_field.formfield(**kwargs)

when I try to save it I get an error:
Value u'accepted' is not a valid choice.

So it updates the list, but validator checks the choices declared in model.

Attachments (1)

Screen Shot 2012-07-15 at 21.59.02.png (17.1 KB ) - added by Aymeric Augustin 12 years ago.

Download all attachments as: .zip

Change History (9)

comment:1 by Robert <weglarek.robert+djangotrac@…>, 12 years ago

Keywords: admin inlines added
Summary: formfield_for_choice_field() doesn't update choices declared in modelformfield_for_choice_field() doesn't validate choices declared in model

comment:2 by Robert <weglarek.robert+djangotrac@…>, 12 years ago

Summary: formfield_for_choice_field() doesn't validate choices declared in modelformfield_for_choice_field() doesn't validate choices updated in method

comment:3 by anonymous, 12 years ago

any workaround for this issue ?

comment:4 by Jannis Leidel, 12 years ago

Could you please provide a full traceback?

comment:5 by Aymeric Augustin, 12 years ago

Triage Stage: UnreviewedAccepted

There is no traceback -- the validation error is displayed in the admin.

The reporter's example uses inlines. I could reproduce the issue with the following code. Screenshot attached.

# models.py

from django.db import models1

class Variable(models.Model):
    NAME_CHOICES = (
        ('foo', 'foo'),
        ('bar', 'bar'),
        ('baz', 'baz'),
    )
    name  = models.CharField(max_length=3, choices=NAME_CHOICES)

# admin.py

from django.contrib import admin
from .models import Variable

class VariableAdmin(admin.ModelAdmin):

    def formfield_for_choice_field(self, db_field, request, **kwargs):
        if db_field.name == "name":
            kwargs['choices'] = (
                ('quux', 'quux'),
                ('quuux', 'quuux'),
            )
        return super(VariableAdmin, self).formfield_for_choice_field(db_field, request, **kwargs)

admin.site.register(Variable, VariableAdmin)

I don't think it makes sense to allow in the form values that aren't allowed by the model. If I understand correctly this method can only be used to restrict changes, not to extend them. If that's true, it should be mentioned in the documentation of formfield_for_choice_field.

by Aymeric Augustin, 12 years ago

comment:6 by Joeri Bekker, 12 years ago

This would limit the flexibility of the formfield_for_choice_field function quite a bit, which I find unfortunate.

Adding arbitrary values to the choices is not wrong, since they are added to the "choices" and therefore not incorrect as a choice. It would be different if you change choices through some Javascript and let Django accept it.

I don't think it makes sense to allow in the form values that aren't allowed by the model

The choice fields (often on top of database CharField or IntegerField) do no enforce any referential integrity anyway. One can simply remove one of the choices in the code and the database nor the Django admin (for reading at least) would complain, which is the correct behavior.

As a workaround, you overriding YourForm.__init___ and use your custom form in the ModelAdmin which adjusts your choices (depends a bit on what you need to base your choices on).

Version 0, edited 12 years ago by Joeri Bekker (next)

comment:7 by Preston Holmes, 11 years ago

hitting an unrelated issue in some of this same code, and figured I'd address this. The best fix for now is to clarify that model choices and their validation always trump form choices.

There are a number of places where this can cause a trip up, but I'm going to go with a fix for this as a doc note.

comment:8 by Preston Holmes <preston@…>, 11 years ago

Resolution: fixed
Status: newclosed

In 10f8a2100279621ca0e0fa47d99ee744741a05e7:

Fixed #18168 -- clarified precedence of validation

any choices set by formfield_for_choice_field are still subject
to model validation of the model field's choices attribute

Note: See TracTickets for help on using tickets.
Back to Top