2009-04-07 34 views
8

Tôi đang cố gắng thêm một thuộc tính Meta động cho tất cả các mô hình Django của tôi bằng cách sử dụng mô hình kế thừa, nhưng tôi không thể làm cho nó hoạt động. Tôi có một phép mà tôi muốn thêm vào tất cả các mô hình của tôi như thế này:Thuộc tính Meta động cho Mô hình Django?

class ModelA(models.Model): 
    class Meta: 
     permisssions =(('view_modela','Can view Model A'),) 

class ModelB(models.Model): 
    class Meta: 
     permisssions =(('view_modelb','Can view Model B'),) 

Tôi đã cố gắng tạo ra một lớp cơ sở trừu tượng như thế này:

class CustomModel(models.Model): 
    def __init__(self, *args, **kwargs): 
     self._meta.permissions.append(('view_'+self._meta.module_name, u'Can view %s' % self._meta.verbose_name)) 
     super(CustomModel,self).__init__(*args, **kwargs) 

class ModelA(CustomModel): 
    .... 

class ModelB(CustomModel): 
    ... 

nhưng nó không làm việc. Đây có phải là cách tiếp cận đúng? Bởi vì Django sử dụng nội tâm để xây dựng các lớp Mô hình, tôi không chắc chắn nếu thêm quyền trong các __init__() của lớp thậm chí sẽ làm việc. Với việc thực hiện hiện tại của tôi mỗi khi tôi truy cập một cá thể mô hình, nó nối thêm một bộ các quyền khác.

Trả lời

23

Bản năng của bạn là đúng rằng điều này sẽ không hoạt động. Trong Django, các quyền được lưu trữ trong cơ sở dữ liệu, có nghĩa là:

  • chúng cần phải có sẵn ở cấp lớp khi syncdb được chạy để điền bảng auth_permission (và cách tiếp cận của bạn yêu cầu một cá thể không được thực hiện trong syncdb)
  • ngay cả khi bạn đã thêm nó vào _meta.permissions trong __init__, đối tượng User sẽ không nhận nó trong bất kỳ cuộc gọi kiểm tra quyền nào vì những người tham khảo bảng quyền trong DB (và bộ nhớ cache của bảng, lúc đó).

Điều bạn thực sự cần ở đây là metaclass ... không thể hoàn thành mục tiêu của bạn bằng cách sử dụng thừa kế. Các metaclass viết lại các định nghĩa lớp ModelAModelB động của bạn trước khi chúng được xác định, do đó nó không yêu cầu một cá thể ModelA và có sẵn cho syncdb. Kể từ khi mô hình của Django cũng sử dụng metaclasses để xây dựng các đối tượng Meta ở nơi đầu tiên, yêu cầu duy nhất là metaclass của bạn phải thừa hưởng từ cùng một metaclass như mô hình của Django. Dưới đây là một số mã mẫu:

from django.db.models.base import ModelBase 

class CustomModelMetaClass(ModelBase): 

    def __new__(cls, name, bases, attrs): 
     klas = super(CustomModelMetaClass, cls).__new__(cls, name, bases, attrs) 
     klas._meta.permissions.append(('view_' + klas._meta.module_name, u'Can view %s' % klas._meta.verbose_name)) 

     return klas 

class ModelA(models.Model): 

    __metaclass__ = CustomModelMetaClass 

    test = models.CharField(max_length=5) 

Lưu ý rằng quyền trong trường hợp này sẽ chỉ được viết trên syncdb. Nếu bạn cần thay đổi quyền trong thời gian chạy trên cơ sở người dùng, bạn sẽ muốn cung cấp cho riêng mình authentication backend.

+3

+1 cho một ví dụ về cách viết một metaclass tùy chỉnh trong một câu hỏi django liên quan. công cụ này khó tìm! –

+0

Tôi biết đây là những gì upvotes là cho, và đây là một chủ đề cũ, nhưng ... damn, Jarret. Mức độ kiến ​​thức bách khoa của Django và Python cần thiết để đưa ra câu trả lời này và sau đó giải thích đơn giản và rõ ràng là tâm-boggling. Cảm ơn bạn đã làm cho Internet trở thành một nơi hữu ích hơn. – jfmatt

+0

Tôi không biết bạn là ai, @General_Mayhem, nhưng bạn đã thực hiện năm của tôi. Cảm ơn bạn đã đưa ra nhận xét tốt đẹp về bài đăng cũ như vậy! –

0

Cố gắng sử dụng một trình quản lý tùy chỉnh:

#create a custom manager 
class DynTableNameManager(models.Manager): 
    #overwrite all() (example) 
    #provide table_name 
    def all(self, table_name): 
     from django.db import connection 
     cursor = connection.cursor() 
     cursor.execute(""" 
      SELECT id, name 
      FROM %s 
      """ % table_name) 
     result_list = [] 
     for row in cursor.fetchall(): 
      p = self.model(id=row[0], name=row[1]) 
      result_list.append(p) 
     return result_list 

#cerate a dummy table 
class DummyTable(models.Model): 
    name = models.CharField (max_length = 200) 
    objects = DynTableNameManager() 

sử dụng như thế này:

f = DummyTable.objects.all('my_table_name') 
+0

tôi quên đề cập đến: điều này chỉ hoạt động nếu cấu trúc bảng của bạn giống nhau cho mỗi bảng. – rexus

Các vấn đề liên quan