【问题标题】:Django compound/nested/subforms?Django复合/嵌套/子表单?
【发布时间】:2011-03-08 01:52:33
【问题描述】:

我正在寻找这些 Django SuperForms 的更新版本。似乎无法让它在 Django 1.2 中工作。特别是,我希望它与 ModelForms 一起使用。

我的用例几乎和他的一样;我有一个Address 模型,我想在各个地方用作子表单。尝试在视图函数中组合所有内容很痛苦。

【问题讨论】:

  • SuperForms 应该做什么?
  • 你基本上想要一个遍历外键的ModelForm 吗?
  • @sdolan:是这样的。好吧,不是只是遍历外键,因为这样子表单就必须完全基于从模型生成的自动生成的表单。我希望能够实际选择要嵌入的其他表单(或模型表单)。
  • @Nick:链接的 google 群组帖子总结得很好:groups.google.com/group/django-developers/browse_thread/thread/… 我想到的具体例子是,用户有用户名、电子邮件、电话号码等。但他们也有地址。地址在整个项目中使用,并且有自己的模型,所以我想将此表单嵌入到用户注册中,这样我就不必尝试在视图中分别验证两个表单,然后组合生成的模型。

标签: django django-1.2


【解决方案1】:

我已更新 superforms.py 以使用 1.2,并将其附加到您链接到的票证上:http://code.djangoproject.com/attachment/ticket/3706/superform.2.py

我正在做的一个项目可以从中受益,所以我想我也会花时间帮助你。

请记住,我只是让它在 1.2 中工作,并没有真正尝试清理内部。现在我已经有了证明 API 的测试用例,我可以稍后再回去清理它。

如果您将它与 ModelForms 一起使用,并且想要 save() 功能,则必须覆盖 SuperForm 类上的方法。

我目前在我的“通用”存储库中本地拥有它,代码覆盖率约为 90%,涵盖多个子表单、混合子表单和声明的表单以及模型表单。我在下面包含了测试用例(注意它使用了我的 TestCaseBase 类,但这应该会给你 API 的要点)。如果您有任何问题或我遗漏的任何地方,请告诉我。

from django_common.forms.superform import SuperForm, SubForm
from django_common.test import TestCaseBase
from django import forms

class WhenSuperFormsIsUsedWithOnlySubForms(TestCaseBase):
    def get_superform_with_forms(self, post_data=None):
        class AddressForm(forms.Form):
            street = forms.CharField(max_length=255)
            city = forms.CharField(max_length=255)

        class BusinessLocationForm(forms.Form):
            phone_num = forms.CharField(max_length=255)

        class TestSuperForm(SuperForm):
            address = SubForm(AddressForm)
            business_location = SubForm(BusinessLocationForm)

        return TestSuperForm(data=post_data)

    def should_not_be_valid_with_no_data(self):
        tsf = self.get_superform_with_forms()
        self.assert_false(tsf.is_valid())

    def should_have_two_sub_forms(self):
        tsf = self.get_superform_with_forms()
        self.assert_equal(len(tsf.base_subforms),  2)
        self.assert_equal(len(tsf.forms), 2)

    def should_display_as_ul(self):
        tsf = self.get_superform_with_forms()
        self.assert_equal(tsf.as_ul(), '<li><label for="id_business_location-phone_num">Phone num:</label> <input id="id_business_location-phone_num" type="text" name="business_location-phone_num" maxlength="255" /></li>\n<li><label for="id_address-street">Street:</label> <input id="id_address-street" type="text" name="address-street" maxlength="255" /></li>\n<li><label for="id_address-city">City:</label> <input id="id_address-city" type="text" name="address-city" maxlength="255" /></li>')

    def should_display_as_table(self):
        tsf = self.get_superform_with_forms()
        self.assert_equal(tsf.as_table(), '<tr><th><label for="id_business_location-phone_num">Phone num:</label></th><td><input id="id_business_location-phone_num" type="text" name="business_location-phone_num" maxlength="255" /></td></tr>\n<tr><th><label for="id_address-street">Street:</label></th><td><input id="id_address-street" type="text" name="address-street" maxlength="255" /></td></tr>\n<tr><th><label for="id_address-city">City:</label></th><td><input id="id_address-city" type="text" name="address-city" maxlength="255" /></td></tr>')

    def should_display_as_p(self):
        tsf = self.get_superform_with_forms()
        self.assert_equal(tsf.as_p(), '<p><label for="id_business_location-phone_num">Phone num:</label> <input id="id_business_location-phone_num" type="text" name="business_location-phone_num" maxlength="255" /></p>\n<p><label for="id_address-street">Street:</label> <input id="id_address-street" type="text" name="address-street" maxlength="255" /></p>\n<p><label for="id_address-city">City:</label> <input id="id_address-city" type="text" name="address-city" maxlength="255" /></p>')

    def should_display_as_table_with_unicode(self):
        tsf = self.get_superform_with_forms()
        self.assert_equal(tsf.__unicode__(), tsf.as_table())

    def should_be_valid_if_good_data(self):
        data = {
            'business_location-phone_num' : '8055551234',
            'address-street' : '1234 Street Dr.',
            'address-city' : 'Santa Barbara',
        }
        tsf = self.get_superform_with_forms(data)
        self.assert_true(tsf.is_valid())
        self.assert_equal(tsf.cleaned_data['business_location']['phone_num'],
                          '8055551234')
        self.assert_equal(tsf.cleaned_data['address']['street'], '1234 Street Dr.')
        self.assert_equal(tsf.cleaned_data['address']['city'], 'Santa Barbara')

    def should_be_invalid_if_missing_data(self):
        data = {
            'business_location-phone_num' : '8055551234',
            'address-street' : '1234 Street Dr.',
        }
        tsf = self.get_superform_with_forms(data)
        self.assert_false(tsf.is_valid())

        self.assert_false(tsf.errors['business_location'])
        self.assert_true(tsf.errors['address'])
        self.assert_equal(tsf.errors['address']['city'], ['This field is required.'])

    def should_be_invalid_if_invalid_data(self):
        data = {
            'business_location-phone_num' : '8055551234',
            'address-street' : '1234 Street Dr.',
            'address-city' : '',
        }
        tsf = self.get_superform_with_forms(data)
        self.assert_false(tsf.is_valid())


