2009-04-07 19 views
8

Ich versuche, allen meinen Django-Modellen mithilfe der Modellvererbung ein dynamisches Meta-Attribut hinzuzufügen, aber ich kann es nicht zum Laufen bringen. Ich habe eine Erlaubnis, die ich so an alle meine Modelle hinzufügen möchten:Dynamische Meta-Attribute für Django-Modelle?

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

Ich habe versucht, eine abstrakte Basisklasse wie folgt zu erstellen:

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

aber es funktioniert nicht. Ist das der richtige Ansatz? Da Django die Modellklassen mit Introspektion erstellt, bin ich mir nicht sicher, ob das Hinzufügen von Berechtigungen während der Klasse __init__() überhaupt möglich ist. Mit meiner aktuellen Implementierung hängt jedes Mal, wenn ich auf eine Modellinstanz zugreife, ein weiteres Tupel der Berechtigungen an.

Antwort

23

Ihr Instinkt ist richtig, dass dies nicht funktioniert. In Django, Berechtigungen werden in der Datenbank gespeichert sind, was bedeutet, dass:

  • sie auch in der Klasse verfügbar sein müssen, wenn syncdb um läuft die auth_permission Tabelle zu füllen (und Ihr Ansatz erfordert eine Instanz, die gewonnen Wird nicht während der syncdb gemacht.
  • Auch wenn Sie es zu _meta.permissions in __init__ hinzugefügt haben, das User Objekt würde es nicht in irgendeiner Erlaubnis Checkaufrufe abholen, weil diese die Berechtigungstabelle in der DB konsultieren (und einen Cache davon Tisch, bei dem).

Was Sie hier eigentlich brauchen, ist eine Metaklasse ... Ihr Ziel kann nicht durch Vererbung erreicht werden. Die Metaklasse schreibt Ihre Klassendefinitionen ModelA und ModelB dynamisch um, bevor sie definiert werden. Daher ist keine Instanz ModelA erforderlich, und sie ist für syncdb verfügbar. Da Djangos Modelle auch Metaklassen verwenden, um das Objekt Meta zu erstellen, ist die einzige Voraussetzung, dass Ihre Metaklasse von derselben Metaklasse wie Djangos Modelle erben muss. Hier ist ein Beispielcode:

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) 

Beachten Sie, dass Berechtigungen in diesem Fall nur auf syncdb geschrieben werden. Wenn Sie Berechtigungen dynamisch zur Laufzeit basierend auf dem Benutzer ändern müssen, sollten Sie Ihre eigenen angeben.

+3

+1 für ein Beispiel einer benutzerdefinierten metaclass in einer django bezogene Frage des Schreibens. Dieses Zeug ist schwer zu finden! –

+0

Ich weiß, das ist was upvotes sind, und das ist ein alter Thread, aber ... verdammt, Jarret. Der enzyklopädische Kenntnisstand von Django und Python musste mit dieser Antwort aufkommen und dann einfach und klar erklären, dass sie irrsinnig ist. Danke, dass Sie das Internet zu einem nützlicheren Ort gemacht haben. – jfmatt

+0

Ich weiß nicht, wer du bist, @General_Mayhem, aber du hast gerade mein Jahr gemacht. Danke, dass du nette Kommentare zu solch einem alten Beitrag geschrieben hast! –

0

versuchen, eine benutzerdefinierte Manager zu verwenden:

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

Verwendung wie folgt aus:

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

Ich habe vergessen zu erwähnen: dies funktioniert nur, wenn Ihre Tabellenstruktur die gleiche für jede Tabelle. – rexus