Làm cách nào để sử dụng quyền Django mà không cần xác định kiểu nội dung hoặc mô hình?


84

Tôi muốn sử dụng hệ thống dựa trên quyền để hạn chế các hành động nhất định trong ứng dụng Django của mình. Những hành động này không cần thiết phải liên quan đến một mô hình cụ thể (ví dụ: truy cập vào các phần trong ứng dụng, tìm kiếm ...), vì vậy tôi không thể sử dụng khung cấp quyền chứng khoán trực tiếp, vì Permissionmô hình yêu cầu tham chiếu đến loại nội dung đã cài đặt.

Tôi có thể viết mô hình quyền của riêng mình nhưng sau đó tôi phải viết lại tất cả các tính năng bổ sung đi kèm với các quyền của Django, chẳng hạn như:

Tôi đã kiểm tra một số ứng dụng như django-Authoritydjango-Guardian , nhưng chúng dường như cung cấp quyền nhiều hơn cùng với hệ thống mô hình, bằng cách cho phép quyền đối với từng đối tượng.

Có cách nào để sử dụng lại khuôn khổ này mà không cần xác định bất kỳ mô hình nào (ngoài UserGroup) cho dự án không?

Câu trả lời:


57

PermissionMô hình của Django yêu cầu một ContentTypephiên bản .

Tôi nghĩ một cách để giải quyết vấn đề đó là tạo một hình nộm ContentTypekhông liên quan đến bất kỳ mô hình nào (trường app_labelmodelcó thể được đặt thành bất kỳ giá trị chuỗi nào).

Nếu bạn muốn tất cả đều sạch sẽ và đẹp đẽ, bạn có thể tạo một Permission mô hình proxy xử lý tất cả các chi tiết xấu xí của hình nộm ContentTypevà tạo các phiên bản quyền "không có mô-đun". Bạn cũng có thể thêm trình quản lý tùy chỉnh để lọc ra tất cả các Permissiontrường hợp liên quan đến mô hình thực.


3
Nếu bạn không phiền, tôi sẽ hoàn thành câu trả lời của bạn bằng cách triển khai của tôi.
Chewie 19/12/12

Rất tiếc, tôi không thể phê duyệt vì tôi không có đủ danh tiếng để xem xét bản chỉnh sửa của bạn (nó yêu cầu tôi + 2k). Những người dùng khác đang từ chối các chỉnh sửa của bạn, vì vậy tôi khuyên bạn nên thêm nó như một câu trả lời khác (bạn có ủng hộ của tôi!) Cảm ơn một lần nữa.
Gonzalo

1
Lạ nhỉ. Nó thực sự là một sự hoàn thiện cho câu trả lời của bạn, vì vậy bạn nên chỉnh sửa nó. Dù sao, tôi đặt nó trong một câu trả lời khác.
Chewie 19/12/12

138

Đối với những bạn vẫn đang tìm kiếm:

Bạn có thể tạo một mô hình phụ trợ mà không có bảng cơ sở dữ liệu. Mô hình đó có thể mang lại cho dự án của bạn bất kỳ sự cho phép nào bạn cần. Không cần phải xử lý ContentType hoặc tạo các đối tượng Quyền một cách rõ ràng.

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

Ngay sau đó manage.py makemigrationsmanage.py migratebạn có thể sử dụng các quyền này như bất kỳ quyền nào khác.

# 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
đó là thiên tài, hãy cứu lấy ngày của tôi!
Reorx

