I am trying to add a dynamic Meta attribute to all of my Django models using model inheritance, but I can't get it to work. I have a permission that I want to add to all my models like this:
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'),)
I tried creating an abstract base class like this:
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):
...
but it's not working. Is this the right approach? Because Django uses introspection to construct the Model classes, I'm not sure if adding permissions during the __init__()
of the class will even work. With my current implementation every time I access a model instance it appends another tuple of the permissions.
Dynamic Django models allow users to define, edit, and populate their own database tables and apply runtime schema changes to the database. django-dynamic-models is loosely based on the runtime dynamic models talk from DjangoCon 2011.
Model Meta is basically the inner class of your model class. Model Meta is basically used to change the behavior of your model fields like changing order options,verbose_name, and a lot of other options. It's completely optional to add a Meta class to your model.
This document of django Model metadata is “anything that's not a field”, such as ordering options (ordering), database table name (db_table), or human-readable singular and plural names (verbose_name and verbose_name_plural) .
In Django , a model field pertains to a column in the database. On the other hand, a model attribute pertains to a method or property that is added to a model .
Your instinct is right that this won't work. In Django, permissions are stored in the database, which means that:
_meta.permissions
in __init__
, the User
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')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With