Django 表单 - 在多选复选框列表中显示 M2M 选择
Django form - Display M2M choices in multiselect check boxes' list
我正在尝试在表单中设置复选框以填充链接到具有 M2M 关系的另一个模型的字段。
我曾经管理过这个但做了很多测试 to display additional information,现在我选择了一个解决方案(基于我模型的 __str__()
方法),我无法再显示所有组的列表那些已经链接到我的活动的地方被选中。
同样,我可能错过了一个非常小的细节,但我没有看到任何东西,我将不胜感激。
现阶段的一些代码。
型号:
class UserGroup(models.Model):
company = models.ForeignKey(
Company, on_delete=models.CASCADE, verbose_name="société"
)
users = models.ManyToManyField(UserComp, verbose_name="utilisateurs", blank=True)
group_name = models.CharField("nom", max_length=100)
weight = models.IntegerField("poids", default=0)
hidden = models.BooleanField(default=False)
@property
def nb_users(self):
return UserComp.objects.filter(usergroup=self).count()
def __str__(self):
return self.group_name + " (Poids : " + str(self.weight) + " / " + str(self.nb_users) + " utilisateurs)"
class Event(models.Model):
company = models.ForeignKey(
Company, on_delete=models.CASCADE, verbose_name="société"
)
groups = models.ManyToManyField(UserGroup, verbose_name="groupes", blank=True)
rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")]
event_name = models.CharField("nom", max_length=200)
event_date = models.DateField("date de l'événement")
slug = models.SlugField()
current = models.BooleanField("en cours", default=False)
quorum = models.IntegerField(default=33)
rule = models.CharField(
"mode de scrutin", max_length=5, choices=rules, default="MAJ"
)
class Meta:
# Constraint(s) : an event_slug can be linked to only one company
# This allow several companies to use the same event_slugs,
# unless one sngle event_slug is used by company
verbose_name = "Evénement"
constraints = [
models.UniqueConstraint(fields=["company_id", "slug"], name="unique_event_slug")
]
def __str__(self):
return self.event_name
Form(我很确定我在第一次工作测试中已经使用了这个 __init__()
方法):
class EventDetail(forms.ModelForm):
groups = forms.ModelMultipleChoiceField(
label = "Liste des groupes",
queryset = None,
widget = forms.CheckboxSelectMultiple,
required = False
)
class Meta:
model = Event
fields = ['event_name', 'event_date', 'quorum', 'rule']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
instance = kwargs.get('instance', None)
self.fields['groups'].queryset= UserGroup.objects.\
filter(company=instance.company, hidden=False).\
order_by('group_name')
查看(创建部分有效,更新部分无效,但确实有效):
@user_passes_test(lambda u: u.is_superuser or (u.id is not None and u.usercomp.is_admin))
def adm_event_detail(request, comp_slug, evt_id=0):
'''
Manage events creation and options
'''
company = Company.get_company(request.session['comp_slug'])
# all_groups = list(UserGroup.objects.filter(company=company, hidden=False).order_by('group_name').values())
if evt_id > 0:
current_event = Event.objects.get(id=evt_id)
event_form = EventDetail(request.POST or None, instance=current_event)
else:
event_form = EventDetail(request.POST or None)
if request.method == 'POST':
if event_form.is_valid():
if evt_id == 0:
# Create new event
event_data = {
"company": company,
"groups": event_form.cleaned_data["groups"],
"event_name": event_form.cleaned_data["event_name"],
"event_date": event_form.cleaned_data["event_date"],
"quorum": event_form.cleaned_data["quorum"],
"rule":event_form.cleaned_data["rule"]
}
new_event = Event.create_event(event_data)
else:
new_event = event_form.save()
else:
print("****** FORMULAIRE NON VALIDE *******")
print(event_form.errors)
return render(request, "polls/adm_event_detail.html", locals())
HTML(摘录):
{% if evt_id %}
<form action="{% url 'polls:adm_event_detail' company.comp_slug evt_id %}" method="post">
{% else %}
<form action="{% url 'polls:adm_create_event' company.comp_slug %}" method="post">
{% endif %}
{% csrf_token %}
<p>Sélectionnez le(s) groupe(s) d'utilisateurs participants à l'événement :</p>
<ul style="list-style-type: none">
{% for grp in event_form.groups %}
<li>{{ grp }}</li>
{% endfor %}
</ul>
</form>
相关显示(第1组框应勾选):
我有点失望,至少没有评论,但我终于找到了处理所有这些问题的方法,并将在这里分享。
首先,我需要移动所有的东西在vie中,所以表格变得非常简单:
class EventDetail(forms.ModelForm):
groups = forms.ModelMultipleChoiceField(
label = "Liste des groupes",
queryset = None,
widget = forms.CheckboxSelectMultiple,
required = False
)
class Meta:
model = Event
fields = ['event_name', 'event_date', 'quorum', 'rule']
要管理创建和更新值,关键点如下:
- 我刚刚了解到,就表单创建而言,基于模型与无模型并不等同于查询集与初始值。我猜对你们中的许多人来说这是显而易见的,但对我来说还不是!因此,我实际上需要两者都有正确的显示:
queryset
属性将显示整个列表,initial
将检查链接到我的对象的那些
当字段链接到 M2M 关系时,save()
方法是不够的。我从the documentation了解到(但我显然错了!)这就足够了,但我必须单独管理这个文件。它的基本意思是:在新表单中添加值,并且不要忘记删除那些用户不想再链接的值。
因此,这是我最终的工作观点:
@user_passes_test(lambda u: u.is_superuser or (u.id is not None and u.usercomp.is_admin))
def adm_event_detail(request, comp_slug, evt_id=0):
company = Company.get_company(comp_slug)
if evt_id > 0:
current_event = Event.objects.get(id=evt_id)
event_form = EventDetail(request.POST or None, instance=current_event)
event_form.fields['groups'].initial= current_event.groups.all()
else:
event_form = EventDetail(request.POST or None)
# question_set = QuestionFormset()
event_form.fields['groups'].queryset= UserGroup.objects.\
filter(company=company, hidden=False).\
order_by('group_name')
if request.method == 'POST':
if event_form.is_valid():
if evt_id == 0:
# Create new event
event_data = {
"company": company,
"groups": event_form.cleaned_data["groups"],
"event_name": event_form.cleaned_data["event_name"],
"event_date": event_form.cleaned_data["event_date"],
"quorum": event_form.cleaned_data["quorum"],
"rule":event_form.cleaned_data["rule"]
}
new_event = Event.create_event(event_data)
else:
new_event = event_form.save()
new_event.groups.clear()
new_event = event_form.save()
new_event.groups.add(*event_form.cleaned_data['groups'])
else:
print("****** FORMULAIRE NON VALIDE *******")
print(event_form.errors)
return render(request, "polls/adm_event_detail.html", locals())
如果有更简单的方法,我将不胜感激 =)
为了提供完整的答案,我还更改了模板并只使用了一行:
{{ event_form.groups }}
和一个专门的 CSS 属性 来隐藏列表的项目符号:
#id_groups {
list-style-type: none;
}
我正在尝试在表单中设置复选框以填充链接到具有 M2M 关系的另一个模型的字段。
我曾经管理过这个但做了很多测试 to display additional information,现在我选择了一个解决方案(基于我模型的 __str__()
方法),我无法再显示所有组的列表那些已经链接到我的活动的地方被选中。
同样,我可能错过了一个非常小的细节,但我没有看到任何东西,我将不胜感激。
现阶段的一些代码。
型号:
class UserGroup(models.Model):
company = models.ForeignKey(
Company, on_delete=models.CASCADE, verbose_name="société"
)
users = models.ManyToManyField(UserComp, verbose_name="utilisateurs", blank=True)
group_name = models.CharField("nom", max_length=100)
weight = models.IntegerField("poids", default=0)
hidden = models.BooleanField(default=False)
@property
def nb_users(self):
return UserComp.objects.filter(usergroup=self).count()
def __str__(self):
return self.group_name + " (Poids : " + str(self.weight) + " / " + str(self.nb_users) + " utilisateurs)"
class Event(models.Model):
company = models.ForeignKey(
Company, on_delete=models.CASCADE, verbose_name="société"
)
groups = models.ManyToManyField(UserGroup, verbose_name="groupes", blank=True)
rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")]
event_name = models.CharField("nom", max_length=200)
event_date = models.DateField("date de l'événement")
slug = models.SlugField()
current = models.BooleanField("en cours", default=False)
quorum = models.IntegerField(default=33)
rule = models.CharField(
"mode de scrutin", max_length=5, choices=rules, default="MAJ"
)
class Meta:
# Constraint(s) : an event_slug can be linked to only one company
# This allow several companies to use the same event_slugs,
# unless one sngle event_slug is used by company
verbose_name = "Evénement"
constraints = [
models.UniqueConstraint(fields=["company_id", "slug"], name="unique_event_slug")
]
def __str__(self):
return self.event_name
Form(我很确定我在第一次工作测试中已经使用了这个 __init__()
方法):
class EventDetail(forms.ModelForm):
groups = forms.ModelMultipleChoiceField(
label = "Liste des groupes",
queryset = None,
widget = forms.CheckboxSelectMultiple,
required = False
)
class Meta:
model = Event
fields = ['event_name', 'event_date', 'quorum', 'rule']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
instance = kwargs.get('instance', None)
self.fields['groups'].queryset= UserGroup.objects.\
filter(company=instance.company, hidden=False).\
order_by('group_name')
查看(创建部分有效,更新部分无效,但确实有效):
@user_passes_test(lambda u: u.is_superuser or (u.id is not None and u.usercomp.is_admin))
def adm_event_detail(request, comp_slug, evt_id=0):
'''
Manage events creation and options
'''
company = Company.get_company(request.session['comp_slug'])
# all_groups = list(UserGroup.objects.filter(company=company, hidden=False).order_by('group_name').values())
if evt_id > 0:
current_event = Event.objects.get(id=evt_id)
event_form = EventDetail(request.POST or None, instance=current_event)
else:
event_form = EventDetail(request.POST or None)
if request.method == 'POST':
if event_form.is_valid():
if evt_id == 0:
# Create new event
event_data = {
"company": company,
"groups": event_form.cleaned_data["groups"],
"event_name": event_form.cleaned_data["event_name"],
"event_date": event_form.cleaned_data["event_date"],
"quorum": event_form.cleaned_data["quorum"],
"rule":event_form.cleaned_data["rule"]
}
new_event = Event.create_event(event_data)
else:
new_event = event_form.save()
else:
print("****** FORMULAIRE NON VALIDE *******")
print(event_form.errors)
return render(request, "polls/adm_event_detail.html", locals())
HTML(摘录):
{% if evt_id %}
<form action="{% url 'polls:adm_event_detail' company.comp_slug evt_id %}" method="post">
{% else %}
<form action="{% url 'polls:adm_create_event' company.comp_slug %}" method="post">
{% endif %}
{% csrf_token %}
<p>Sélectionnez le(s) groupe(s) d'utilisateurs participants à l'événement :</p>
<ul style="list-style-type: none">
{% for grp in event_form.groups %}
<li>{{ grp }}</li>
{% endfor %}
</ul>
</form>
相关显示(第1组框应勾选):
我有点失望,至少没有评论,但我终于找到了处理所有这些问题的方法,并将在这里分享。
首先,我需要移动所有的东西在vie中,所以表格变得非常简单:
class EventDetail(forms.ModelForm):
groups = forms.ModelMultipleChoiceField(
label = "Liste des groupes",
queryset = None,
widget = forms.CheckboxSelectMultiple,
required = False
)
class Meta:
model = Event
fields = ['event_name', 'event_date', 'quorum', 'rule']
要管理创建和更新值,关键点如下:
- 我刚刚了解到,就表单创建而言,基于模型与无模型并不等同于查询集与初始值。我猜对你们中的许多人来说这是显而易见的,但对我来说还不是!因此,我实际上需要两者都有正确的显示:
queryset
属性将显示整个列表,initial
将检查链接到我的对象的那些
当字段链接到 M2M 关系时, save()
方法是不够的。我从the documentation了解到(但我显然错了!)这就足够了,但我必须单独管理这个文件。它的基本意思是:在新表单中添加值,并且不要忘记删除那些用户不想再链接的值。
因此,这是我最终的工作观点:
@user_passes_test(lambda u: u.is_superuser or (u.id is not None and u.usercomp.is_admin))
def adm_event_detail(request, comp_slug, evt_id=0):
company = Company.get_company(comp_slug)
if evt_id > 0:
current_event = Event.objects.get(id=evt_id)
event_form = EventDetail(request.POST or None, instance=current_event)
event_form.fields['groups'].initial= current_event.groups.all()
else:
event_form = EventDetail(request.POST or None)
# question_set = QuestionFormset()
event_form.fields['groups'].queryset= UserGroup.objects.\
filter(company=company, hidden=False).\
order_by('group_name')
if request.method == 'POST':
if event_form.is_valid():
if evt_id == 0:
# Create new event
event_data = {
"company": company,
"groups": event_form.cleaned_data["groups"],
"event_name": event_form.cleaned_data["event_name"],
"event_date": event_form.cleaned_data["event_date"],
"quorum": event_form.cleaned_data["quorum"],
"rule":event_form.cleaned_data["rule"]
}
new_event = Event.create_event(event_data)
else:
new_event = event_form.save()
new_event.groups.clear()
new_event = event_form.save()
new_event.groups.add(*event_form.cleaned_data['groups'])
else:
print("****** FORMULAIRE NON VALIDE *******")
print(event_form.errors)
return render(request, "polls/adm_event_detail.html", locals())
如果有更简单的方法,我将不胜感激 =)
为了提供完整的答案,我还更改了模板并只使用了一行:
{{ event_form.groups }}
和一个专门的 CSS 属性 来隐藏列表的项目符号:
#id_groups {
list-style-type: none;
}