在django-angular中使用formsets

时间:2015-10-06 19:30:09

标签: angularjs django

django-angular非常酷。特别是,我喜欢它如何自动渲染错误。我有很多形式,这很好用。我试图做同样的事情w / formsets和遇到问题。

首先,存在一个问题,即字段名称没有映射到基础角度模型。我正在使用这样的嵌套模型:

var parent = { 
  field1 : 'a',
  field2: 'b',
  child_model: [
    { childfield1: 'c', 
      childfield2: 'd'
    },
    { childfield1: 'e', 
      childfield2: 'f'
    }
  ]
}

为了让这个结构映射到表单字段,我使用以下代码:

class MyFormSet(BaseInlineFormSet):

    def _construct_form(self, i, **kwargs):
        """
        Instantiates and returns the i-th form instance in a formset.
        """

        defaults = {
            'auto_id': self.auto_id,
            'prefix': self.add_prefix(i),
            'error_class': self.error_class,
            # THESE NEXT 3 LINES ARE THE DIFFERENT BIT...
            'formset_class': self.__class__,
            'scope_prefix': "%s[%d]" % (self.scope_prefix, i),
            'form_name': "%s-%s" % (self.formset_name, i),
        }
        if self.is_bound:
            defaults['data'] = self.data
            defaults['files'] = self.files
        if self.initial and 'initial' not in kwargs:
            try:
                defaults['initial'] = self.initial[i]
            except IndexError:
                pass
        # Allow extra forms to be empty, unless they're part of
        # the minimum forms.
        if i >= self.initial_form_count() and i >= self.min_num:
            defaults['empty_permitted'] = True
        defaults.update(kwargs)
        form = self.form(**defaults)
        self.add_fields(form, i)
        return form


class MyChildForm(Bootstrap3ModelForm, NgModelFormMixin, NgFormValidationMixin):
    class Meta:
        model = MyChildModel

    def __init__(self, *args, **kwargs):
        formset_class = kwargs.pop("formset_class", None)
        super(MyChildForm, self).__init__(*args, **kwargs)
        self.formset_class = formset_class

    def get_widget_attrs(self, bound_field):
        """
        just like the base class fn
        except it sets "ng-model" using "get_qualified_model_field_name"
        """
        attrs = super(NgModelFormMixin, self).get_widget_attrs(bound_field)
        identifier = self.add_prefix(bound_field.name)
        ng = {
            'name': bound_field.name,
            'identifier': identifier,
            'model': self.get_qualified_model_field_name(bound_field.name)
        }
        if hasattr(self, 'Meta') and bound_field.name in getattr(self.Meta, 'ng_models', []):
            attrs['ng-model'] = ng['model']
        for key, fmtstr in self.ng_directives.items():
            attrs[key] = fmtstr % ng
        return attrs

    def is_formset(self):
        """
        tells whether or not the form is a member of a formset
        """
        return self.formset_class is not None

    def get_qualified_form_field_name(self, field_name):
        """
        gets a field name suitable for ng use when binding to form
        """
        if self.is_formset():
            identifier = field_name
        else:
            identifier = self.add_prefix(field_name)
        return format_html("{0}['{1}']", self.form_name, identifier)

    def get_qualified_model_field_name(self, field_name):
        """
        gets a field name suitable for ng use when binding to model
        """
        if self.is_formset():
            identifier = field_name
        else:
            identifier = self.add_prefix(field_name)
        return format_html("{0}['{1}']", self.scope_prefix, identifier)


def MyChildModelFormSetFactory(*args, **kwargs):

    instance = kwargs.pop("instance", None)
    initial = kwargs.pop("initial", [])
    queryset = kwargs.pop("queryset", MyChildModel.objects.none())
    prefix = kwargs.pop("prefix", None)
    scope_prefix = kwargs.pop("scope_prefix", None)
    formset_name = kwargs.pop("formset_name", None)

    kwargs.update({
        "can_delete": False,
        "extra": kwargs.pop("extra", 0),
        "formset": MyFormSet,
        "form": MyChildForm,
    })
    formset = inlineformset_factory(MyParentModel, MyChildModel, *args, **kwargs)
    formset.scope_prefix = scope_prefix
    formset.formset_name = formset_name

    return formset(instance=instance, initial=initial, queryset=queryset)

get_widget_attrs中的更改正确呈现如下字段:

<input class="form-control ng-pristine ng-untouched ng-valid ng-valid-parse ng-valid-required ng-valid-maxlength" id="id_my_formset_name-0.field_name" name="my_formset_name-0.field_name" ng-model="parent.child_model[0]['field_name']">

但是当我尝试渲染{{field.errors}}时,我只是得到了一堆垃圾。我没有使用所有预期的django-angular类(&#34; djng-field-errors&#34;等)的合理元素,而是得到这样的原始文本:

(u'my_formset_name-0.my_scope_prefix-0.my_field_name', u'djng-form-control-feedback djng-field-errors', u'$dirty', u'$error.required', u'invalid', u'This field is required.')

有什么想法吗?

更新

如果我打印出.get_field_errors来自&#34;独立&#34;的字段返回的内容,那么它的价值是多少?形式我得到:

<ul class="djng-form-control-feedback djng-field-errors" ng-show="my_form_name['field1'].$dirty" ng-cloak>
  <li ng-show="my_form_name['field1'].$error.required" class="invalid">This field is required.</li>
</ul>

如果我打印出.get_field_errors从表单中的字段中返回的内容,我会得到:

<ul class="errorlist">
  <li>(&quot;my_formset_name-0[&#39;childfield1&#39;]&quot;, u&#39;djng-form-control-feedback djng-field-errors&#39;, &#39;$dirty&#39;, u&#39;$error.required&#39;, &#39;invalid&#39;, u&#39;This field is required.&#39;)</li>
</ul>

除了未转义的HTML之外,请注意父元素的类别错误。

1 个答案:

答案 0 :(得分:0)

问题很简单。 django-angular表单使用自定义的error_class。但_construct_form fn覆盖了这一点。我只需要在FormSet中指定相同的错误类:

class MyFormSet(BaseInlineFormSet):

    def __init__(self, *args, **kwargs):
        kwargs.update({
            # makes sure that child forms render errors correctly...
            "error_class": TupleErrorList,
        })
        super(MyFormSet, self).__init__(*args, **kwargs)