Setting how a form displays a field in a widget in django

I have a custom field called HourField that allows you to enter “1:30” to represent an hour and a half. The method clean()turns it into 1.5 for the FloatField in the model. All of this works great. Now I have a problem when, when I want to create a set of forms, this field will be displayed as "1.5" instead of "1:30", which is what I want.

Is there some method similar to clean()but working the other way around?

edit: using a different type of storage method is out of the question. I have several other custom fields that I created that are stored as one type of object, but entered by the user as a string. I used the above example because it was the most direct.

+3
source share
4 answers

To customize how the field is rendered, you also need to define a widget.

The following code shows some of the responsibilities of widgets in django:

  • render the form tag (the superclass "TextInput" will take care of this)
  • formats the value to display inside the form field. there is useful information here: the class of values ​​may differ, because you can display cleared values ​​coming from the field, or raw data coming from the POST form. Therefore, a test if we have a numerical value in _format_value; if it's a string, just leave it as it is.
  • Please indicate if the value has changed from the original data. Very important when using forms with default values.

, widgets.TimeInput django, .

from django import forms
from django.forms import widgets
from django.forms.util import ValidationError

class HourWidget(widgets.TextInput):

    def _format_value(self, value):
        if isinstance(value, float) or isinstance(value, int):
            import math

            hours = math.floor(value)
            minutes = (value - hours) * 60
            value = "%d:%02d" % (hours, minutes)

        return value

    def render(self, name, value, attrs=None):
        value = self._format_value(value)
        return super(HourWidget, self).render(name, value, attrs)

    def _has_changed(self, initial, data):
        return super(HourWidget, self)._has_changed(self._format_value(initial), data)

class HourField(forms.Field):
    widget = HourWidget

    def clean(self, value):
        super(HourField, self).clean(value)

        import re
        match = re.match("^([0-9]{1,2}):([0-9]{2})$", value)
        if not match:
            raise ValidationError("Please enter a valid hour ( ex: 12:34 )")

        groups = match.groups()
        hour = float(groups[0])
        minutes = float(groups[1])

        if minutes >= 60:
            raise ValidationError("Invalid value for minutes")

        return hour + minutes/60

, 1:20 1:19, , . , , .

+3

, . , , render(), , .

, , , django.forms.extras.widgets , .

+1
from django.forms.widgets import TextInput

class TimeInput(TextInput):
     def _format_value(self, value):
         """Convert float to time"""
         if not value:
             return ''
         fraction, integer = math.modf(value)
         minutes = 0 if fraction == 0 else .6 / fraction
         return "%d:%02d" % (int(integer), int(minutes))

     def render(self, name, value, attrs=None):
        value = self._format_value(value)
        return super(TimeInput, self).render(name, value, attrs)
+1

I think you can experiment with the date template tag, or perhaps create your own template tag. However, this is a strip help - the real problem is that you have to save this time in Python as time using the datetime module. This ensures that it is well formatted.

0
source

Source: https://habr.com/ru/post/1716178/


All Articles