質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.23%

Djangoのフォームで、選択をフィルタリングする方法

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 3
  • VIEW 3,323

am120sec

score 12

行き詰りました。

最初のプルダウンメニューで選択した項目を
次のプルダウンメニューに連動させたいです。
(admin管理サイトは使用しない)

#model.py

class Create_type(models.Model):
    name = models.CharField(max_length=20,unique=True)
    def __str__(self):
        return self.name

class Genre(models.Model):
    name = models.CharField(max_length=20,unique=True)
    create_type = models.ManyToManyField(Create_type)
    def __str__(self):
        return self.name

class Title_Post(models.Model):
    title = models.CharField(unique=True)
    create_type = models.ForeignKey(Create_type,blank=false)
    genre = models.ManyToManyField(Genre,blank=True)


以下のサイトを参考にForm.pyを作成しました。
https://stackoverflow.com/questions/291945/how-do-i-filter-foreignkey-choices-in-a-django-modelform

#form.py

class Title_Form(forms.ModelForm):
    class Meta:
        model = Title_Post
        fields = ('title','create_type','genre')

    def __init__(self, *args, **kwargs):
        super(Title_Form, self).__init__(*args, **kwargs)
        self.fields['genre'].queryset = Genre.objects.filter(
            type=self.instance.type)
#view.py
def title_new(request):
    if request.method == "POST":
        f = Title_Form(request.POST,request.FILES)
        if f.is_valid():
            post = f.save(commit=False)
            post.published_date = timezone.now()
            post.save()
            f.save_m2m()
            return redirect('app:title', pk=post.pk)
    else:
        f = Title_Form()
    return render(request, 'app/title_edit.html', {'form':f})
小説 映画 アニメ
SF SF SF
ホラー ホラー ホラー
3D 3D

イメージ的には
Type(小説、映画、アニメ)があって、
Genre(SF、ホラー、3D)等のジャンルがあり、
フォーム上で小説を選択した場合、genreのリストでは「3D」を選択できないようにしたいです。
表示しないという動作はJavascriptで記述できたのですが、Javascriptを無効にした場合、不正な入力がされてしまうと思うので、Django側のModelやFormで入力をエラーとしたいです。

現状は、そもそもGenreに値が表示されません・・・

イメージ説明

何卒、よろしくお願いいたします・・

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

とりえあえず、全部選択肢にでるように
formのinitなしにしましょう。

それからTitle_Formのcleanをオーバーライドしましょう
cleanのoverride例
値がおかしかったらValidationErrorをraiseして、
あってたら特に何もしなくていいです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/03 19:54 編集

    書くところ間違えました……
    回答は消せないのかな

    "cleaned_data"のタイプミスと思われます。

    キャンセル

  • 2018/04/03 22:53

    私のほうからは削除できませんでした。

    "cleaned_data"でいけました・・・・・・・・・!
    中学生からやり直してきます(´;ω;`)

    キャンセル

  • 2018/04/03 22:58

    スマホからだと見つけられなかったんですが、
    PCで開いたら削除リクエストの場所みつけました。
    お騒がせしました。

    キャンセル

0

やり方が正しいかどうかは置いておいて、とりあえず動く物ができましたので報告です。

genre_filter = genre.filter(create_type__name = create_type)
アンダースコアが2つ入っているのはcreate_typeのnameフィールドを指定するためらしいです。

ご指摘ありましたら是非ともお願いします。

#form.py
class Title_Form(forms.ModelForm):
    volume = forms.IntegerField(required=False)
    class Meta:
        model = Title_Post
        fields = ('title','create_type','genre')


    def clean(self):
        cleaned_date = super().clean()
        genre = self.cleaned_data.get("genre")
        create_type = self.cleaned_data.get("create_type")

        if genre and create_type:
            genre_filter = genre.filter(create_type__name = create_type)
            if len(genre_filter) == 0:
                raise forms.ValidationError(
                    "選択されたジャンルはタイプと関連性がないため使用できません"
                )

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.23%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる