django 表单
Building forms with Django is straightforward if your fields are simple inputs like text fields, but what if your forms need to reference other models?
如果您的字段是文本字段之类的简单输入,那么使用Django构建表单非常简单,但是如果您的表单需要引用其他模型呢?
In this tutorial, we will build a form with a group of checkboxes, where the options come from another model.
在本教程中,我们将构建带有一组复选框的表单,其中的选项来自另一个模型。
使用ManyToMany构建表单 (Building forms with ManyToMany)
I’m currently building a meal-planning app. To give users flexibility, they will have the option to create separate plans for different household members.
我目前正在构建一个膳食计划应用程序。 为了给用户带来灵活性,他们可以选择为不同的家庭成员创建单独的计划。
These are my models. I have a class called ‘Meal’, which references a class called ‘Member’.
这些是我的模特。 我有一个名为“ Meal”的类,它引用了一个名为“ Member”的类。
meals/models.pyclass Meal(models.Model): name = models.CharField(max_length=255)
date = models.DateField()
members = models.ForeignKey(Member, on_delete=models.PROTECT)household/models.pyclass Member(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
photo = models.ImageField(
upload_to=’uploads/members/’, default=’/icons/member.svg’) def __str__(self):
return ‘%s Household | %s’ % (self.user, self.name)
I want users to select which household members should be included for a meal.
我希望用户选择应包括哪些家庭成员作为一餐。
I could use a foreign key field, but that will only let me choose one member.
我可以使用外键字段,但这只能让我选择一个成员。
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/81eeeec46533f8e23ae17a96d89bc341.png)
Using a ForeignKey to link Meal with Member doesn’t work because I can only select one member per meal.
使用ForeignKey将Meal与Member链接不起作用,因为我每餐只能选择一名成员。
We need to use a ManyToManyField instead.
我们需要改用ManyToManyField。
多对多 (ManyToMany)
A ManyToMany field is used when a model needs to reference multiple instances of another model. Use cases include:
当模型需要引用另一个模型的多个实例时,将使用ManyToMany字段。 用例包括:
- A user needs to assign multiple categories to a blog post 用户需要为博客文章分配多个类别
- A user wants to add multiple blog posts to a publication用户想要将多个博客文章添加到发布中
In this case, the user needs to be able to add multiple members to a meal. If the choices were fixed and the same for every user, then we could use django-multiple-select-field. If the choices are customizable (i.e. you can do CRUD on the choices), then the choices will have its own model and must be linked using a ManyToManyField.
在这种情况下,用户需要能够向一餐添加多个成员。 如果选择是固定的,并且对于每个用户都是相同的,那么我们可以使用django-multiple-select-field 。 如果选项是可定制的(即您可以对选项进行CRUD),则选项将具有自己的模型,并且必须使用ManyToManyField进行链接。
The model will now look like this:
现在,模型将如下所示:
models.pyclass Meal(models.Model): name = models.CharField(max_length=255) date = models.DateField() members = models.ManyToManyField(Member)
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/4ef36dae3142b1a7c3587c28b3997094.png)
This is okay. The core functionality is there but the form needs a lot of work.
没关系核心功能在那里,但是表单需要大量工作。
For one, I need to cmd-click to select multiple users. This is bad for user experience; checkboxes would be much more user-friendly.
首先,我需要按一下md键以选择多个用户。 这不利于用户体验。 复选框将更加人性化。
The labels have been taken straight from admin. I want the user to see just the first name, not the user’s email as well.
标签直接取自管理员。 我希望用户仅看到名字,也不要看到用户的电子邮件。
我们需要一个自定义表格 (We need a custom form)
In Django, we can use class-based views to generate forms without defining one in forms.py. This is often fine, but Django chooses the form widgets for us. Sometimes, we need a bit more control.
在Django中,我们可以使用基于类的视图来生成表单,而无需在Forms.py中定义表单。 通常这很好,但是Django为我们选择了表单小部件。 有时,我们需要更多的控制权。
We can create a custom form class in forms.py where we can choose which of Django’s form widgets get used for each field.
我们可以在forms.py中创建一个自定义表单类,在这里我们可以选择将哪个Django表单小部件用于每个字段。
Here, we set the members field to use the CheckboxSelectMultiple widget instead of the default multiple choice field.
在这里,我们将成员字段设置为使用CheckboxSelectMultiple小部件,而不是默认的多选字段。
ManyToManyField is a subclass of django.models but not of django.forms. Instead, we use ModelMultipleChoiceField when referring to forms.
ManyToManyField是django.models的子类,但不是django.forms的子类。 而是在引用表单时使用ModelMultipleChoiceField。
forms.pyclass CreateMealForm(forms.ModelForm): class Meta:
model = Meal
fields = [‘name’, ‘date’, ‘members’] name = forms.CharField()
date = forms.DateInput() members = forms.ModelMultipleChoiceField(
queryset=Member.objects.all(),
widget=forms.CheckboxSelectMultiple
)
ModelMultipleChoiceField takes an argument called ‘queryset’. This lets us control which instances of the Member class will be displayed as options.
ModelMultipleChoiceField采用名为“ queryset ”的参数。 这使我们可以控制Member类的哪些实例将显示为选项。
If you didn’t already have a custom form, you will need to edit your view to include your new formclass.
如果您还没有自定义表单,则需要编辑视图以包括新的表单类。
views.pyclass AddMeal(CreateView):
model = Meal
form_class = CreateMealForm
template_name = ‘meals/add_meal.html’
success_url = reverse_lazy(‘index’)
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/791f144892e9e9c26ee40061dc6a4094.png)
We’ve got our checkboxes but we still have a couple of issues.
我们有我们的复选框,但仍然有几个问题。
We can tweak our CSS to remove the bullet points and execute a more polished design. But first, we need to change the labels for the checkboxes.
我们可以调整CSS以删除项目符号点并执行更精细的设计。 但是首先,我们需要更改复选框的标签。
自定义标签 (Custom Labels)
To control the checkbox labels, we must tweak the ModelMultipleChoiceField.
要控制复选框标签,我们必须调整ModelMultipleChoiceField。
To do this, we:
为此,我们:
- Create a custom form class which inherits from ModelMultipleChoiceField 创建一个从ModelMultipleChoiceField继承的自定义表单类
- Override the ‘label_from_instance’ method覆盖'label_from_instance'方法
- Replace the reference to forms.ModelMultipleChoiceField to our new custom class.将对forms.ModelMultipleChoiceField的引用替换为我们的新自定义类。
Our forms.py will now look like this:
我们的forms.py现在将如下所示:
forms.pyfrom django import forms
from .models import Meal
from core.models import Memberclass CustomMMCF(forms.ModelMultipleChoiceField): def label_from_instance(self, member):
return “%s” % member.nameclass CreateMealForm(forms.ModelForm): class Meta:
model = Meal
fields = [‘name’, ‘date’, ‘members’] name = forms.CharField()
date = forms.DateInput() members = CustomMMCF(
queryset=Member.objects.all(),
widget=forms.CheckboxSelectMultiple
)
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/752e35293e91199a369e2b1fbf499489.png)
At this point, the back-end work is **almost** complete.
至此,后端工作几乎完成了。
等等,有个错误吗? (Wait, there’s a bug?)
See queryset=Member.objects.all()?
看到queryset = Member.objects.all()吗?
forms.pymembers = CustomMMCF(
queryset=Member.objects.all(),
widget=forms.CheckboxSelectMultiple
)
This will return members for every user. The question is, how do you pass the request.user object to a form class?
这将为每个用户返回成员。 问题是,如何将request.user对象传递给表单类?
This bug deserves its own post. It’s right here: 👇
该错误值得其发表。 就在这里:👇
Thanks for reading.
谢谢阅读。
翻译自: https://medium.com/swlh/django-forms-for-many-to-many-fields-d977dec4b024
django 表单