4

Django アプリでは、Django の User モデルとの ManyToMany 関係を含むモデル Bet を使用しています。

class Bet(models.Model):
    ...
    participants = models.ManyToManyField(User)

ユーザーは、フォームを使用して新しい賭けを開始できる必要があります。これまで、賭けには 2 人の参加者がいて、そのうちの 1 人は自分で賭けを作成したユーザーです。つまり、新しい賭けのフォームでは、参加者を 1 人だけ選択する必要があります。フォーム データの保存時に、ベット作成者が参加者として追加されます。

私は自分のためにModelFormを使用していますNewBetForm:

class NewBetForm(forms.ModelForm):
    class Meta:
        model = Bet
        widgets = {
            'participants': forms.Select()
        }

    def save(self, user):
        ... # save user as participant

参加者フィールドのウィジェットが再定義されていることに注意してください。これにより、参加者を 1 人だけ選択できるようになっています。

ただし、これにより検証エラーが発生します。

Enter a list of values.

これがどこから来たのかよくわかりません。開発者ツールで POST データを見ると、デフォルトのウィジェットを使用して参加者を 1 人だけ選択した場合とまったく同じように見えます。ただし、to_python()ManyToManyField のメソッドには、このデータに問題があるようです。Select ウィジェットを有効にすると、少なくとも User オブジェクトは作成されません。

フォームから参加者フィールドを除外して自分で定義することで、この問題を回避できることはわかっていますが、ModelForm の容量を引き続き使用できれば、はるかに良いでしょう (結局のところ、ウィジェットの変更にすぎません)。方法を知っていれば、渡されたデータを何らかの方法で操作できるかもしれません。

問題の正確な内容と、それを解決する良い方法があるかどうかを誰かに教えてもらえますか?

前もって感謝します!

編集

コメントで示唆されているように:ビューの(関連する)コード。

def new_bet(request):
    if request.method == 'POST':
        form = NewBetForm(request.POST)
        if form.is_valid():
            form.save(request.user)
            ... # success message and redirect
    else:
        form = NewBetForm()
    return render(request, 'bets/new.html', {'form': form})
4

6 に答える 6

3

解決済みの質問を復活させるつもりはありませんが、このような解決策に取り組んでおり、コードを共有して他の人を助けると思いました.

j0ker's answer で、彼はこれを機能させるための 2 つの方法を挙げています。方法 1 を使用しました。この方法では、SelectMultiple ウィジェットから「value_from_datadict」メソッドを借用しました。

フォーム.py

from django.utils.datastructures import MultiValueDict, MergeDict

class M2MSelect(forms.Select):
    def value_from_datadict(self, data, files, name):
        if isinstance(data, (MultiValueDict, MergeDict)):
            return data.getlist(name)
        return data.get(name, None)    

class WindowsSubnetForm(forms.ModelForm):
    port_group = forms.ModelMultipleChoiceField(widget=M2MSelect, required=True, queryset=PortGroup.objects.all())
    class Meta:
        model = Subnet
于 2014-01-19T22:36:39.853 に答える
3

Django コードを掘り下げた後、私自身の質問に答えることができます。

問題は、Django の ModelFormManyToManyFieldがモデル内の をフォームの にマップModelMultipleChoiceFieldすることです。この種のフォーム フィールドでは、ウィジェット オブジェクトがそのvalue_from_datadict()メソッドからシーケンスを返すことが期待されます。ModelMultipleChoiceField(である)のデフォルトのウィジェットは、ユーザーが提供したデータからリストを返すようにSelectMultipleオーバーライドします。value_from_datadict()しかし、Selectウィジェットを使用するvalue_from_datadict()と、単純に文字列を返すスーパークラスのデフォルト メソッドが使用されます。ModelMultipleChoiceFieldそれがまったく気に入らないため、検証エラーが発生します。

私が考えることができる解決策に:

  1. 継承または何らかのクラス デコレータを介してvalue_from_datadict()のオーバーライド。Select
  2. 新しいフォーム フィールドを作成し、そのデータを m2m リレーションに保存するsave()方法を調整して、m2m フィールドを手動で処理します。ModelForm

秒の解決策は冗長ではないように思われるので、それを使用します。

于 2012-11-11T23:15:09.577 に答える
1

問題は、ManyToManyがこの関係に対して間違ったデータ型であるということです。

ある意味で、賭け自体は多対多の関係です。参加者をmanytomanyfieldとして持つことは意味がありません。必要なのは、2つのForeignKeysです。どちらもユーザー用です。1つは作成者用、もう1つはもう1つのユーザー(「アクセプター」?)用です。

于 2012-09-28T14:48:38.697 に答える
1

での検証前 (検証中) に、送信された値を変更できますForm.clean_field_name。このメソッドを使用して、select の単一の値をリストにラップできます。

class NewBetForm(forms.ModelForm):
    class Meta:
        model = Bet
        widgets = {
            'participants': forms.Select()
        }

    def save(self, user):
        ... # save user as participant

    def clean_participants(self):
        data = self.cleaned_data['participants']
        return [data]

私は実際には、選択によって提供される値がどのように見えるかを推測しているだけなので、これには少し調整が必要かもしれませんが、うまくいくと思います.

ここにドキュメントがあります。

于 2012-09-28T17:40:07.093 に答える