7

Django 1.1 を実行していますが、ManytoManyField の「limit_choices_to」オプションが機能しません。

私は2つのモデルを持っています:

class MemberPhoto(ImageModel):
    title       = models.CharField(_('title'), max_length=255, blank=True, null=True)
    caption     = models.CharField(_('caption'), max_length=255, blank=True, null=True)
    date_added  = models.DateTimeField(_('date added'), default=datetime.now, editable=False)
    member      = models.ForeignKey(User)

    def __unicode__(self):
        return u'%s (%s)' % (self.member.username, self.id)

class lock(models.Model):
    user = models.ForeignKey(User, related_name="owner")
    to_user = models.ForeignKey(User, related_name="to_user")
    unlocked_photos = models.ManyToManyField(MemberPhoto, blank=True, null=True, limit_choices_to = {'member':'user'})
    objects = locking_manager()

2 番目のモデルでは、複数選択フィールドに表示される "unlocked_photos" ("MemberPhoto" オブジェクト) のみが、"ロック」オブジェクトの「ユーザー」(ユーザーオブジェクトでもあります)。

これについては Django のドキュメントに従っていると思っていましたが、うまくいきません。次のエラーが表示されます。

TemplateSyntaxError

Caught an exception while rendering: invalid input syntax for integer: "user"

「limit_choices_to」を次のように変更してみました。

limit_choices_to = {'member': user} --- 機能しません

limit_choices_to = {'member__username':'kyle'} --- これは機能しますが、役に立ちません。ユーザー名を手動で指定しているだけです

代わりに、現在の「ロック」オブジェクトからユーザーを取得し、それによって MemberPhoto の「メンバー」プロパティをフィルター処理するにはどうすればよいですか?

助けてくれる人に感謝します。

カイル

4

3 に答える 3

14

このリンクで私が望んでいたことを正確に達成する答えを見つけました: Django MTMField: limit_choices_to = other_ForeignKeyField_on_same_model? 、同じ問題を抱えている人のために、ここに作業コードを投稿しています。「limit_choices_to」は単に私が望んでいたことを達成できない可能性があり、管理者が使用するフォームをカスタマイズすることが最善の方法であることがわかりました。

from django.contrib import admin
from django import forms
from gayhop.apps.locking.models import lock
from gayhop.apps.photos.models import MemberPhoto

class LockAdminForm(forms.ModelForm):
  class Meta:
    model = lock

  def __init__(self, *args, **kwargs):
    super(LockAdminForm, self).__init__(*args, **kwargs)
    self.fields['unlocked_photos'].queryset = MemberPhoto.objects.filter(member=self.instance.user)


class LockAdmin(admin.ModelAdmin):
  form = LockAdminForm
  filter_horizontal = ('unlocked_photos',)

django.contrib.admin.site.register(lock, LockAdmin)

変更する必要があるのは次のとおりです。

  1. モデルの名前 (上記の例では「lock」)
  2. モデルの ManyToManyField フィールドの名前 (上記の例では「unlocked_photos」)
  3. 関連するモデルの名前 (上の例では「MemberPhoto」)
  4. 関連オブジェクトをフィルタリングするフィールドの名前 (上記の例では「メンバー」)
  5. 関連オブジェクトをフィルタリングするために使用するフィールドの値 (「self.instance.」で始まり、フィールドの名前になります。上記の例では「user」です)
  6. 最後に、カスタム管理フォームと管理モデルのクラス名がすべて一致していることを確認してください。

これが誰かに役立つことを願っています!

于 2011-08-21T08:02:11.583 に答える
4

カイルの答えに追加するには、

カスタム フォームを作成することは、多対多フィールドをそのようにカスタマイズする唯一の方法です。しかし、私が発見したように、その方法は、そのモデルのインスタンスを変更している場合にのみ機能します。(少なくとも Django 1.5 では)

これは、self.instanceModel オブジェクトを返すためです。(私はクレイジーな概念を知っています) しかし、そのモデルのインスタンスを作成している場合、そのモデルはまだ作成されていないため、self.instanceは例外を返しDoesNotExistます。

この問題を回避するには、次の 2 つのフォームを作成します。

class MyModelChangeForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        super(MyModelChangeForm, self).__init__(*args, **kwargs)
        my_model = self.instance
        self.fields['fields'].queryset = OtherRelatedModel.objects.filter(other_id=my_model.other)


class MyModelCreationForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def save(self, commit=True):
        my_model = super(MyModelCreationForm, self).save(commit=False)
        *** Do other things with the my_model if you want ***
        my_model.save()
        return my_model

次に、admin.py 内で、作成フォーム用に別のフィールドセットを作成します。

class MyModelAdmin(admin.ModelAdmin):
    filter_horizontal = ('other')
    list_display = ('field1', 'field2' 'field3')

    fieldsets = (
        ("Model info:", {'fields': ("field1", "field2", "field3")}),
        ("More Model info:", {'fields': ("other",)}),
    )
    add_fieldsets = (
        ("Initial info:", {'fields': ("field1", "field2", "field3")}),
    )

    form = MyModelChangeForm
    add_form = MyModelCreationForm

    def get_fieldsets(self, request, obj=None):
        if not obj:
            return self.add_fieldsets
        return super(MyModelAdmin, self).get_fieldsets(request, obj)

    def get_form(self, request, obj=None, **kwargs):
        """
        Use special form during MyModel creation
        """
        defaults = {}
        if obj is None:
            defaults.update({
                'form': self.add_form,
                'fields': admin.util.flatten_fieldsets(self.add_fieldsets),
            })
        defaults.update(kwargs)
        return super(MyModelAdmin, self).get_form(request, obj, **defaults)

obj が None の場合 (つまり、リクエストがモデルのインスタンスを追加することである場合)、MyModelCreationForm を使用する get_form と get_fieldsets をオーバーライドする必要があることに注意してください。これは、django 開発者が django.contrib.auth.admin でカスタム UserCreation フォームとフィールドセットを取得するために使用するメソッドと同じです。(その例については、そこにあるソースコードを見てください)

最後に、モデルは model.py で次のようになります。

class MyModel(models.Model):
    *** field definitions ***
    other = models.ManytoManyField(OtherRelatedModel, null=True, blank=True)

class OtherRelatedModel(models.Model):
    other_id = model.AutoField(primary_key=True)
    *** more field definitions ***

モデルを含めた唯一の理由は、クラス MyModel の多対多フィールド定義を確認できるようにするためです。null と空白を True に設定する必要ありません。そうでない場合は、定義でデフォルト値を割り当てるか、MyModelCreationForm の save() 関数で設定する必要があることを覚えておいてください。

お役に立てれば!(完全に間違っている場合は、訂正してください! 私も学ぶ必要があります。)

-ありがとう

于 2013-07-31T22:14:44.843 に答える
1

これを Django 3 で動作させるために数時間を費やしました。Django 仲間のためにここに貼り付けます。更新が必要だったのは「def init」の部分です。

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.fields['unlocked_photos'].queryset = MemberPhoto.objects.filter(member=self.instance.user)
于 2021-05-20T06:02:05.663 に答える