Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django - how can I access the form field from inside a custom widget

The below class inherits from the Textarea widget and features javascript code that displays how many characters more a user can enter in a textarea.

class TextAreaWithCharCounter(forms.Textarea):

    class Media:
        js = ('js/jquery.charcounter.js',)

    def render(self, name, value, attrs = None):
        id = attrs['id']
        max_length = self.attrs.get('max_length', 200)
        output = super(TextAreaWithCharCounter, self).render(name, value, attrs)
        output += mark_safe(u'''
                        <script type="text/javascript">
                        $("#%s").charCounter(%d, {classname:"charcounter"});
                        </script>'''%(id, max_length))        
        return output

The relevant portion of the form code is as follows:

class MyForm(forms.Form):
    foo = forms.CharField(max_length = 200, widget = TextAreaWithCharCounter(attrs={'max_length':200}))
    ...

You can see that I pass the max_length argument twice, one for the field and one for the widget. A better way may be accessing the form field from inside the widget and get its max_length attribute, so that a max_length argument won't be required by the widget. How can I do that?

like image 921
shanyu Avatar asked Aug 04 '09 09:08

shanyu


2 Answers

Technically, a Widget doesn't have to have a direct relationship back to a Field, so you don't do this.

Looking at the source of CharField, you can see that it has a widget_attrs method which automatically adds the maxlength attribute to TextInput / PasswordInput fields.

I suggest you use a custom Field which overrides this method and adds an attribute for your custom Widget.

Also, I'm not sure that leaving it in attrs is a good idea anyway - the <TextArea> will be rendered with an invalid max_length argument. Perhaps you should be pop()ing it off instead?

like image 137
SmileyChris Avatar answered Oct 19 '22 14:10

SmileyChris


Although not required for solving your problem, access to the form or form field may be indeed useful sometimes. See the full answer at another question, but in short, you can bind the form or field to the widget manually in form __init__:

class MyForm(forms.ModelForm):
    foo = forms.ModelChoiceField(Foo.objects, widget=CustomWidget())

    class Meta:
        model = Bar

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['foo'].widget.form_instance = self
like image 23
mrts Avatar answered Oct 19 '22 16:10

mrts