Django自定义小部件将IntegerField分割为模型窗体中的小时和秒字段 [英] Django custom widget to split an IntegerField into hours and seconds fields in a Model Form

查看:183
本文介绍了Django自定义小部件将IntegerField分割为模型窗体中的小时和秒字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在以IntegerField的分钟数保存时间。在我的前端表单(模型窗体)中,我想将输入分成一个字段,分钟和一个字段数小时。我也希望能够使用InLineFormset。这可以用一个很好的方式完成吗?我可以使用JavaScript,但这似乎不是一个很好的解决方案,或者是吗?

解决方案

你想使用 MultiWidget 。我建议你覆盖该字段本身。请查看创建自定义字段的文档



如果可以,可能值得将数据存储为代表 python timedelta 。您可能需要在您的字段中添加一些自定义方法。使用timedelta将会很有用,如果你想对你的字段进行任何计算。



你可能想在表单验证



ve测试了以下几个小时,mins,sec multiwidget与基于浮动的自定义字段Django 1.4 -

 从datetime import timedelta 
从django导入表单
从django.db导入模型
从django.core导入异常,验证器


def validate_timedelta(value):
if不是isinstance(value,timedelta):
raise exceptions.ValidationError('一个有效的时间段是必需的)


class SplitHoursMinsWidget(forms.widgets.MultiWidget):

def __init __(self,* args,** kwargs):
widgets =(
forms.TextInput(),
forms.TextInput(),
forms.TextInput(),

super(SplitHoursMinsWidget,self).__ init __(widgets,* args,** kwargs)

def解压缩(self,value):
如果值:
return [value.seconds // 3600,(value.seconds // 60)%60,
value.seconds%60]
返回[无,无,无]

def format_output(self,rendered_widgets):
return(HH:MM:SS+ rendered_widgets [0] +:+ rendered_widgets [1] +
:+ rendered_widgets [2])

def value_from_datadict(self,data,files,name):
hours_mins_secs = [
widget.value_from_datadict(data,files,name +'_%s'%i)
for i,widget in enumerate(self.widgets)]
try:
time_delta =(timedelta(hours = float(hours_mins_secs [0]))+
timedelta(minutes = float hours_mins_secs [1]))+
timedelta(seconds = float(hours_mins_secs [2])))
除了ValueError:
返回无
其他:
返回time_delta


类TimeDeltaFormField(forms.Field):

小部件= SplitHoursMinsWidget


class TimeDeltaField(models.Field):

__metaclass__ = models.SubfieldBase

description =要保存的字段python timedelta对象

default_validators = [validate_timedelta]

def to_python(self,value):
如果validator.EMPTY_VALUES或isinstance中的值(value,timedelta )
返回值
try:
return timedelta(seconds = float(value))
除了:
raise exceptions.ValidationError('需要一个有效的时间段')

def get_prep_value(self,value):
return float(value.days * 86400 + value.seconds)

def get_internal_type(self):
return'FloatField'

def formfield (self,** kwargs):
defaults = {'form_class':TimeDeltaFormField}
defaults.update(kwargs)
return super(TimeDeltaField,self).formfield(** defaults)$值得注意的是,这不提供任何有用的输入级验证(可以在60以上输入)分钟和秒输入元素)。也许这可以通过javascript最好地解决。


I am storing time duration as minutes in an IntegerField. In my front end form (Model Form), I want to split the input into one field for minutes and one field for hours. I also want to be able to use the InLineFormset. Can this be done in a nice way? I could use javascript, but that does not seeml like a good solution to me, or is it?

解决方案

You want to use a MultiWidget. I'd suggest that you override the field itself. Have a look at the docs for creating custom fields.

If you can, it might be worth storing your data as a floating point representing a python timedelta. You may want to add a few custom methods to your field. Using timedelta will be useful if you want to do any calculations with your field.

You'll probably want to do a bit of reading on form validation too.

I've tested the following hours, mins, sec multiwidget with float based custom field on Django 1.4 -

from datetime import timedelta
from django import forms
from django.db import models
from django.core import exceptions, validators


def validate_timedelta(value):
    if not isinstance(value, timedelta):
        raise exceptions.ValidationError('A valid time period is required')


class SplitHoursMinsWidget(forms.widgets.MultiWidget):

    def __init__(self, *args, **kwargs):
        widgets = (
            forms.TextInput(),
            forms.TextInput(),
            forms.TextInput(),
        )
        super(SplitHoursMinsWidget, self).__init__(widgets, *args, **kwargs)

    def decompress(self, value):
        if value:
            return [value.seconds // 3600, (value.seconds // 60) % 60,
                    value.seconds % 60]
        return [None, None, None]

    def format_output(self, rendered_widgets):
        return ("HH:MM:SS " + rendered_widgets[0] + ":" + rendered_widgets[1] +
                 ":" + rendered_widgets[2])

    def value_from_datadict(self, data, files, name):
        hours_mins_secs = [
            widget.value_from_datadict(data, files, name + '_%s' % i)
            for i, widget in enumerate(self.widgets)]
        try:
            time_delta = (timedelta(hours=float(hours_mins_secs[0])) +
                          timedelta(minutes=float(hours_mins_secs[1])) +
                          timedelta(seconds=float(hours_mins_secs[2])))
        except ValueError:
            return None
        else:
            return time_delta


class TimeDeltaFormField(forms.Field):

    widget = SplitHoursMinsWidget


class TimeDeltaField(models.Field):

    __metaclass__ = models.SubfieldBase

    description = "Field to hold a python timedelta object"

    default_validators = [validate_timedelta, ]

    def to_python(self, value):
        if value in validators.EMPTY_VALUES or isinstance(value, timedelta):
            return value
        try:
            return timedelta(seconds=float(value))
        except:
            raise exceptions.ValidationError('A valid time period is required')

    def get_prep_value(self, value):
        return float(value.days * 86400 + value.seconds)

    def get_internal_type(self):
        return 'FloatField'

    def formfield(self, **kwargs):
        defaults = {'form_class': TimeDeltaFormField}
        defaults.update(kwargs)
        return super(TimeDeltaField, self).formfield(**defaults)

It's worth noting that this doesn't provide any useful input level validation (it's possible to enter over 60 in both the minutes and seconds input elements). Perhaps that could be best resolved with javascript.

这篇关于Django自定义小部件将IntegerField分割为模型窗体中的小时和秒字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