Правила Django с абстрактным базовым классом выдают ошибку после добавления
Пытаясь добавить Django-правила в свой проект Django, я столкнулся со следующей проблемой: У меня есть абстрактный базовый класс, который я использую для добавления пары общих ключей. Теперь я хочу добавить разрешения по умолчанию в абстрактный класс и при необходимости перезаписать их. Ниже приведены мой абстрактный базовый класс и пример подкласса.
class BaseModel(RulesModelBaseMixin):
company_id = models.ForeignKey('company.Company', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
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,
"can_edit": can_change_obj | is_author | is_superuser,
"can_delete": can_delete_obj | is_author | is_superuser,
}
class Ticket(RulesModelMixin, BaseModel, metaclass=RulesModelBase):
name = models.CharField(max_length=512, null=True, blank=True)
description = models.TextField(blank=True, null=True)
После добавления этой абстрактной базы я получаю сообщение об ошибке от Django:
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/python@3.10/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'?
Удаление нового кода подтвердило, что дело действительно в этом фрагменте. Похоже, что это как-то связано с пользовательской пагинацией, но я не думаю, что это причина проблемы, так как раньше все работало.
BaseModel
не должен подклассифицироватьRulesModelBaseMixin
, который используется какmetaclass
. ОднакоBaseModel
не нуждается вmetaclass=RulesModelBaseMixin
, посколькуTicket
имеетmetaclass=RulesModelBase
, аRulesModelBase
подклассыRulesModelBaseMixin
.BaseModel
должен быть подклассомModel
, поскольку он определяет поля модели, предназначенные для наследования.Model
также предоставляет много функциональных возможностей, которые вы могли бы ожидать.Ticket
должен подклассифицировать класс, имеющийmetaclass=ModelBase
(например,Model
), чтобы получить установленный_meta
(ошибка, показанная в вопросе). ПосколькуTicket
подклассыBaseModel
, которые мы изменим на подклассыModel
, ему не нужно явно подклассироватьModel
снова.django-rules
не поддерживает определениеrules_permissions
в абстрактной моделиMeta
. Вы можете реализовать метод классаpreprocess_rules_permissions
для динамического определения этого в подклассах. Вам также нужно поставитьBaseModel
передRulesModelMixin
вTicket
, чтобы переопределить этот метод класса.
# 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):
...
Вы можете упростить все это, сделав BaseModel
подклассом 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):
...