13

「self」への外部キーを持つ ForeignKeyField (FKF) または ManyToManyField (MTMF) フィールドを持つモデルが与えられた場合、Django 管理者 (管理者) 内で自己(再帰) 選択を防ぐにはどうすればよいですか。

つまり、admin でモデル インスタンスの自己 (再帰的) 選択を防ぐことができるはずです。これは、新しいインスタンスを作成するのではなく、モデルの既存のインスタンスを編集するときに適用されます。

たとえば、ニュース アプリの記事の次のモデルを取り上げます。

class Article(models.Model):           
    title = models.CharField(max_length=100)
    slug = models.SlugField()
    related_articles = models.ManyToManyField('self')

3 つArticleのインスタンス (タイトル: a1-3) がある場合Article、管理者を介して既存のインスタンスを編集すると、related_articlesフィールドはデフォルトで、すべての記事のリストを提供する html (複数) 選択ボックスによって表されます ( Article.objects.all())。Articleユーザーは、それ自体以外のインスタンスのみを表示し、選択できるようにする必要があります。たとえば、 Articlea1 を編集するときrelated_articlesに、a2、a3 を選択できます。

現在、これを行う方法には 3 つの可能性があると考えています。

  1. の管理フォーム フィールドで利用可能な選択肢を提供するクエリセットを設定する方法を提供します(たとえば、ユーザーが表示および選択できる related_articles のリストから編集中の現在のインスタンスを除外するrelated_articlesなど、除外クエリ フィルターを介して) 。使用するクエリセットは、 customArticle.objects.filter(~Q(id__iexact=self.id))のコンストラクタ ( ) 内で発生する可能性があり、または何らかの動的オプションを介して発生する可能性があります. これには、フィルタリングに使用するために編集中のインスタンスを取得する方法が必要になります.__init__Article ModelFormlimit_choices_to Model
  2. またはクラスのsave_model関数をオーバーライドして、インスタンスを保存する前にそれ自体をチェックしてから削除します。これは、管理者ユーザーが編集中のインスタンスを含むすべての記事を表示および選択できることを意味します (既存の記事の場合)。Article ModelModelAdminrelated_articles
  3. テンプレートなど、管理者以外で使用する必要がある場合は、自己参照を除外します。

理想的な解決策 (1) は、編集中のインスタンスのフィルターされたクエリセット変数をモデル フォーム コンストラクターに渡すことができるため、現在、管理者の外部のカスタム モデル フォームを介して実行できます。質問は、Article同じことを行うためにフォームが作成される前に管理者が編集しているインスタンス、つまり 'self' を取得できるかということです。

これについて間違った方法で行っている可能性がありますが、FKF / MTMF を同じモデルに定義することが許可されている場合は、管理者に正しいことをさせ、ユーザーが自分自身を選択できないようにする方法があるはずです。利用可能な選択肢のリストから除外します。

注:解決策 2 と 3 は現在実行可能であり、これらを回答として取得しないようにするために提供されています。理想的には、解決策 1 への回答を得たいと考えています。

4

4 に答える 4

10

カールは正しいです。これがカットアンドペーストのコードサンプルです。admin.py

しっかりと把握していない場合、Djangoの関係をナビゲートするのは難しい場合があり、実際の例は「これを読んでください」よりも1000倍の価値がある可能性があります(何が起こっているのかを理解する必要はありません)。 。

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

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['myManyToManyField'].queryset = MyModel.objects.exclude(
            id__exact=self.instance.id)
于 2009-12-08T21:18:57.257 に答える
2

admin でカスタム ModelForm を使用できます ( ModelAdmin サブクラスの "form" 属性を設定することにより)。そのため、他の場所と同じように管理者で行うことができます。

于 2009-05-16T16:23:07.037 に答える
1

get_form次のように ModelAdminのメソッドをオーバーライドすることもできます。

def get_form(self, request, obj=None, **kwargs):
    """
    Modify the fields in the form that are self-referential by
    removing self instance from queryset
    """
    form = super().get_form(request, obj=None, **kwargs)
    # obj won't exist yet for create page
    if obj:
        # Finds fieldnames of related fields whose model is self
        rmself_fields = [f.name for f in self.model._meta.get_fields() if (
            f.concrete and f.is_relation and f.related_model is self.model)]
        for fieldname in rmself_fields:
            form.base_fields[fieldname]._queryset =
                form.base_fields[fieldname]._queryset.exclude(id=obj.id)
    return form

これは、自己参照モデルフィールドを自動的に見つけて、それらすべてから self を削除する、サイズに合わせたすべてのソリューションであることに注意してください:-)

于 2016-09-28T15:45:01.093 に答える