各种动态 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'
)
我在动态构建多个表单集时验证 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'
)