带验证的Django自定义模型字段...如何将其绑定回ModelForm

问题描述:

我在一个特定项目中经常遇到的情况是,它要求用户在英尺和英尺中输入尺寸(对于宽度/深度/高度)。计算需要在该维度上执行,所以我一直在处理一个自定义字段类型,它采用英尺/英寸(例如1'-10“)的维度并使用十进制数字将其保存到数据库一个用于解析输入的正则表达式,该字段始终显示为最终用户的英尺英寸(最终目标是编写一个 方法,以便可以选择以公制方式显示,并与measure.py和geodjango进行交互)到目前为止,我所拥有的绝对不是DRY,但除此之外,我在表单级别验证时遇到了问题。自定义模型字段本身可以正常工作(从我所见过的),而且我已经编写了一个表单字段清理方法,它应该可以验证字段的效果我的问题是如何将表单字段绑定到我的模型表单中以适用于所有宽度/深度/高度字段 我在考虑可能会覆盖初始化模型(a la self.fields ['depth'] ...),但我并不完全知道在哪里何去何从......带验证的Django自定义模型字段...如何将其绑定回ModelForm

DCML_PATTERN = re.compile(r'^(?P<feet>\d+)(?P<dec_inch>\.?\d*)\'?$') 
FTIN_PATTERN = re.compile(r'^(?P<feet>\d+)\'?\s*-?\s*(?P<inch>[0-9]|10|11)?\"?$') 

class FtInField(models.Field): 
     __metaclass__ = models.SubfieldBase 

     empty_strings_allowed = False 

     def db_type(self): 
       return 'double' 

     def get_internal_type(self): 
       return "FtInField" 

     def to_python(self,value): 
       if value is u'' or value is None: 
         return None 
       if isinstance(value, float): 
         m = FTDCML_PATTERN.match(str(value)) 
         if m is None: 
           raise Exception('Must be an integer or decimal number') 
         feet = int(m.group('feet')) 
         dec_inch = float(m.group('dec_inch') or 0) 
         inch = dec_inch * 12 
         return "%d\'-%.0f\"" % (feet,inch) 
       return value 

     def get_db_prep_value(self,value): 
       if value is u'' or value is None: 
         return None 
       m = FTIN_PATTERN.match(value) 
       if m is None: 
         raise Exception('Must be in X\'-Y" Format') 
       feet = int(m.group('feet')) 
       inch = int(m.group('inch') or 0) 
       return (feet + (inch/float(12))) 

class FtInField(forms.Field): 
     def clean(self,value): 
       super(FtInField, self).clean(value) 
       if value is u'' or value is None: 
         raise forms.ValidationError('Enter a dimension in X\'-Y" format') 
       m = FTIN_PATTERN.match(value) 
       if m is None: 
         raise forms.ValidationError('Must be in X\'-Y" Format') 
       feet = int(m.group('feet')) 
       inch = int(m.group('inch') or 0) 
       value = '%d\'-%.0f"' % (feet,inch) 
       return value 

class ProductClass(models.Model): 
     productname = models.CharField('Product Name', max_length=60,blank=True) 
     depth = FtInField('Depth (Feet/Inches)') 
     width = FtInField('Width (Feet/Inches)') 
     height = FtInField('Height (Feet/Inches)') 

class ProductClassForm(forms.ModelForm): 
     depth = FtInField() 
     width = FtInField() 
     height = FtInField() 

     class Meta: 
       model = ProductClass 

class ProductClassAdmin(admin.ModelAdmin): 
     form = ProductClassForm 
+0

为什么两个字段类具有相同的名称? – 2009-12-18 21:53:56

为避免重复,你可能应该实现处理千丈和英寸的解析为您的数据类型的类,它应该大大简化其他代码。

然后你应该创建一个模型字段和表单域,记住这些是两个完全独立的组件。 (你或多或少已经完成了,但这只是为了完整性)

现在,如果我正在阅读正确的问题,您希望为模型字段设置默认窗体字段。为了实现这一点,你需要在模型字段类上实现formfield()函数。 Ref:the django docs

谢谢你,谢谢你们俩。这是我想出的(基于你的建议)。我将努力定义一种数据类型,以便在重复方面做得更好,但与此同时,这也是有效的......(我离得很近,但距离很远......)你们真棒。谢谢。

DCML_PATTERN = re.compile(r'^(?P<feet>\d+)(?P<dec_inch>\.?\d*)\'?$') 
FTIN_PATTERN = re.compile(r'^(?P<feet>\d+)\'?\s*-?\s*(?P<inch>[0-9]|10|11)?\"?$') 

class FtInFormField(forms.Field): 
     def clean(self,value): 
       super(FtInFormField, self).clean(value) 
       if value is u'' or value is None: 
         raise forms.ValidationError('Enter a dimension in X\'-Y" format') 
       m = FTIN_PATTERN.match(value) 
       if m is None: 
         raise forms.ValidationError('Must be in X\'-Y" Format') 
       feet = int(m.group('feet')) 
       inch = int(m.group('inch') or 0) 
       value = '%d\'-%.0f"' % (feet,inch) 
       return value 

class FtInField(models.Field): 
     __metaclass__ = models.SubfieldBase 

     empty_strings_allowed = False 

     def db_type(self): 
       return 'double' 

     def get_internal_type(self): 
       return "FtInField" 

     def to_python(self,value): 
       if value is u'' or value is None: 
         return None 
       if isinstance(value, float): 
         m = FTDCML_PATTERN.match(str(value)) 
         if m is None: 
           raise Exception('Must be an integer or decimal number') 
         feet = int(m.group('feet')) 
         dec_inch = float(m.group('dec_inch') or 0) 
         inch = dec_inch * 12 
         return "%d\'-%.0f\"" % (feet,inch) 
       return value 

     def get_db_prep_value(self,value): 
       if value is u'' or value is None: 
         return None 
       m = FTIN_PATTERN.match(value) 
       if m is None: 
         raise Exception('Must be in X\'-Y" Format') 
       feet = int(m.group('feet')) 
       inch = int(m.group('inch') or 0) 
       return (feet + (inch/float(12))) 

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

class ProductClass(models.Model): 
     productname = models.CharField('Product Name', max_length=60,blank=True) 
     depth = FtInField('Depth (Feet/Inches)') 
     width = FtInField('Width (Feet/Inches)') 
     height = FtInField('Height (Feet/Inches)') 

class ProductClassForm(forms.ModelForm): 

     class Meta: 
       model = ProductClass 

class ProductClassAdmin(admin.ModelAdmin): 
     form = ProductClassForm