class WhenSuperformsIsUsedWithSubFormsAndDeclaredFields(TestCaseBase):
    """Some basic sanity checks that working with fields combined with SubForms works."""
    def get_superform_with_forms(self, post_data=None):
        class AddressForm(forms.Form):
            street = forms.CharField(max_length=255)

        class TestSuperForm(SuperForm):
            name = forms.CharField(max_length=255)
            address = SubForm(AddressForm)

        return TestSuperForm(data=post_data)

    def should_not_be_valid_with_no_data(self):
        tsf = self.get_superform_with_forms()
        self.assert_false(tsf.is_valid())

    def should_have_two_forms_and_a_single_subform(self):
        tsf = self.get_superform_with_forms()
        self.assert_equal(len(tsf.base_subforms),  1)
        self.assert_equal(len(tsf.forms), 2)

    def should_print_as_table(self):
        tsf = self.get_superform_with_forms()
        self.assert_equal(tsf.as_table(), '<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="255" /></td></tr>\n<tr><th><label for="id_address-street">Street:</label></th><td><input id="id_address-street" type="text" name="address-street" maxlength="255" /></td></tr>')

    def should_validate_when_fields_exist(self):
        data = {
            'name': 'Sam',
            'address-street': 'Some Street',
        }
        tsf = self.get_superform_with_forms(data)
        self.assert_true(tsf.is_valid())

        self.assert_equal(tsf.cleaned_data['name'], 'Sam')
        self.assert_equal(tsf.cleaned_data['address']['street'], 'Some Street')

    def should_not_validate_with_invalid_data(self):
        data = {
            'name': '',
            'address-street': 'Some Street',
        }
        tsf = self.get_superform_with_forms(data)
        self.assert_false(tsf.is_valid())

        self.assert_equal(tsf.errors['name'], ['This field is required.'])



class WhenSuperformsIsUsedWithModelForms(TestCaseBase):
    def get_superform_with_forms(self, post_data=None):
        from django.db import models
        class Address(models.Model):
            city = models.CharField(max_length=255)

        class AddressForm(forms.ModelForm):
            class Meta:
                model = Address

        class TestSuperForm(SuperForm):
            address = SubForm(AddressForm)

        return TestSuperForm(data=post_data)

    def should_not_be_valid_with_no_data(self):
        tsf = self.get_superform_with_forms()
        self.assert_false(tsf.is_valid())

    def should_have_two_forms_and_a_single_subform(self):
        tsf = self.get_superform_with_forms()
        self.assert_equal(len(tsf.base_subforms),  1)
        self.assert_equal(len(tsf.forms), 1)

    def should_print_as_table(self):
        tsf = self.get_superform_with_forms()
        self.assert_equal(tsf.as_table(), '<tr><th><label for="id_address-city">City:</label></th><td><input id="id_address-city" type="text" name="address-city" maxlength="255" /></td></tr>')

    def should_validate_when_fields_exist(self):
        data = {
            'address-city': 'Some City',
        }
        tsf = self.get_superform_with_forms(data)
        self.assert_true(tsf.is_valid())

        self.assert_equal(tsf.cleaned_data['address']['city'], 'Some City')

    def should_not_validate_with_invalid_data(self):
        data = {
            'address-city': '',
        }
        tsf = self.get_superform_with_forms(data)
        self.assert_false(tsf.is_valid())

        self.assert_equal(tsf.errors['address']['city'], ['This field is required.'])

享受吧!

【讨论】:

  • 哇,太棒了!非常感谢 :) 不幸的是,这对于我当前的项目(找到另一个解决方案)来说有点晚了,但也许是我的下一个项目!
  • 是的,没问题。感谢您提出问题并提供良好的起点。这是很长时间以来对我有用的东西。至于你的其他解决方案,你最终做了什么?哦,我赚到了赏金吗? :)
  • 你确实做到了。我假设您的代码确实有效;)我最终做了什么?只有程序员能做的最愚蠢的事情!从头开始重写整个表单应用程序!
  • 老实说,我还没有在实践中使用过该课程(现在必须进行 付费 工作:)),尽管测试非常彻底集成/接口。而且,哇,这太傻了:) 单元测试是修复原始 superforms.py 文件唯一耗时的部分。如果你确实重写了它,而且更好,你可以考虑将你的新类上传到 bitbucket(或类似的)。
  • 可能会那样做!他们还有一些工作要做,但结果非常好。与 Django 的表单相比,所需的代码要少得多。同时执行客户端和服务器端验证!
猜你喜欢
  • 2018-04-28
  • 2013-12-11
  • 1970-01-01
  • 2013-02-15
  • 1970-01-01
  • 2012-04-13
  • 2018-08-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多