2
Không có gì thay đổi sau khi tôi chạy management.py di chuyển ... Tôi không thấy bất kỳ quyền mới nào :(
Agey

2
Bạn đã thêm ứng dụng vào dự án của mình (INSTALLED_APPS) chưa?
Dmitry

2
Câu trả lời này là hoàn hảo. Tôi cũng [] chỉnh sửa default_permissions, tăng NotImplementedError trên save () của mô hình và có thể cân nhắc việc đặt _ * _ allow () trả về False nếu mô hình không được quản lý thực sự CHỈ cho quyền này.
Douglas Denhartog

4
Tôi đề nghị bổ sung thêm trong lớp Meta như sau: default_permissions = (). Điều này sẽ ngăn Django tự động tạo các quyền thêm / thay đổi / xóa / xem mặc định cho mô hình này, rất có thể là không cần thiết nếu bạn đang sử dụng phương pháp này.
Jordan

51

Theo lời khuyên của Gonzalo , tôi đã sử dụng mô hình proxy và trình quản lý tùy chỉnh để xử lý các quyền "không cần modell" của tôi với loại nội dung giả.

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
cảm ơn vì mã, sẽ rất vui nếu bạn cũng cho thấy một ví dụ về cách sử dụng mã này.
Ken Cochrane

2
sự cho phép mô hình đó nên sống ở đâu?
Mirat Can Bayrak

4
Để tạo GlobalPermission: từ app.models import GlobalPermission gp = GlobalPermission.objects.create (codename = 'can_do_it', name = 'Can do it') Sau khi điều này được chạy, bạn có thể thêm quyền đó cho người dùng / nhóm giống như bất kỳ quyền nào khác .
Julien Grenier

3
@JulienGrenier Mã lỗi trong Django 1.8 : FieldError: Cannot resolve keyword 'name' into field. Choices are: app_label, id, logentry, model, permission.
maciek

2
Cảnh báo: Phiên bản mới hơn của Django (ít nhất là 1.10) cần phải ghi đè lên các phương pháp "get_queryset" (lưu ý sự thiếu _ giữa các từ "truy vấn" và "thiết lập).
Lobe

10

Sửa lỗi cho câu trả lời của Chewie trong Django 1.8, như được yêu cầu trong một vài nhận xét.

Nó nói trong ghi chú phát hành:

Trường tên của django.contrib.contenttypes.models.ContentType đã bị di chuyển loại bỏ và thay thế bằng thuộc tính. Điều đó có nghĩa là không thể truy vấn hoặc lọc ContentType theo trường này nữa.

Vì vậy, đó là 'tên' được tham chiếu trong ContentType mà không sử dụng trong GlobalPermissions.

Khi tôi sửa nó, tôi nhận được như sau:

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)

Lớp GlobalPermissionManager không thay đổi nhưng được bao gồm để hoàn thiện.


1
Điều này vẫn không khắc phục được nó cho django 1.8 vì trong thời gian của syncdb django khẳng định rằng trường "tên" không thể trống.
Armita

Nó hoạt động với tôi, nhưng tôi không sử dụng di chuyển do nội dung kế thừa không phải django vẫn còn trong dự án của tôi. Bạn đang nâng cấp từ một django trước, vì có không phải là một trường tên trong 1,8
rgammans

4

Đây là giải pháp thay thế. Đầu tiên hãy tự hỏi bản thân: Tại sao không tạo một Mô hình giả thực sự tồn tại trong DB nhưng không bao giờ được sử dụng, ngoại trừ việc giữ quyền? Điều đó không hay, nhưng tôi nghĩ đó là giải pháp hợp lệ và thẳng thắn.

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

Giải pháp trên có lợi ích là bạn có thể sử dụng biến Permissions.can_search_blue_flowertrong mã nguồn của mình thay vì sử dụng chuỗi chữ "my_app.can_search_blue_flower". Điều này có nghĩa là ít lỗi chính tả hơn và nhiều tự động điền hơn trong IDE.


1
Sử dụng managed=Falsekhông cho phép bạn sử dụng Permissions.can_search_blue_flowervì một số lý do?
Sam Bobel

@SamBobel vâng, bạn có thể đúng. Tôi đoán tôi vừa thử "trừu tượng" lần trước.
guettli

1

Bạn có thể sử dụng proxy modelcho việc này với một loại nội dung giả.

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)

Bây giờ bạn có thể tạo quyền chỉ với namecodenamequyền từ CustomPermissionmô hình.

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

Và bạn có thể truy vấn và chỉ hiển thị các quyền tùy chỉnh trong các mẫu của mình như thế này.

 CustomPermission.objects.filter(content_type__model='custom permission')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.