views:

81

answers:

4

I'd like to set the class attribute for the TextInput Widget to one value for all the fields in my form that use it without having to list them all in Meta: widgets = {.... Is this possible?

Thanks.

+1  A: 

Create a new widget with your CSS class:

class PrettyWidget(forms.TextInput):
    class Media:
        css = {
            'all': ('pretty.css',)
        }

In your form use your new widget in all your fields:

class ContactForm(forms.Form):
    subject = TextField(widget=PrettyWidget)
Trunet
This feels like the more Djangonic way to accomplish the task, but it doesn't quite seem like what Kevin is after to me...
codekoala
+1  A: 

One thing I've done in the past is create a lambda function that returns the fields I use often:

from django import forms

fancy = lambda: forms.TextInput(attrs={'class': 'derp'})

class MyForm(forms.Form):
    attr1 = fancy()
    attr2 = fancy()
    attr3 = fancy()

I don't remember why exactly I opted to use a function to handle my situation, but I suspect that it had something to do with creating separate instances of the forms.TextInput object for each form field....

codekoala
This is what I do now. It's necessary to create separate instances of forms.Select.
Kevin
+1  A: 

You can use following:

def set_attrs_for_fields(fields, default_attrs):
    for field_name in fields:
        field = fields[field_name]

        try:
            default_widget_attr = default_attrs[field.widget.__class__]
            field.widget.attrs = default_widget_attr
        except KeyError:
            pass


class DefaultAttrForm(forms.Form):

    def __init__(self, *args, **kwargs):

        super(DefaultAttrForm, self).__init__(*args, **kwargs)

        set_attrs_for_fields(self.fields, self.get_default_attrs())


    def get_default_attrs(self):
        '''
        Child class should overwrite this method and return dict with example format

        {forms.TextInput: {'class': 'my_value'}}

        where keys, are widget classes for which default attrs will be set. 
        '''
        raise NotImplementedError()


class DefaultAttrModelForm(ModelForm):

    def __init__(self, *args, **kwargs):

        super(DefaultAttrModelForm, self).__init__(*args, **kwargs)

        set_attrs_for_fields(self.fields, self.get_default_attrs())


    def get_default_attrs(self):
        '''
        Child class should overwrite this method and return dict with example format

        {forms.TextInput: {'class': 'my_value'}}

        where keys, are widget classes for which default attrs will be set. 
        '''
        raise NotImplementedError() 

Now, every time you want to use default attrs for some widget, you just need to create class that inherits from DefaultAttrForm or DefaultAttrModelForm and overwrite method get_default_attrs. Example for normal Django Form:

class MyForm(DefaultAttrForm):
    my_field1 = forms.CharField(widget=forms.TextInput())
    my_field2 = forms.CharField(widget=forms.PasswordInput(attrs={'my_non_default': 'Non default'}))

    def get_default_attrs(self):
        # all fields with widget TextInput will have 
        # default attrs set to {'class': 'my_value'}

        return {forms.TextInput: {'class': 'my_value'},
               }


In [1]: form = MyForm()

In [2]: form.fields['my_field1'].widget.attrs
Out[2]: {'class': 'my_value'}

In [3]: form.fields['my_field2'].widget.attrs
Out[3]: {'my_non_default': 'Non default'}

For Model form use following:

class MyModelForm(DefaultAttrModelForm):

    class Meta:
        model = my_model

    def get_default_attrs(self):
        # all fields with widget TextInput will have 
        # default attrs set to {'class': 'my_value'}

        return {forms.TextInput: {'class': 'my_value'},
               }


In [1]: form = MyModelForm()

In [2]: form.fields['my_field1'].widget.attrs
Out[2]: {'class': 'my_value'}

In [3]: form.fields['my_field2'].widget.attrs
Out[3]: {'my_non_default': 'Non default'}
Dominik Szopa
+1  A: 
from django import forms

class MyForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        for name, field in self.fields.items():
            if field.widget.__class__ == forms.widgets.TextInput:
                if field.widget.attrs.has_key('class'):
                    field.widget.attrs['class'] += ' my-class'
                else:
                    field.widget.attrs.update({'class':'my-class'})
    class Meta:
        model = MyModel
Paulo Scardine
can you override init with a decorator? also, there should be an if statement before the attrs.update() becuase I want to set a default rather than override what may already be there.
Kevin
I think decorators are intended to be used with callables, overriding the constructor should be done through the normal inheritance mechanism. In django it is not uncommon to have some functions returning classes, these functions are usually called 'factories'. Updated with "if" condition (untested).
Paulo Scardine