以下代码可以在 python 3.10 中运行,但由于
enum
模块的更改而无法在 3.11 中运行。
现在应用程序将无法启动,并显示以下错误消息:
File "/home/runner/work/e/e/certification/models.py", line 3, in <module>
from .certifications.models import Certification, QuizzCertification # noqa: F401
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/work/e/e/certification/certifications/models.py", line 17, in <module>
class CertificationType(models.TextChoices):
File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/site-packages/django/db/models/enums.py", line 49, in __new__
cls = super().__new__(metacls, classname, bases, classdict, **kwds)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/enum.py", line 560, in __new__
raise exc
File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/enum.py", line 259, in __set_name__
enum_member = enum_class._new_member_(enum_class, *args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/enum.py", line 1278, in __new__
raise TypeError('%r is not a string' % (values[0], ))
TypeError: <class 'certification.certifications.models.QuizzCertification'> is not a string
如何实现带有选择的模型字段,其中第一个元素是模型类?
class QuizzCertification(models.Model):
...
class OtherCertification(models.Model):
...
class CertificationType(models.TextChoices):
QUIZZ = QuizzCertification, "Quizz"
OTHER = OtherCertification, "Other"
class Certification(models.Model):
name = models.CharField(max_length=100, unique=True)
description = models.TextField(_("Description"), blank=True, null=True)
type_of = models.CharField(
_("Type of certification"),
max_length=100,
choices=CertificationType,
default=CertificationType.QUIZZ,
)
我最终创建了一个新的模型字段。我需要重写 _check_choices 方法,以避免由于选择不是字符串而在启动时出现错误,并且我添加了一个检查来验证所有选择是否都是有效的类名。
class ModelField(models.CharField):
"""Same as CharField but python object is a django.db.models.Model class."""
_choice_not_found_error = (
"Some model %s.%s choices saved in DB do not exist in the code. "
"Did you rename or delete a model? Incorrect choices: %s"
)
def from_db_value(self, value, *args):
"""Converts the value as it's loaded from the database."""
if value is None:
return value
return self._get_value_from_choices(value)
def to_python(self, value):
"""Converts the value into the correct Python object."""
if not isinstance(value, str):
return value
if value is None:
return value
return self._get_value_from_choices(value)
def get_prep_value(self, value):
"""Convert class to string value to be saved in the DB."""
return str(value)
def _check_choices(self):
"""Checks if all choices saved in the database exist in the code."""
model_names_from_choices = [str(choice[0][0]) for choice in self.choices]
wrong_models = self.model.objects.exclude(type_of__in=model_names_from_choices)
if wrong_models:
wrong_choices = [
choice
for model in wrong_models
for choice in getattr(model.choices)
if choice[0][0] in model_names_from_choices
]
formatted_wrong_choices = ", ".join(wrong_choices)
return [
checks.Error(
self._choice_not_found_error
% (self.model, self.attname, formatted_wrong_choices),
)
]
return []
def _get_value_from_choices(self, value: str):
"""
Retrieves the corresponding choice for the given value.
If choice is invalid, it raises a ValueError.
"""
try:
return next(choice for choice in self.choices if str(choice[0][0]) == value)
except StopIteration as error:
raise ValueError(
self._choice_not_found_error % (self.model, self.attname, value)
) from error
我可以这样使用它:
class CertificationType(models.Choices):
QUIZZ = QuizzCertification, _("Quizz")
class Certification(models.Model):
name = models.CharField(max_length=100, unique=True)
type_of = ModelField(
_("Type of certification"),
max_length=100,
choices=CertificationType,
default=CertificationType.QUIZZ,
)