有朋友问: 假如我们有文章Article和类别Category两个模型,其中类别和文章是一对多的关系。我们希望某个用户在使用表单创建或编辑某篇新文章时,表单上类别对应的下拉菜单选项不显示所有类别,而只显示用户自己创建的类别,我们该如何实现?小编我今天就提供两种经典方法供参考。
如果不出意外,Django模型models.py应该是如下所示:
from django.db import modelsfrom django.contrib.auth.models import Userclass Article(models.Model):"""文章模型"""title = models.CharField('标题', max_length=200, db_index=True) author = models.ForeignKey(User, verbose_name='作者', on_delete=models.CASCADE, related_name='articles') category = models.ForeignKey('Category', verbose_name='分类', on_delete=models.CASCADE, blank=False, null=False)def __str__(self):return self.titleclass Category(models.Model):"""类别模型""" name = models.CharField('name', max_length=30, unique=True) author = models.ForeignKey(User, verbose_name='作者', on_delete=models.CASCADE, blank=True, null=True,)def __str__(self):return self.name
表单文件forms.py应该如下所示。从ArticleForm定义我们可得知,ArticleForm类没有指定显示何种类别,所以默认将显示所有类别。或许你想尝试修改ArticleForm的定义来将类别选项限定于作者自己创建的类别,但却找不到真正的突破口。
from django import formsfrom .models import Article, Categoryclass ArticleForm(forms.ModelForm):class Meta: model = Article exclude = ['author',]
那么答案在哪里?答案在视图里。我们可以在视图里对表单ForeignKey对应下拉菜单选项的内容和数量做出限制。接下来我们将分别介绍如何在基于类的视图和传统函数视图里实现。
基于类的视图(Class-based Views)
原先的视图views.py可能如下所示。 你直接使用ArticleForm,没有对form的各个字段做出任何修改或调整。
@method_decorator(login_required, name='dispatch')class ArticleCreateView(CreateView): model = Article form_class = ArticleForm template_name = 'blog/article_form.html'# Associate form.instance.user with self.request.userdef form_valid(self, form): form.instance.author = self.request.userreturn super().form_valid(form)
现在我们就要来见证下奇迹的时刻了。通过重写基于类的视图自带的get_context_data方法,你可以对form的任何字段做出修改和限制。比如本例中限定了category对应的queryset仅限于用户自己创建的类别。models.py, forms.py和模板文件什么都不需要修改。
@method_decorator(login_required, name='dispatch')class ArticleCreateView(CreateView): model = Article form_class = ArticleForm template_name = 'blog/article_form.html'# Associate form.instance.user with self.request.userdef get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['form'].fields['category'].queryset = Category.objects.filter(author=self.request.user)return contextdef form_valid(self, form): form.instance.author = self.request.userreturn super().form_valid(form)
传统函数视图(Functional Based Views)
如果你喜欢传统的函数视图,你会从下面的代码中获取启发。其中最重要的一行代码莫过于
form.fields['category'].queryset = Category.objects.filter(author=request.user).
@login_requireddef article_create(request):if request.method == 'POST': form = ArticleForm(request.POST)if form.is_valid():
article = form.save(commit=False)
article.author = request.user # Set the user object here article.save() # Now you can send it to DB
return HttpResponseRedirect("/blog/")else: form = ArticleForm() form.fields['category'].queryset = Category.objects.filter(author=request.user)return render(request, 'blog/article_create_form.html', {'form': form, })
注意:
本例ArticleForm使用的是ModelForm,由模型创建,所以使用form.fields['category']获取category字段。如果你是自定义的普通form,应使用form.category获取category字段。
小结
如果你想对表单里显示的各个字段的内容或数量做出限制,你应该在视图里操作,而不是表单里操作。记住了吗?
大江狗
2019.12