コンテンツタイプやモデルを定義せずにDjangoの権限を使用するにはどうすればよいですか?


84

権限ベースのシステムを使用して、Djangoアプリケーション内の特定のアクションを制限したいと思います。これらのアクションは特定のモデルに関連付ける必要はありません(たとえば、アプリケーションのセクションへのアクセス、検索など)。そのため、モデルにはインストールされたコンテンツタイプへの参照が必要なため、ストック権限フレームワークを直接使用することはできませんPermission

独自のパーミッションモデルを作成することもできますが、Djangoパーミッションに含まれるすべての機能を書き直す必要があります。

django-authoritydjango-guardianなどのいくつかのアプリを確認しましたが、オブジェクトごとのアクセス許可を許可することで、モデルシステムにさらに結合されたアクセス許可を提供しているようです。

プロジェクトのモデル(Userおよび以外Group)を定義せずにこのフレームワークを再利用する方法はありますか?

回答:


57

DjangoのPermissionモデルにContentTypeインスタンスが必要です。

私はその周りに一つの方法は、ダミー作成されたと思いますContentType(任意のモデルに関連していないapp_labelと、modelフィールドは任意の文字列値に設定することができます)。

すべてをクリーンで快適なものにしたい場合は、ダミーのすべての醜い詳細を処理し、「Permission モデルContentTypeレス」権限インスタンスを作成するプロキシモデルを作成できます。Permission実際のモデルに関連するすべてのインスタンスを除外するカスタムマネージャーを追加することもできます。


3
よろしければ、私の実装であなたの答えを完成させます。
チューバッカ2012

残念ながら、編集内容を確認するのに十分な評判がないため、承認できません(+ 2kを要求されます)。他のユーザーがあなたの編集を拒否しているので、別の回答として追加することをお勧めします(あなたは私の賛成票を持っています!)ありがとう。
ゴンザロ

1
それは変だ。それは本当にあなたの答えの完成です、それでそれを編集することは理にかなっています。とにかく、私はそれを別の答えに入れました。
チューバッカ2012

138

まだ検索しているあなたのそれらのために:

データベーステーブルなしで補助モデルを作成できます。そのモデルは、必要な許可をプロジェクトにもたらすことができます。ContentTypeを処理したり、Permissionオブジェクトを明示的に作成したりする必要はありません。

from django.db import models
        
class RightsSupport(models.Model):
            
    class Meta:
        
        managed = False  # No database table creation or deletion  \
                         # operations will be performed for this model. 
                
        default_permissions = () # disable "add", "change", "delete"
                                 # and "view" default permissions

        permissions = ( 
            ('customer_rights', 'Global customer rights'),  
            ('vendor_rights', 'Global vendor rights'), 
            ('any_rights', 'Global any rights'), 
        )

直後にmanage.py makemigrationsmanage.py migrateこれらの権限を他の権限と同じように使用できます。

# Decorator

@permission_required('app.customer_rights')
def my_search_view(request):
    …

# Inside a view

def my_search_view(request):
    request.user.has_perm('app.customer_rights')

# In a template
# The currently logged-in user’s permissions are stored in the template variable {{ perms }}

{% if perms.app.customer_rights %}
    <p>You can do any customer stuff</p>
{% endif %}

2
それは天才です、私の日を救ってください!
Reorx 2016

2
manage.pymigrateを実行した後も何も変更されていません...新しい権限が表示されません:(
Agey

2
アプリをプロジェクト(INSTALLED_APPS)に追加しましたか?
ドミトリー

2
この答えは完璧です。また、default_permissionsを[]し、モデルのsave()でNotImplementedErrorを発生させ、アンマネージモデルが本当にこのパーミッションに対してJUSTである場合、has _ * _ permission()がFalseを返すようにすることを検討するかもしれません。
ダグラスデンハルトグ2018年

4
Metaクラスに以下を追加することをお勧めしますdefault_permissions = ()。これにより、Djangoがこのモデルのデフォルトの追加/変更/削除/表示権限を自動的に作成するのを防ぎます。これは、このアプローチを使用している場合はほとんど不要です。
ヨルダン

51

Gonzaloのアドバイスに従い、プロキシモデルカスタムマネージャーを使用して、ダミーのコンテンツタイプで「モデルレス」権限を処理しました。

from django.db import models
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType


class GlobalPermissionManager(models.Manager):
    def get_query_set(self):
        return super(GlobalPermissionManager, self).\
            get_query_set().filter(content_type__name='global_permission')


class GlobalPermission(Permission):
    """A global permission, not attached to a model"""

    objects = GlobalPermissionManager()

    class Meta:
        proxy = True

    def save(self, *args, **kwargs):
        ct, created = ContentType.objects.get_or_create(
            name="global_permission", app_label=self._meta.app_label
        )
        self.content_type = ct
        super(GlobalPermission, self).save(*args, **kwargs)

10
コードのおかげで、このコードの使用方法の例も示すといいでしょう。
ケン・コクラン

2
そのモデルの許可はどこにあるべきですか?
Mirat Can Bayrak 2013

4
GlobalPermissionを作成するには:app.modelsからimport GlobalPermission gp = GlobalPermission.objects.create(codename = 'can_do_it'、name = 'Can do it')これを実行すると、他のアクセス許可と同様に、そのアクセス許可をユーザー/グループに追加できます。 。
Julien Grenier 2014

3
@JulienGrenier Django1.8ではコードが壊れていますFieldError: Cannot resolve keyword 'name' into field. Choices are: app_label, id, logentry, model, permission
maciek 2015

2
警告:メソッド「get_queryset」(ノート言葉「クエリ」と「セット間の_の欠如)をオーバーライドする新しいジャンゴのバージョン(少なくとも1.10)が必要。
ローブ

10

いくつかのコメントで要求された、Django1.8でのChewieの回答の修正。

リリースノートには次のように書かれています。

django.contrib.contenttypes.models.ContentTypeの名前フィールドは、移行によって削除され、プロパティに置き換えられました。つまり、このフィールドでContentTypeをクエリまたはフィルタリングすることはできなくなりました。

したがって、GlobalPermissionsでは使用されないのはContentTypeでの参照の「名前」です。

修正すると、次のようになります。

from django.db import models
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType


class GlobalPermissionManager(models.Manager):
    def get_queryset(self):
        return super(GlobalPermissionManager, self).\
            get_queryset().filter(content_type__model='global_permission')


class GlobalPermission(Permission):
    """A global permission, not attached to a model"""

    objects = GlobalPermissionManager()

    class Meta:
        proxy = True
        verbose_name = "global_permission"

    def save(self, *args, **kwargs):
        ct, created = ContentType.objects.get_or_create(
            model=self._meta.verbose_name, app_label=self._meta.app_label,
        )
        self.content_type = ct
        super(GlobalPermission, self).save(*args)

GlobalPermissionManagerクラスは変更されていませんが、完全を期すために含まれています。


1
syncdbの時点で、djangoは「name」フィールドをnullにすることはできないと主張しているため、これでもdjango1.8では修正されません。
Armita 2015

それは私にとってはうまくいきましたが、私のプロジェクトにはまだdjango以外のレガシーなものがあるため、移行を使用していません。1.8には名前フィールドがないはずなので、以前のdjangoからアップグレードしていますか
rgammans 2015

4

これは代替ソリューションです。最初に自問してみてください。DBに実際に存在するが、権限を保持することを除いて決して使用されないダミーモデルを作成してみませんか?それは良くありませんが、それは有効で簡単な解決策だと思います。

from django.db import models

class Permissions(models.Model):

    can_search_blue_flower = 'my_app.can_search_blue_flower'

    class Meta:
        permissions = [
            ('can_search_blue_flower', 'Allowed to search for the blue flower'),
        ]

上記のソリューションには、Permissions.can_search_blue_flowerリテラル文字列「my_app.can_search_blue_flower」を使用する代わりに、ソースコードで変数を使用できるという利点があります。これは、IDEでのタイプミスが少なくなり、オートコンプリートが増えることを意味します。


1
使用していmanaged=Falseますが、使用させないPermissions.can_search_blue_flower何らかの理由で?
Sam Bobel 2018年

@SamBobelはい、あなたは正しいかもしれません。前回は「アブストラクト」を試したばかりだと思います。
guettli 2018年

1

proxy modelこれには、ダミーのコンテンツタイプでを使用できます。

from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType


class CustomPermission(Permission):

    class Meta:
        proxy = True

    def save(self, *args, **kwargs):
        ct, created = ContentType.objects.get_or_create(
            model=self._meta.verbose_name, app_label=self._meta.app_label,
        )
        self.content_type = ct
        super(CustomPermission, self).save(*args)

これで、モデルからの許可だけnameで許可を作成できます。codenameCustomPermission

 CustomPermission.objects.create(name='Can do something', codename='can_do_something')

また、このようにテンプレート内のカスタム権限のみをクエリして表示できます。

 CustomPermission.objects.filter(content_type__model='custom permission')
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.