I've only been using Django for a couple of weeks now, so I may be approaching this all kinds of wrong, but:
I have a base ModelForm that I put some boilerplate stuff in to keep things as DRY as possible, and all of my actual ModelForms just subclass that base form. This is working great for error_css_class = 'error'
and required_css_class = 'required'
but formfield_callback = add_css_classes
isn't working like I would expect it to.
# snippet I found
def add_css_classes(f, **kwargs):
field = f.formfield(**kwargs)
if field and 'class' not in field.widget.attrs:
field.widget.attrs['class'] = '%s' % field.__class__.__name__.lower()
return field
class BaseForm(forms.ModelForm):
formfield_callback = add_css_classes # not working
error_css_class = 'error'
required_css_class = 'required'
class Meta:
pass
class TimeLogForm(BaseForm):
# I want the next line to be in the parent class
# formfield_callback = add_css_classes
class Meta(BaseForm.Meta):
model = TimeLog
The end goal is to slap some jquery datetime pickers on forms with a class of datefield/timefield/datetimefield. I want all of the date time fields within the app to use the same widget, so I opted to do it this way than explicitly doing it for each field in every model. Adding an extra line to each form class isn't that big of a deal, but it just bugged me that I couldn't figure it out. Digging around in the django source showed this is probably doing something I'm not understanding:
class ModelFormMetaclass(type):
def __new__(cls, name, bases, attrs):
formfield_callback = attrs.pop('formfield_callback', None)
But I don't know how __init__
and __new__
are all intermangled. In BaseForm I tried overriding __init__
and setting formfield_callback before and after the call to super, but I'm guessing it needs to be somewhere in args or kwargs.
__new__ is called before object construction. Actually this is a factory method that returns the instance of a newly constructed object.
So there there are 3 key lines in ModelFormMetaclass:
formfield_callback = attrs.pop('formfield_callback', None) #1
fields = fields_for_model(opts.model, opts.fields,
opts.exclude, opts.widgets, formfield_callback) #2
new_class.base_fields = fields #3
In the class we attach base_fields to our form.
Now let's look to ModelForm class:
class ModelForm(BaseModelForm):
__metaclass__ = ModelFormMetaclass
This means that ModelFormMetaclass.__new__(...) will be called when we create a ModelForm instance to change the structure of the future instance. And attrs of __new__ (def __new__(cls, name, bases, attrs)) in ModelFormMetaclass is a dict of all attributes of ModelForm class.
So decision is to create new InheritedFormMetaclass for our case (inheriting it from ModelFormMetaclass). Don't forget to call new of the parent in InheritedFormMetaclass. Then create our BaseForm class and say:
__metaclass__ = InheritedFormMetaclass
In __new__(...) implementation of InheritedFormMetaclass we could do all we want.
If my answer is not detailed enough please let me know with help of comments.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With