Bir içerik türü veya modeli tanımlamadan Django izinlerini nasıl kullanabilirim?


86

Django uygulamamdaki belirli eylemleri kısıtlamak için izin tabanlı bir sistem kullanmak istiyorum. Bu eylemlerin belirli bir modelle ilgili olması gerekmez (örn. Uygulamadaki bölümlere erişim, arama ...), bu nedenle stok izinleri çerçevesini doğrudan kullanamıyorum , çünkü Permissionmodel yüklü bir içerik türüne referans gerektiriyor.

Kendi izin modelimi yazabilirdim, ancak daha sonra Django izinlerine dahil olan tüm güzellikleri yeniden yazmam gerekir, örneğin:

Django-otorite ve django-guardian gibi bazı uygulamaları kontrol ettim , ancak nesne başına izinlere izin vererek model sistemine daha da bağlı izinler sağlıyorlar.

Proje için ( Userve dışında Group) herhangi bir model tanımlamadan bu çerçeveyi yeniden kullanmanın bir yolu var mı ?

Yanıtlar:


57

Django'nun Permissionmodeli bir ContentTypeörnek gerektirir .

Sanırım bunun etrafından dolaşmanın bir yolu ContentType, herhangi bir modelle ilgili olmayan bir kukla oluşturmaktır ( app_labelve modelalanları herhangi bir dize değerine ayarlanabilir).

Her şeyin temiz ve güzel olmasını istiyorsanız , kuklaların tüm çirkin ayrıntılarını işleyen ve "modsuz" izin örnekleri oluşturan bir Permission proxy modeli oluşturabilirsiniz ContentType. Ayrıca Permission, gerçek modellerle ilgili tüm örnekleri filtreleyen özel bir yönetici de ekleyebilirsiniz .


3
Sakıncası yoksa, cevabını uygulamamla tamamlayacağım.
Chewie

Maalesef, düzenlemenizi gözden geçirmek için yeterli itibarım olmadığı için onaylayamıyorum (benden + 2k istiyor). Diğer kullanıcılar düzenlemelerinizi reddediyor, bu yüzden onu başka bir cevap olarak eklemenizi öneririm (benim oyum sizde!) Tekrar teşekkürler.
Gonzalo

1
Bu tuhaf. Cevabınız için gerçekten bir tamamlayıcıdır, bu yüzden onu bir düzenleme yapmak mantıklıdır. Neyse, başka bir cevaba koydum.
Chewie

148

Hala arayanlar için:

Veritabanı tablosu olmayan bir yardımcı model oluşturabilirsiniz. Bu model, ihtiyacınız olan herhangi bir izni projenize getirebilir. ContentType ile uğraşmaya veya açıkça İzin nesneleri oluşturmaya gerek yoktur.

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'), 
        )

Hemen sonra manage.py makemigrationsve manage.py migratebu izinleri diğerleri gibi kullanabilirsiniz.

# 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
bu dahi, günümü kurtar!
reorx


2
Uygulamanızı projenize (INSTALLED_APPS) eklediniz mi?
Dmitry

2
Bu cevap mükemmel. Ayrıca [] ed default_permissions, modelin save () öğesinde NotImplementedError'ı yükselttim ve yönetilmeyen model gerçekten bu izin için SADECE ise _ * _ iznine () sahipse False döndürmeyi düşünebilirim.
Douglas Denhartog

4
Aşağıdaki Meta sınıfında eklemenizi öneririz: default_permissions = (). Bu, Django'nun bu model için varsayılan ekleme / değiştirme / silme / görüntüleme izinlerini otomatik olarak oluşturmasını engelleyecektir; bu, bu yaklaşımı kullanıyorsanız büyük olasılıkla gereksizdir.
Ürdün

51

Aşağıdaki Gonzalo'nun tavsiyesi , ben kullanılan vekil modeli ve özel yöneticisi bir kukla içerik türü ile benim "modelless" izinleri uygulamayı.

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
kod için teşekkürler, bu kodun nasıl kullanılacağına dair bir örnek de göstermek güzel olurdu.
Ken Cochrane

2
bu model izni nerede yaşamalı?
Mirat Can Bayrak

4
Bir GlobalPermission oluşturmak için: app.models'dan import GlobalPermission gp = GlobalPermission.objects.create (codename = 'can_do_it', name = 'Yapabilir') Bu çalıştırıldığında, bu izni diğer izinler gibi kullanıcılara / gruba ekleyebilirsiniz .
Julien Grenier

3
@JulienGrenier Django 1.8 kod sonları: FieldError: Cannot resolve keyword 'name' into field. Choices are: app_label, id, logentry, model, permission.
maciek

2
Uyarı: Django'nun daha yeni sürümleri (en az 1.10), "get_queryset" yöntemini geçersiz kılmalıdır ("sorgu" ve "küme sözcükleri arasında _ eksikliğine dikkat edin).
Lob

10

Birkaç yorumda talep edildiği üzere, Django 1.8'de Chewie'nin cevabı için düzeltme.

Sürüm notlarında şöyle yazıyor:

Django.contrib.contenttypes.models.ContentType'ın ad alanı bir geçişle kaldırıldı ve bir özellik ile değiştirildi. Bu, artık bir ContentType'ı bu alana göre sorgulamanın veya filtrelemenin mümkün olmadığı anlamına gelir.

Bu nedenle, GlobalPermissions'da kullanılmayan ContentType'daki referanstaki 'ad'dır.

Düzeltdiğimde aşağıdakileri alıyorum:

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 sınıfı değişmemiştir ancak tamlık için dahil edilmiştir.


1
Bu hala django 1.8 için düzeltmiyor çünkü syncdb zamanında django "isim" alanının boş olamayacağını iddia ediyor.
Armita

Benim için çalıştı, ancak hala projemde bulunan eski django dışı şeyler nedeniyle geçişleri kullanmıyorum. 1.8
rgammans

4

Bu alternatif bir çözümdür. Önce kendinize sorun: Neden DB'de gerçekten var olan ancak izinleri tutmak dışında hiç kullanılmayan bir Kukla Model yaratmıyorsunuz? Bu hoş değil ama bence geçerli ve açık bir çözüm.

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'),
        ]

Yukarıdaki çözümün faydası, değişkeni Permissions.can_search_blue_flower"my_app.can_search_blue_flower" değişmez dizesi yerine kaynak kodunuzda kullanabilmenizdir . Bu, daha az yazım hatası ve IDE'de daha fazla otomatik tamamlama anlamına gelir.


1
Kullanmak managed=Falseherhangi Permissions.can_search_blue_flowerbir nedenle kullanmanıza izin vermiyor mu?
Sam Bobel

@SamBobel evet, haklı olabilirsin. Sanırım geçen sefer "soyut" u denedim.
guettli

1

Bunun proxy modeliçin kukla içerik türüyle kullanabilirsiniz.

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)

Artık sadece nameve modelin codenameizniyle izin oluşturabilirsiniz CustomPermission.

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

Ayrıca şablonlarınızda bunun gibi yalnızca özel izinleri sorgulayabilir ve görüntüleyebilirsiniz.

 CustomPermission.objects.filter(content_type__model='custom permission')
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.