czwartek, 7 maja 2009

django bug #8620 - ModelForm i exclude

Django svn/1.0.2 zawiera irytujący błąd, który uniemożliwia wyłączanie niektórych pól z formularza, jeśli je redefiniowano za pomocą class properties klasy dziedziczącej:

class RegisterForm(ModelForm):
email = forms.EmailField()
password = forms.CharField(widget=widgets.PasswordInput)
name = forms.CharField()
birthdate = forms.DateField(widget=widgets.DateTimeInput,required=False)
class Meta:
model = models.Person
fields = ('email','password','name','birthdate')

class CustomRegisterForm(RegisterForm):
class Meta(RegisterForm.Meta):
exclude = ['birthdate']
fields = ['email','password','name']

W powyższym przypadku "birthdate" zmieni tylko swoje położenie - pojawi się na końcu formularza.

Tymczasowym rozwiązaniem jest utworzenie własnego modułu forms.py:

from django.forms.models import ModelForm as BaseModelForm

class FixedModelFormMetaclass(ModelFormMetaclass):
"""
meta class for model forms with fixed ticket #8620
"""
def __new__(cls, name, bases, attrs):
formfield_callback = attrs.pop('formfield_callback',
lambda f: f.formfield())
try:
parents = [b for b in bases if issubclass(b, ModelForm)]
except NameError:
# We are defining ModelForm itself.
parents = None
declared_fields = get_declared_fields(bases, attrs, False)
new_class = super(FixedModelFormMetaclass, cls).__new__(cls, name, bases,
attrs)
if not parents:
return new_class

if 'media' not in attrs:
new_class.media = media_property(new_class)
opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))
if opts.model:
# If a model is defined, extract form fields from it.
fields = fields_for_model(opts.model, opts.fields,
opts.exclude, formfield_callback)
# Override default model fields with any custom declared ones
# (plus, include all the other declared fields).
# 8620 fix:
if opts.fields:
[declared_fields.pop(f) for f in declared_fields.keys() if f not in opts.fields]
if opts.exclude:
[declared_fields.pop(f) for f in declared_fields.keys() if f in opts.exclude]
fields.update(declared_fields)
else:
fields = declared_fields
new_class.declared_fields = declared_fields
new_class.base_fields = fields
return new_class

class ModelForm(BaseModelForm):
"""
same as django model form but using FixedModelFormMetaclass
"""
__metaclass__ = FixedModelFormMetaclass

Teraz wystarczy użyć własnej klasy z myproject.forms.ModelForm zamiast django.forms.

0 komentarzy: