web-dev-qa-db-ja.com

Django Admin-特定のユーザー(admin)コンテンツ

新しいプロジェクトを整理し始めており、productscatalogsのようないくつかのモデルがあるとしましょう。

クライアント(訪問者ではなく、特定のクライアントのみ)にDjango=管理サイトで独自のカタログを作成、編集、削除することを許可します。

"Shop"というモデルを作成し、すべてのショップ(名前、住所、ロゴ、連絡先など)を作成し、そのショップにバインドされた管理ユーザーを作成するとします。

今、私はこの新しい管理者(サイト管理者ではなく、ショップ管理者-おそらくユーザーグループ)を見て、編集することを望んでいます彼のショップにリンクされているカタログのみ

それは可能ですか?

Django Admin内でこれを行う必要がありますか、それとも新しい「ショップ管理者」アプリを作成する必要がありますか?

46
Thiago Belem

まず、警告の警告:Django adminの設計哲学は、admin(_is_staff==True_)にアクセスできるユーザーはtrustedユーザー、たとえば従業員、したがって「管理者」のアクセス権を得るための「スタッフ」の指定。 、およびDjangoは、その時点でいかなる種類のセキュリティについても保証しません。

続行したい場合は、これらの特権をユーザーに割り当てないだけで、店以外のほとんどすべてを制限できます。すべてのショップオーナーに、アクセスする必要があるショップモデルを編集する権限を与える必要がありますが、他のすべては権限リストから除外する必要があります。

次に、所有者の目だけに限定する必要がある各モデルについて、「所有者」またはユーザーがアクセスを許可するフィールドを追加する必要があります。これを行うには、ModelAdminの_save_model_メソッドを使用します。このメソッドはリクエストオブジェクトにアクセスできます。

_class MyModelAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        obj.user = request.user
        super(MyModelAdmin, self).save_model(request, obj, form, change)
_

次に、ModelAdminのクエリセットを、現在のユーザーが所有するアイテムのみに制限する必要があります。

_class MyModelAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super(MyModelAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(owner=request.user)
_

ただし、それはリストされるものを制限するだけであり、ユーザーはアクセスできない他のオブジェクトにアクセスするためにURLで遊ぶことができるので、ModelAdminの脆弱なビューのそれぞれをオーバーライドして、ユーザーがいない場合にリダイレクトする必要があります所有者:

_from Django.http import HttpResponseRedirect
from Django.core.urlresolvers import reverse

class MyModelAdmin(admin.ModelAdmin):
    def change_view(self, request, object_id, form_url='', extra_context=None):
        if not self.queryset(request).filter(id=object_id).exists():
            return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))

        return super(MyModelAdmin, self).change_view(request, object_id, form_url, extra_context)

    def delete_view(self, request, object_id, extra_context=None):
        if not self.queryset(request).filter(id=object_id).exists():
            return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))

        return super(MyModelAdmin, self).delete_view(request, object_id, extra_context)

    def history_view(self, request, object_id, extra_context=None):
        if not self.queryset(request).filter(id=object_id).exists():
            return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))

        return super(MyModelAdmin, self).history_view(request, object_id, extra_context)
_

PDATE 06/05/12

ModelAdminのクエリセットはすでにユーザーによって制限されているため、変更、削除、および履歴ビューでself.queryset()を使用できることを指摘してくれた@ christophe31に感謝します。これにより、モデルのクラス名がきれいに抽象化され、コードの脆弱性が軽減されます。また、filterで_try...except_ブロックの代わりにexistsgetを使用するように変更しました。この方法はより合理化されており、実際にはより単純なクエリにもなります。

59
Chris Pratt

トップコメントが最新の回答ではなくなったため、ここに投稿しています。私はDjango 1.9を使用しています。これがいつ変更されたのかわかりません。

たとえば、異なる会場と各会場に関連付けられた異なるユーザーがいる場合、モデルは次のようになります。

class Venue(models.Model):
    user = models.ForeignKey(User)
    venue_name = models.CharField(max_length=255)
    area = models.CharField(max_length=255)

ここで、ユーザーがDjango=管理パネルからログインすることを許可した場合、ユーザーのスタッフステータスはtrueでなければなりません。

Admin.pyは次のようになります。

class FilterUserAdmin(admin.ModelAdmin): 
    def save_model(self, request, obj, form, change):
        if getattr(obj, 'user', None) is None:  
            obj.user = request.user
        obj.save()
    def get_queryset(self, request):
        qs = super(FilterUserAdmin, self).queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(user=request.user)
    def has_change_permission(self, request, obj=None):
        if not obj:
            return True 
        return obj.user == request.user or request.user.is_superuser


@admin.register(Venue)
class VenueAdmin(admin.ModelAdmin):
    pass

関数名がquerysetからget_querysetに変更されました。

編集:私は私の答えを拡張したかった。 queryset関数を使用せずにフィルターされたオブジェクトを返す別の方法があります。私は、この方法がより効率的であるか効率的であるかを知らないことを強調したいと思います。

Get_querysetメソッドの代替実装は次のとおりです。

def get_queryset(self, request):
    if request.user.is_superuser:
        return Venue.objects.all()
    else:
        return Venue.objects.filter(user=request.user)

さらに、関係がより深い場合、コンテンツをフィルタリングすることもできます。

class VenueDetails(models.Model):
    venue = models.ForeignKey(Venue)
    details = models.TextField()

ここで、Venueを外部キーとして持つがUserを持たないこのモデルをフィルタリングする場合、クエリは次のように変更されます。

def get_queryset(self, request):
    if request.user.is_superuser:
        return VenueDetails.objects.all()
    else:
        return VenueDetails.objects.filter(venue__user=request.user)

Django ORMでは、さまざまな種類の関係にアクセスできます。これらの関係は、「__」を介して必要なだけ深くすることができます

Here's 上記の公式ドキュメントへのリンク。

12
chatuur

RelatedOnlyFieldListFilterが役立つと思います。ここへのリンクDjango Doc: RelatedOnlyFieldListFilter

list_filterは次のようになります。最初の要素はフィールド名で、2番目の要素はDjango.contrib.admin.FieldListFilterを継承するクラスです。例:

class PersonAdmin(admin.ModelAdmin):
    list_filter = (
        ('is_staff', admin.BooleanFieldListFilter),
    )

関連モデルの選択肢を制限 RelatedOnlyFieldListFilterを使用して、そのリレーションに含まれるオブジェクトに制限します。

  class BookAdmin(admin.ModelAdmin):
      list_filter = (
           ('author', admin.RelatedOnlyFieldListFilter),
      ) 

著者がUserモデルのForeignKeyであると仮定すると、これはlist_filterの選択肢を、すべてのユーザーをリストする代わりに本を書いたユーザーに制限します。 (外部ユーザーとモデル以外のユーザー、リストフィルターのリストとフィルターのリストのフィルターを使用して、ユーティリティを追加することはできません。)

0
HamzDiou

申し訳ありませんが、遅れていることはわかっていますが、他の人の助けになるかもしれません。 Django-permission アプリが目的の実行に役立つと思います。

0
Khchine Hamza