Dynamic Meta attributes for Django Models?
Your instinct is right that this won't work. In Django, permissions are stored in the database, which means that:
- they need to be available at the class level when syncdb is run in order to populate the auth_permission table (and your approach requires an instance, which won't be made during syncdb)
- even if you did add it to
_meta.permissions
in__init__
, theUser
object wouldn't pick it up in any permission check calls because those consult the permissions table in the DB (and a cache of that table, at that).
Your goal can't be accomplished using inheritance. What you actually need here is a Python metaclass.
This metaclass re-writes your ModelA
and ModelB
class definitions dynamically before they are defined, thus it doesn't require a ModelA
instance, and is available to syncdb. Since Django's models also use metaclasses to build the Meta
object in the first place, the only requirement is that your metaclass must inherit from the same metaclass as Django's models.
Here's some sample code (Python 2):
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_{0.module_name}'.format(klas._meta),
u'Can view {0.verbose_name}'.format(klas._meta))
)
return klas
class ModelA(models.Model):
__metaclass__ = CustomModelMetaClass
test = models.CharField(max_length=5)
Python 3:
from django.db.models.base import ModelBase
class CustomModelMetaClass(ModelBase):
def __new__(cls, name, bases, attrs):
klas = super().__new__(cls, name, bases, attrs)
klas._meta.permissions.append(
(
'view_{0.module_name}'.format(klas._meta),
'Can view {0.verbose_name}'.format(klas._meta))
)
return klas
class ModelA(models.Model, metaclass=CustomModelMetaClass):
test = models.CharField(max_length=5)
Note that permissions in this case will be written only on migrate
. If you need to change permissions dynamically at run time base on the user, you'll want to provide your own authentication backend.
Try to use a custom manager:
#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()
use like this:
f = DummyTable.objects.all('my_table_name')