在我的应用程序的早期版本中,我与帐户和俱乐部之间存在多对多的关系。在我的AccountForm中,我使用" club = forms.MultipleChoiceField(widget = CheckboxSelectMultiple)"使用户能够从俱乐部的完整列表中进行选择。
▢足球
▢曲棍球
▢网球
▢游泳
但是,我现在需要包含一个可选字段,如果有的话,可以包含其成员资格引用号。像
这样的东西▢足球________
▢曲棍球________
▢网球________
▢游泳________
我意识到我必须使用through
模型,但现在我正在努力复制之前的多选样式布局。
a)我认为我需要使用内联formset但基于直通表,所以不知何故我需要一个formset工厂为每个俱乐部创建表单。我不知道该怎么做。线索?
b)包含一个复选框以反映该俱乐部的成员资格。所以可能是一个布尔字段,其中隐藏字段指示了俱乐部的ID,然后是一些自定义工作清理和保存功能。
这看起来是对的,还是有更简单的方法?
class Account(models.Model):
name = models.CharField(max_length=20)
address_street01 = models.CharField(max_length=50)
address_pc = models.CharField(max_length=10)
address_city = models.CharField(max_length=50)
class Club(models.Model):
name = models.CharField(max_length=30, unique=True)
class Membership(models.Model):
club = models.ForeignKey(Club)
account = models.ForeignKey(Account)
membership_ref = models.CharField(max_length=50, blank=True)
答案 0 :(得分:3)
我们正在使用django-extra-views中的ModelFormSetView
来查找类似的用例。它不是由through
模型支持,而是由具有多对一关系的表支持,其中与其所有属性的许多关系显示为通过ForeignKey关联的主模型的详细视图的一部分。
只需将through
模型作为through
的模型属性,它也适用于ModelFormSetView
模型。保存时甚至之前,通过get_extra_form_kwargs
,您必须设置对定义m2m字段的主模型实例的引用。
常规django FormSets的棘手问题是(对我来说)它主要用于创建新对象,而我们只需要显示现有对象并对其进行修改。基本上我们需要重复填充初始数据的表单,这些数据一次全部保存。也可以删除它们。
# You could additionally try to inherit from SingleObjectMixin
# if you override the methods that refer to cls.model
class ImportMatchView(ImportSessionMixin, ModelFormSetView):
template_name = 'import_match.html'
model = Entry # this is your through model class
form_class = EntryForm
can_delete = True
def get_success_url(self):
return self.get_main_object().get_absolute_url()
def get_factory_kwargs(self):
kwargs = super().get_factory_kwargs()
num = len(self.get_match_result())
kwargs['extra'] = num # this controls how many forms are generated
kwargs['max_num'] = num # no empty forms!
return kwargs
def get_initial(self):
# override this if you have to previous m2m relations for
# this main object
# this is a dictionary with the attributes required to prefill
# new instances of the through model
return self.get_match_result() # this fetches data from the session
def get_extra_form_kwargs(self):
# you could add the instance of the m2m main model here and
# handle it in your custom form.save method
return {'user': self.request.user}
def get_queryset(self):
# return none() if you have implemented get_initial()
return Entry.objects.none()
# return existing m2m relations if they exist
# main_id = self.get_object().pk # SingleObjectMixin or alike
# return Entry.objects.filter(main=main_id)
def formset_valid(self, formset):
# just some example code of what you could do
main = self.get_main_object()
response = super().formset_valid(formset)
main_attr_list = filter(None, [form.cleaned_data.get('entry_attr') for form in formset.forms])
main.main_attr = sum(main_attr_list)
main.save()
return response
适用于through
模型的常规Django ModelForm。与此处的用户一样,提供对定义m2m字段的模型实例的引用,以便您可以在保存之前进行分配。
def __init__(self, *args, user=None, **kwargs):
self.user = user
super().__init__(*args, **kwargs)
def save(self, commit=True):
self.instance.owner = self.user
return super().save(commit)
<form id="the-matching" method="POST"
action="{{ save_url }}" data-session-url="{{ session_url }}">
{% csrf_token %}
{{ formset.management_form }}
<ul class="match__matches">
{% for form in formset %}
{% include 'import_match__match.html' %}
{% endfor %}
</ul>
</form>
在每种形式(内部import_match__match.html
)中,您以通常的django方式迭代字段。这是隐藏字段的示例:
{% for field in form %}
{% if field.value %}
<input type="hidden" name="{{ form.prefix }}-{{ field.name }}" value="{{ field.value }}"/>
{% endif %}
{% endfor %}
处理主要对象的表单: