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

发布于 2024-08-15 13:55:43 字数 2991 浏览 6 评论 0原文

我在一个特定项目中经常遇到的情况是,它要求用户输入以英尺和英寸为单位的尺寸(宽度/深度/高度)。需要在该尺寸上执行计算,因此我一直在研究一种自定义字段类型,该类型采用英尺/英寸(例如 1'-10")为单位的尺寸,并将其作为十进制数保存到数据库中用于解析输入的正则表达式。该字段始终向最终用户显示为英尺-英寸(最终目标是编写一个 方法能够选择以公制显示,并与measure.py和geodjango东西交互)。到目前为止,我所拥有的绝对不是 DRY,但除此之外,我在表单级别的验证方面遇到了麻烦。自定义模型字段本身工作正常(从我所见),并且我编写了一个表单字段清理方法,该方法应该可以验证该字段。我的问题是如何将该表单字段挂回到我的模型表单中以适用于所有宽度/深度/高度字段。 我在想也许会覆盖模型表单上的 init (la self.fields['depth']...) ,但我不太确定从这里该去哪里...

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

A common occurrence I have with one particular project is that it requires the user to enter dimensions (for width/depth/height) in Feet and Inches. Calculations are needed to be performed on that dimension, so I've been working on a custom field type that takes in a dimension in Feet/Inches (eg. 1'-10") and saves it to the database as a decimal number using a regex to parse the input. The field displays to the end-user as feet-inches at all times (with the eventual goal of writing a
method to be able to optionally display in metric, and interact with measure.py, and geodjango stuff). What I have so far is definitely not DRY, but aside from that, I'm having trouble with validation at the form level. The custom model field itself works properly (from what I've seen), and I've written a form field clean method which should work to validate the field. My question is how to hook that form field back into my model form to work for all the width/depth/height fields.
I'm thinking maybe an override of the init on the modelform (a la self.fields['depth']...) , but I'm not quite sure where to go from here...

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

2024-08-22 13:55:43

谢谢你们,谢谢你们俩。这就是我想到的(基于您的建议)。我将致力于定义一种数据类型,以使其在重复方面变得更好,但与此同时,这有效......(我是如此接近,但又如此遥远......)你们太棒了。谢谢。

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

Thank you, thank you to both of you. This is what I came up with (based on both of your advise). I'll work on defining a data type to make it better in terms of repetition, but in the meantime, this works...(I was so close, yet so far away...) You guys are amazing. Thanks.

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
〗斷ホ乔殘χμё〖 2024-08-22 13:55:43

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

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

现在,如果我正确地阅读问题,您想要为模型字段设置默认表单字段。为了促进这一点,您需要在模型字段类上实现 formfield() 函数。参考: django 文档

To avoid duplication you should probably implement a data type class that handles the parsing of feets and inches for you, it should greatly simplify the other code.

Then you should create a model field and form field, keeping in mind that these are two COMPLETELY SEPARATE components. (which you more or less have already done, but this is just for completeness)

Now, if I'm reading the question right, you want to set the default form field for your model field. To facilitate this you want to implement the formfield() function on your model field class. Ref: the django docs

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文