各种动态 django 表单集抛出 missing_management_form 错误

Various dynamically django formsets throws missing_management_form error

我在动态构建多个表单集时验证 Django 表单集时遇到问题 在这种情况下,一个客户可以是各种品牌和联系人。

models.py

class Client(ChangesMixin, models.Model):
  name = models.CharField(verbose_name="Nombre", max_length=100, unique=True)
  code = models.PositiveIntegerField(verbose_name="Código", blank=True)

  class Meta:
    verbose_name = "Cliente"
    verbose_name_plural = "Clientes"

class Brand(ChangesMixin, models.Model):
  name = models.CharField(verbose_name="Marca", max_length=100, blank=True, null=True)
  client = models.ForeignKey('Client', verbose_name="Cliente", related_name='brand_client', on_delete=models.DO_NOTHING)

  class Meta:
    verbose_name = "Marca"
    verbose_name_plural = "Marcas"

 class Contact(ChangesMixin, models.Model):
   name = models.CharField(verbose_name="Contacto", max_length=100, blank=True, null=True)
   client = models.ForeignKey('Client', verbose_name="Cliente", related_name='contact_client', on_delete=models.DO_NOTHING)

   class Meta:
     verbose_name = "Contacto"
     verbose_name_plural = "Contactos"

我有两种动态创建表单和表单集的方法

forms.py

def get_custom_formset(entry_model=None, entry_fields=None, action=None):

  formset = None

  if action == 'create':
    extra = 1
  else:
    extra = 0

  formset = modelformset_factory(
    model = entry_model,
    extra = extra,
    form = get_custom_form(entry_model, entry_fields, action)

  return formset

def get_custom_form(entry_model=None, entry_fields=None, action=None):

  class _CustomForm(forms.ModelForm):

    class Meta:
      model = entry_model
      fields = [field.name for field in entry_fields]

    def __init__(self, *args, **kwargs):
      """
      """
      super(_CustomForm, self).__init__(*args, **kwargs)
      instance = getattr(self, 'instance', None)
      if instance and instance.pk:
        for field in entry_fields:
          if action == 'detail':
            self.fields[field.name].widget.attrs['readonly'] = True

  return _CustomForm

我创建了一个 class 视图,其中包含 get 和 post 方法,具体取决于传递的模型。 我获取模型字段来构建表单,如果字段是外键,我会使用这些具体模型构建表单集。

views.py

class CustomCreateView(LoginRequiredMixin, View, PermissionRequiredMixin):
  model = None
  template = 'create.html'

  def get(self, request, *args, **kwargs):
    template_form = str(self.model._meta.verbose_name).lower() + "_form.html"
    model_fields = self.model._meta.get_fields()

    form = None
    formset = None
    formsets = {}

    for main_field in model_fields:
      main_field_name = main_field.__class__.__name__
      if main_field_name == 'ManyToOneRel':
        model_names = str(main_field.name).split("_")
        submodel = apps.get_model('app', model_names[0])
        submodel_fields = submodel._meta.get_fields()
        formset = app_forms.get_custom_formset(submodel, submodel_fields, 'create')
        queryset = submodel.objects.none()

        UPDATED with SOLUTION
        formset = formset(queryset=queryset, prefix=submodel.__name__.lower())
        formsets[submodel._meta.verbose_name.lower()] = formset

      elif main_field_name == 'ManyToManyField':
        print("NOT PROVIDED YET")

      form = app_forms.get_custom_form(self.model, model_fields, 'create')
      form = form(prefix=self.model.__name__.lower())

    return render(request, self.template, {
        'form': form,
        'formsets': formsets,
        'template_form': template_form,
    })

  def post(self, request, *args, **kwargs):
    template_form = str(self.model._meta.verbose_name).lower() + "_form.html"
    model_fields = self.model._meta.get_fields()

    for main_field in model_fields:
      main_field_name = main_field.__class__.__name__

      if main_field_name == 'ManyToOneRel':
        model_names = str(main_field.name).split("_")
        submodel = apps.get_model('app', model_names[0])
        submodel_fields = submodel._meta.get_fields()
        formset = app_forms.get_custom_formset(submodel, submodel_fields, 'create')
        queryset = submodel.objects.none()
        formset = formset(queryset=queryset, prefix=submodel.__name__.lower())
        formsets[submodel.__name__.lower()] = formset

      elif main_field_name == 'ManyToManyField':
        print("NOT PROVIDED YET")

    form = app_forms.get_custom_form(self.model, model_fields, 'create')
    form = form(request.POST, prefix=self.model.__name__.lower())

    for prefix, formset in formsets.items():
      formset = formset.__class__(request_post, prefix=prefix)
      if formset.is_valid() and form.is_valid(): HERE THROWS THE ERROR

对于模板,我有 3 个级别来动态构建表单和表单集

create.html

{% get_url urls 'create' as element_create %}
<form class="" action="{% url element_create %}" method="POST">
  {% csrf_token %}
  {% include template_form %}
  {% if formsets|length > 0 %}
    {% for subtemplateformname, formset in formsets.items %}
      {% include 'formset.html' %}
    {% endfor %}
  {% endif %}
</form>

formset.html

{{ formset.management_form }}
{% for form in formset %}
  {% for hidden in form.hidden_fields %}
    {{ hidden }}
  {% endfor %}
  {% include 'form.html' %}
{% endfor %}

form.html

{% load widget_tweaks %}

{% for field in form %}
  <div class="form-group{% if field.errors %} has-error{% endif %}">
    <label for="{{ field.id_for_label }}">{{ field.label }}</label>
    {{ field|add_class:"form-control" }}
    {% for error in field.errors %}
      <p class="help-block">{{ error }}</p>
    {% endfor %}
  </div>
{% endfor %}

首先,当您获得相关模型的表单集时,您通过了 sub_fields 但我不知道这是从哪里来的?

formset = app_forms.get_custom_formset(submodel, sub_fields, 'create')

错误与您定义表单集前缀的方式有关。在 GET 中,您传递的是 model_names[0],对于正常关系,它将是模型名称小写且不带任何空格。让我们使用名为 MyModel 的模型,例如

main_field.name  # 'mymodel_set'
model_names = str(main_field.name).split("_")  # ['mymodel', 'set']
model_names[0]  # 'mymodel'
formset = formset(queryset=queryset, prefix=model_names[0])

当您将表单集分配给 formsets 词典时,您使用的是不同的东西,即使您对它的处理相同

formsets[submodel._meta.verbose_name.lower()] = formset
...
for prefix, formset in formsets.items():
    formset = formset.__class__(request_post, prefix=prefix)

submodel._meta.verbose_name 将 return 模型名称 空格。因此,任何在其详细名称中包含 2 "words" 的模型都不会设置正确的前缀(例如 MyModel._meta.verbose_name.lower() == 'my model'model_names[0] == 'mymodel'