2012-12-18 48 views
53

我想使用基於權限的系統來限制我的Django應用程序中的某些操作。這些操作不需要與特定模型相關(例如,訪問應用程序中的各個部分,搜索...),所以我不能直接使用stock permissions framework,因爲Permission模型需要對已安裝內容類型的引用。如何在不定義內容類型或模型的情況下使用Django權限?

我可以寫我自己的權限模型,但後來我不得不重寫所有附帶的Django權限的好東西,如:

我查了一些應用程序,如django-authoritydjango-guardian,但他們似乎提供更多的耦合到模型系統權限,允許每個對象的權限。

有沒有一種方法可以重用這個框架,而沒有爲項目定義任何模型(除了UserGroup)?

回答

37

Django的Permission模型requires a ContentType instance

我認爲一個方法是創建一個與任何模型無關的模擬ContentTypeapp_labelmodel字段可以設置爲任何字符串值)。

如果你希望它全部乾淨漂亮,你可以創建一個Permissionproxy model,它處理虛擬ContentType的所有醜陋的細節,並創建「無模型」權限實例。您還可以添加一個自定義管理器,用於過濾掉與真實模型相關的所有Permission實例。

+0

好棒的想法,像魅力一樣工作! – Chewie

+2

如果你不介意,我會在我的實施中完成你的答案。 – Chewie

+0

那真是太棒了Chewie,謝謝! –

44

以下Gonzalo's advice,我用proxy modelcustom manager來處理我的「模型」權限與虛擬內容類型。

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

模型許可應該在哪裏生活? –

+0

@MiratCanBayrak,我相信它假設你的核心應用程序的某個地方居住。 – Pol

1

相反編寫和運行該代碼插入記錄到數據庫中,你可以只插入記錄到數據庫中(當然編輯主鍵和外鍵需要)

insert into django_content_type(id,name,app_label,model) values (22,'app_permission','myapp','app_permission'); 
insert into auth_permission(id,name,content_type_id,codename) values (64,'Is Staff Member',22,'staff_member'); 

然後在您的應用程序管理員您可以將「職員」分配給您的用戶或組。要檢查你的班級的這種權限,你會寫

from django.contrib.auth.decorators import permission_required 
from django.utils.decorators import method_decorator 
from django.views.generic import TemplateView 

class MyClass(TemplateView): 
    template_name = myapp/index.html' 

    @method_decorator(permission_required(['myapp.staff_member'],raise_exception=True)) 
    def dispatch(self, *args, **kwargs): 
     return super(MyClass, self).dispatch(*args, **kwargs) 
+0

這應該不是理想的方式。 – Shrey

7

修復了在Django 1.8中的Chewie的答案,這在一些評論中已經被要求。

它說,在發行說明:

The name field of django.contrib.contenttypes.models.ContentType has been removed by a migration and replaced by a property. That means it’s not possible to query or filter a ContentType by this field any longer.

所以它在contentType中引用的「名稱」的用途並不GlobalPermissions。

當我解決它,我得到以下幾點:

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

這仍然不能解決它的django 1.8在syncdb的時間django聲稱「名稱」字段不能爲空 – Armita

+0

它爲我工作,但我沒有使用遷移由於非django傳統東西仍然在我的項目中,你是從以前的django升級,因爲在1.8 – rgammans

51

對於那些你,誰仍在尋找:

您可以創建一個沒有數據庫表中的一個輔助模型。該模型可以爲您的項目帶來任何您需要的權限。不需要處理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. 

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

manage.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_rigths %} 
    <p>You can do any customer stuff</p> 
{% endif %} 
+0

這不應該是一個名稱字段,這是天才,拯救我的一天! – Reorx

+1

我跑完管理後沒有改變。py遷移...我沒有看到任何新的權限:( – Agey

+1

您是否將您的應用程序添加到您的項目(INSTALLED_APPS)? – Dmitry

1

這是替代解決方案。首先問問自己:爲什麼不創建一個真正存在於數據庫中的Dummy-Model,但永遠不會被使用,除了持有權限?這並不好,但我認爲這是有效和直接的解決方案。

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中。

+0

使用'managed = False'是否讓某些原因使用'Permissions.can_search_blue_flower'? –

+0

@SamBobel是的,你可能是對的,我想我只是最後一次嘗試「抽象」。 – guettli

相關問題