Как сослаться на внутренний класс или атрибут до того, как он будет полностью определен?
У меня есть сценарий, в котором класс содержит внутренний класс, и я хочу ссылаться на этот внутренний класс (или его атрибуты) во внешнем классе. Вот конкретный пример с использованием Django:
from django.db import models
from django.utils.translation import gettext_lazy as _
class DummyModel(models.Model):
class StatusChoices(models.TextChoices):
ACTIVE = "active", _("Active")
INACTIVE = "inactive", _("Inactive")
status = models.CharField(
max_length=15,
choices=StatusChoices.choices,
verbose_name=_("Status"),
help_text=_("Current status of the model."),
default=StatusChoices.ACTIVE,
null=False,
blank=False,
)
class Meta:
verbose_name = _("Dummy Model")
verbose_name_plural = _("Dummy Models")
constraints = [
models.CheckConstraint(
name="%(app_label)s_%(class)s_status_valid",
check=models.Q(status__in=[choice.value for choice in DummyModel.StatusChoices]),
)
]
В этом случае список ограничений в классе Meta пытается сослаться на DummyModel.StatusChoices
. Однако на момент оценки этой ссылки DummyModel
не полностью определен, что приводит к ошибке (ни StatusChoices
не доступен в этой строке).
Я хотел бы решить эту проблему без существенного изменения структуры кода -StatusChoices
должен оставаться определенным внутри DummyModel.
Как решить эту проблему, сохранив доступ к внутреннему классу и его атрибутам, как и предполагалось?
Python оценивает класс Meta
в момент его определения, но класс DummyModel
еще не полностью определен. Поэтому обращение к DummyModel.StatusChoices
внутри класса Meta
приводит к ошибке.
Вероятно, вы можете обойтись без использования функции classmethod
.
Определите вспомогательный метод для получения вариантов выбора:
class DummyModel(models.Model):
class StatusChoices(models.TextChoices):
ACTIVE = "active", _("Active")
INACTIVE = "inactive", _("Inactive")
status = models.CharField(
max_length=15,
choices=StatusChoices.choices,
verbose_name=_("Status"),
help_text=_("Current status of the model."),
default=StatusChoices.ACTIVE,
null=False,
blank=False,
)
@classmethod
def valid_status_choices(cls):
return [choice.value for choice in cls.StatusChoices]
class Meta:
verbose_name = _("Dummy Model")
verbose_name_plural = _("Dummy Models")
constraints = [
models.CheckConstraint(
name="%(app_label)s_%(class)s_status_valid",
# Use the classmethod for valid choices
check=models.Q(status__in=DummyModel.valid_status_choices()),
)
]
Это позволяет избежать использования lambda
и вместо этого оценивает его позже, так что класс уже определен при вызове.
Вероятно, вы можете сделать это, определив сначала варианты вне класса, потому что Meta
класс фактически строится еще до того, как status
становится доступным:
# 🖟 outside DummyModel
class StatusChoices(models.TextChoices):
ACTIVE = 'active', _('Active')
INACTIVE = 'inactive', _('Inactive')
class DummyModel(models.Model):
status = models.CharField(
max_length=15,
choices=StatusChoices.choices,
verbose_name=_('Status'),
help_text=_('Current status of the model.'),
default=StatusChoices.ACTIVE,
null=False,
blank=False,
)
class Meta:
verbose_name = _('Dummy Model')
verbose_name_plural = _('Dummy Models')
constraints = [
models.CheckConstraint(
name='%(app_label)s_%(class)s_status_valid',
check=models.Q(
status__in=[choice.value for choice in StatusChoices]
),
)
]
DummyModel.StatusChoices = StatusChoices
Если уж на то пошло, я сделал небольшой пакет Django под названием django-enforced-choices
[GitHub], который может принудительно реализовать выбор в базе данных, просто просматривая поле с выбором.