ForeinKey のフィールドをもつモデルのフォームを ModelForm で定義してちょっと困ったこと

例えば、Django で下記のモデルを定義したとする。

from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    user = models.ForeinKey(User)
    name = models.CharField(max_length=255)


class Entry(models.Model):
    user = models.ForeinKey(User)
    title = models.CharField(max_length=255)
    content = models.TextField()
    category = models.ForeinKey(Category)

次に ModelForm を使って

from django import forms
from models Entry

class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
        exclude = ("user",)

というフォームを定義する。

で、このフォームをテンプレートで表示すると、category テーブルにある全てのデータを選択できてしまう。ログインユーザーが作成したカテゴリだけを選択させたいのに。ちょっと困った。

Django のソースコードを見ると、ModelForm は models.ForeinKey のフィールドを forms.ModelChoiceField を使って表示していた。この ModelChoiceField はコンストラクタで選択肢(クエリセット)を渡せるみたいだ。

ということは、ログインユーザーが作成したカテゴリだけを選択させたい場合、ユーザーで絞り込んだクエリセットを渡せばいい。

フォームを定義するときは、ログインユーザーで絞り込んだクエリセットを渡せないので、ビュー内でフォームを作成した後に ModelChoiceField を差し替えれば OK。

form = EntryForm()
queryset = Category.objects.filter(user=request.user)
form.fields["category"] = forms.ModelChoiceField(queryset,
                                                 initial=form.fields["category"].initial)

あちらこらちのビューに同じコードを書き散らすのは行儀がよろしくないので、フォームを生成する関数を定義するべきだな。