带有类选择的 Django Choices 模型字段

问题描述 投票:0回答:1

以下代码可以在 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,
    )
python django django-models enums
1个回答
0
投票

我最终创建了一个新的模型字段。我需要重写 _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,
    )
© www.soinside.com 2019 - 2024. All rights reserved.