有朋友問: 假如我們有文章Article和類別Category兩個模型,其中類別和文章是一對多的關係。我們希望某個用戶在使用表單創建或編輯某篇新文章時,表單上類別對應的下拉菜單選項不顯示所有類別,而只顯示用戶自己創建的類別,我們該如何實現?小編我今天就提供兩種經典方法供參考。
如果不出意外,Django模型models.py應該是如下所示:
from django.db import models
from django.contrib.auth.models import User
class 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.title
class 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 forms
from .models import Article, Category
class 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.user
def form_valid(self, form):
form.instance.author = self.request.user
return 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.user
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'].fields['category'].queryset = Category.objects.filter(author=self.request.user)
return context
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
傳統函數視圖(Functional Based Views)
如果你喜歡傳統的函數視圖,你會從下面的代碼中獲取啟發。其中最重要的一行代碼莫過於
form.fields['category'].queryset = Category.objects.filter(author=request.user).
@login_required
def 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
注意:
本例ArticleForm使用的是ModelForm,由模型創建,所以使用form.fields['category']獲取category欄位。如果你是自定義的普通form,應使用form.category獲取category欄位。
小結
如果你想對表單裡顯示的各個欄位的內容或數量做出限制,你應該在視圖裡操作,而不是表單裡操作。記住了嗎?
大江狗
2019.12