How to override admin field widget?

148 views
Skip to first unread message

Mark Phillips

unread,
Apr 11, 2014, 1:56:42 AM4/11/14
to django users
I hope I am using the right terminology....

I have a Decimal field in a model called 'width'. In the admin screen, I would like to display this field as three text boxes like this:

width:  [] []/[]

so I can enter 2 1/8 for the width, and then have the code convert that to 2.125 as a Decimal field and store that Decimal field in the database. 

How would I go about doing this?

Thanks,

Mark

Lucas Klassmann

unread,
Apr 11, 2014, 2:09:42 AM4/11/14
to django...@googlegroups.com
Hi!

I never used this, but i found here and can be useful:


Cheers.


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAEqej2MkiPYfjO4afqyWVr587AL5UczhuzJCLhgxeqjda8%2BcDw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.



--
Lucas Klassmann
Desenvolvedor de Software

Email: lucaskl...@gmail.com
Web site: http://www.lucasklassmann.com

Mark Phillips

unread,
Apr 11, 2014, 2:25:06 PM4/11/14
to django users
I looked at the MultiWidget and tried to implement following a couple of examples, but I have run into a problem. I get this error when I try to open the admin form - type object 'MeasurementWidget' has no attribute 'attrs'

widgets.py
class MeasurementWidget(forms.MultiWidget):
    def __init__(self, attrs=None):
        widgets = (
            forms.textInput(attrs=attrs),
            forms.textInput(attrs=attrs),
            forms.textInput(attrs=attrs), 
            )
        super(MeasurementWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            frac = Fraction(value)
            num = frac.numerator
            denom = frac.denominator
            whole = num/denom
            if whole == 0:
                whole = ''
            numerator = num - (num/denom)*denom
            return [whole, numerator, denom]
        return ['', '', '']

    def format_output(self, rendered_widgets):
        return u''.join(rendered_widgets)
        
    def value_from_datadict(self, data, files, name):
        valuelist = [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
        try:
            if valuelist[0] == '':
                valuelist[0] = 0
            numerator = int(valuelist[0]) * int(valuelist[2]) + int(valuelist[1])
            denominator = int(valuelist[2])
            return Decimal(float(Fraction(numerator, denominator)))
        except ValueError:
            return ""

models.py
class Inventory(models.Model):
    location = models.ForeignKey(Location)
    room = models.ForeignKey(Room, blank=True, null=True)
    condition = models.ForeignKey(Condition)
    status = models.ForeignKey(Status)
    category = models.ForeignKey(Category)
    length = models.DecimalField(max_digits=5, decimal_places=3, default=0)
    width = models.DecimalField(max_digits=5, decimal_places=3, default=0)
    height = models.DecimalField(max_digits=5, decimal_places=3, default=0)
    and other fields.....

admin.py
class MeasurementForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(MeasurementForm, self).__init__(*args, **kwargs)
        self.fields['width'].widget = MeasurementWidget
        self.fields['height'].widget = MeasurementWidget
        self.fields['length'].widget = MeasurementWidget

    class Meta:
        model = Inventory

class InventoryAdmin(admin.ModelAdmin):        
    list_display = ('id','location', 'room', 'category', 'description', 'condition', 'length', 'width', 'height', 'status', 'sale_price', 'selling_costs', 'debt')
    list_filter = ['category']
    search_fields = ['description']
    form = MeasurementForm
    inlines = [NoteInline, ImageInline, EstimateInline]

admin.site.register(Inventory, InventoryAdmin)

I am really lost here. Does anyone have an easier way to override the widgets on the admin form?

Thanks,

Mark


Lucas Klassmann

unread,
Apr 11, 2014, 2:37:05 PM4/11/14
to django...@googlegroups.com
Hi!

I will test you code, but for now, try rewrite the form class, this way:

class MeasurementForm(forms.ModelForm):
    width = forms.DecimalField(widget=MeasurementWidget())
    height = forms.DecimalField(widget=MeasurementWidget())
    length = forms.DecimalField(widget=MeasurementWidget())

    class Meta:
        model = Inventory

Cheers.



For more options, visit https://groups.google.com/d/optout.

Mark Phillips

unread,
Apr 11, 2014, 2:59:48 PM4/11/14
to django users
Lucas,

Thanks for your comment. 

I tried your suggestion, and got this error
'module' object has no attribute 'textInput'
referring to the forms.textInput(attrs=attrs) in the widget class.

I am only trying to replace three out of 16 fields in my Inventory model with the custom widgets. I think your solution would require me to enumerate all the other fields as well? I found this on the Internet - http://collingrady.wordpress.com/2008/07/24/useful-form-tricks-in-django/, which gave me the idea for how I wrote the form.

I could be totally off base, so this is a great learning experience for me! Thanks for your help!

Mark


Lucas Klassmann

unread,
Apr 11, 2014, 3:05:36 PM4/11/14
to django...@googlegroups.com
Hi Mark,

Because TextInput is a classe inside forms module and python is case-sensitive, try this:

forms.TextInput(attrs=attrs)

Cheers.



For more options, visit https://groups.google.com/d/optout.

Mark Phillips

unread,
Apr 11, 2014, 3:33:03 PM4/11/14
to django users
Man, are you good....;)

It now works as expected!

One last question....the form shows three text boxes next to each other. How can I change the html to look like this:

[     ]  [     ] / [    ]

{text_box} space {text_box} / {text_box}

It has something to do with format_output, but I don't understand how to return a string to represent this layout. Also, how to control the size of the text boxes.

Thanks again!!

Mark


Lucas Klassmann

unread,
Apr 11, 2014, 4:03:43 PM4/11/14
to django...@googlegroups.com
Hi Mark,

You must understand that each Widget in :

widgets = (
            forms.TextInput(attrs=attrs),
            forms.TextInput(attrs=attrs),
            forms.TextInput(attrs=attrs), 
            )

Has a render() method that return html of widget. You must only concatenate each html of widget. You can use format_output like this:

def format_output(self, rendered_widgets):
        widget_a = rendered_widgets[0]
        widget_b = rendered_widgets[1]
        widget_c = rendered_widgets[2]

        return "%s %s/%s" $(widget_a, widget_b, widget_c)

I hope that solution above be that you want, but if you want customizing html/css/javascript of TextInput widget, you must override render() method of forms.TextInput widget.

Cheers.



For more options, visit https://groups.google.com/d/optout.

Mark Phillips

unread,
Apr 11, 2014, 4:13:40 PM4/11/14
to django users
Ahhhh....that makes sense. 

I was able to change the size of the text box in the widget file this way...

forms.TextInput(attrs={'size':'5'}),

Thanks again for all your help!! I learned a lot!

Mark


Reply all
Reply to author
Forward
0 new messages