2009-04-07 15 views
8

Estoy tratando de agregar un atributo Meta dinámico a todos mis modelos de Django usando la herencia del modelo, pero no puedo hacer que funcione. Tengo un permiso que quiero añadir a todos mis modelos como este:Atributos de Meta dinámico para modelos de 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'),) 

He intentado crear una clase base abstracta como esto:

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): 
    ... 

pero no funciona. ¿Es este el enfoque correcto? Debido a que Django utiliza la introspección para construir las clases de modelo, no estoy seguro de si agregar permisos durante el __init__() de la clase incluso funcionará. Con mi implementación actual cada vez que accedo a una instancia de modelo, agrega otra tupla de permisos.

Respuesta

23

Su instinto es correcto que esto no funcionará. En Django, los permisos se almacenan en la base de datos, lo que significa que:

  • Tienen que estar disponibles a nivel de clase cuando syncdb se ejecuta con el fin de completar la tabla auth_permission (y su enfoque requiere una instancia, que ganó no se realizará durante syncdb)
  • aunque lo haya agregado a _meta.permissions en __init__, el objeto User no lo recogió en ninguna llamada de verificación de permisos porque esos consultan la tabla de permisos en el DB (y un caché de ese mesa, en eso).

Lo que realmente necesita aquí es una metaclase ... su objetivo no se puede lograr utilizando la herencia. La metaclase vuelve a escribir sus definiciones de clase ModelA y ModelB dinámicamente antes de definirse, por lo que no requiere una instancia ModelA, y está disponible para syncdb. Dado que los modelos de Django también usan metaclases para construir el objeto Meta en primer lugar, el único requisito es que la metaclase debe heredar de la misma metaclase que los modelos de Django. He aquí algunos ejemplos de código:

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) 

Tenga en cuenta que los permisos en este caso serán escritos sólo en syncdb. Si necesita cambiar los permisos dinámicamente en la base de tiempo de ejecución en el usuario, querrá proporcionar su propio authentication backend.

+3

+1 para un ejemplo de escritura de una metaclase costumbre en una pregunta relacionada con Django. esto es difícil de encontrar –

+0

Sé que esto es para lo que son los votos expresos, y este es un hilo viejo, pero ... maldición, Jarret. El nivel enciclopédico de conocimiento de Django y Python necesitaba dar con esta respuesta y luego explicar de manera simple y clara es alucinante. Gracias por hacer de Internet un lugar más útil. – jfmatt

+0

No sé quién es usted, @General_Mayhem, pero acaba de hacer mi año. ¡Gracias por hacer buenos comentarios en una publicación tan antigua! –

0

intenta utilizar un gestor de encargo:

#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() 

uso como esto:

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

olvidé mencionar: esto solo funciona si la estructura de su tabla es la misma para cada tabla. – rexus

Cuestiones relacionadas