Adding django-rules
to my Django project I am encountering the following issue: I want to add default permissions to my abstract base class and overwrite them, if needed. Below an example of a base class and the inheriting class:
class BaseModel(RulesModelBaseMixin):
company_id = models.ForeignKey('company.Company', on_delete=models.CASCADE)
created_by = models.ForeignKey(
'user.User', on_delete=models.SET_NULL, null=True, blank=True)
class Meta:
abstract = True
rules_permissions = {
"can_create": can_create_in_company | is_superuser,
"can_view": can_view_in_company | is_author | is_superuser
}
class Ticket(RulesModelMixin, BaseModel, metaclass=RulesModelBase):
name = models.CharField(max_length=512, null=True, blank=True)
After adding this abstract base class, there is this seemingly unrelated error message:
Traceback (most recent call last):
File "REPO_PATH/.venv/lib/python3.10/site-packages/django/utils/module_loading.py", line 30, in import_string
return cached_import(module_path, class_name)
File "REPO_PATH/.venv/lib/python3.10/site-packages/django/utils/module_loading.py", line 15, in cached_import
import_module(module_path)
File "/opt/homebrew/Cellar/[email protected]/3.10.4/Frameworks/Python.framework/Versions/3.10/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 883, in exec_module
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "REPO_PATH/app/api/pagination.py", line 10, in <module>
from ticket.models import Ticket, TicketStatus, TicketType
File "REPO_PATH/app/ticket/models.py", line 106, in <module>
class Ticket(RulesModelMixin, BaseModel, metaclass=RulesModelBase):
File "REPO_PATH/.venv/lib/python3.10/site-packages/rules/contrib/models.py", line 36, in __new__
new_class._meta.rules_permissions = perms
AttributeError: type object 'Ticket' has no attribute '_meta'. Did you mean: 'Meta'?
It seems to have something to do with the custom pagination later on, but I don't think that this is the cause of the problem, as this worked before. The base class case is even mentioned in the docs, but it doesn't work.
BaseModel
should not subclass RulesModelBaseMixin
, which is used as a metaclass
. BaseModel
doesn't need metaclass=RulesModelBaseMixin
though, since Ticket
has metaclass=RulesModelBase
, and RulesModelBase
subclasses RulesModelBaseMixin
.BaseModel
should subclass Model
since it defines model fields meant to be inherited. Model
also provides a lot of functionality that you might expect.Ticket
needs to subclass a class that has metaclass=ModelBase
(e.g. Model
) to get _meta
set (the error shown in the question). Since Ticket
subclasses BaseModel
, which we will change to subclass Model
, it does not need to explicitly subclass Model
again.django-rules
does not support defining rules_permissions
in abstract model Meta
. You can implement preprocess_rules_permissions
class method to dynamically define that on subclasses. You also need to put BaseModel
before RulesModelMixin
in Ticket
to override that class method.# class BaseModel(RulesModelBaseMixin):
class BaseModel(Model):
...
class Meta:
abstract = True
# rules_permissions = {
# "can_create": can_create_in_company | is_superuser,
# "can_view": can_view_in_company | is_author | is_superuser
# }
@classmethod
def preprocess_rules_permissions(cls, perms):
perms.update({
"can_create": can_create_in_company | is_superuser,
"can_view": can_view_in_company | is_author | is_superuser,
})
# class Ticket(RulesModelMixin, BaseModel, metaclass=RulesModelBase):
class Ticket(BaseModel, RulesModelMixin, metaclass=RulesModelBase):
...
You can simplify all that by making BaseModel
subclass RulesModel
:
class BaseModel(RulesModel):
...
class Meta:
abstract = True
@classmethod
def preprocess_rules_permissions(cls, perms):
perms.update({
"can_create": can_create_in_company | is_superuser,
"can_view": can_view_in_company | is_author | is_superuser,
})
class Ticket(BaseModel):
...