【问题标题】:Django 2.2 cannot serialize default values once migration has been done迁移完成后,Django 2.2 无法序列化默认值
【发布时间】:2021-08-18 14:32:51
【问题描述】:

我有一个模型,它被称为外键,on_delete 设置为 SET_DEFAULT。因此,我需要为这个模型提供一个默认项目。我创建了一个静态方法来做这件事。

class ScheduleChoice(models.Model):
    """
    This model allows users to define crontab schedules.
    """
    label = models.CharField(max_length=256, verbose_name="Label", unique=True)
    job_schedule = models.CharField(
        max_length=256,
        default="0 0 * * *", verbose_name="Crontab"
    )

    @staticmethod
    def get_default_schedule_choice():
        """
        Because some models rely on ScheduleChoice and need a default value,
        we need to give them a default ScheduleChoice.
        """
        try:
            choice = ScheduleChoice.objects.get_or_create(
                label="Every minute",
                job_schedule="* * * * *"
            )
        except ProgrammingError:
            choice = None
        return choice

    @classmethod
    def get_choice_count(cls):
        """
        Returns how many schedule choices have been defined.
        """
        return len(cls.objects.all())

    class Meta:
        verbose_name = "schedule choice"
        verbose_name_plural = "schedule choices"

    def __str__(self):
        return self.label


class MyOtherModel(models.Model):
    """
    Model using ScheduleChoices.
    """
    job_schedule = models.ForeignKey(
        "ScheduleChoice",
        on_delete=models.SET_DEFAULT,
        default=ScheduleChoice.get_default_schedule_choice(),
        verbose_name="Schedule"
    )
    activated = models.BooleanField(default=False, verbose_name="activated")

我能够运行 makemigrations 并毫无问题地进行迁移。

当我修改模型并尝试再次使用 makemigrations 以更新迁移文件时,我的问题就开始了。我得到错误:

ValueError:无法序列化: 有些值 Django 无法序列化到迁移文件中。 更多内容请见https://docs.djangoproject.com/en/2.2/topics/migrations/#migration-serializing

我尝试申请this answer,但没有帮助。为什么 Django 需要序列化我的默认值?为什么只有在第一次迁移成功结束后才会这样做?

我总是可以使用 reset_db 进行迁移,但在我的生产环境中是不可接受的。

我该如何解决这个问题?

【问题讨论】:

    标签: python django django-migrations


    【解决方案1】:

    Django 需要序列化你的模型来为它们制作迁移文件。因此,它还需要序列化您在模型字段上设置的大部分属性(包括default)。目前,您定义一个方法并直接调用该方法,而不是提供该方法作为默认方法,您的方法还返回一个带有 ScheduleChoice 和布尔值的元组。

    Django 可以序列化布尔值,但不能序列化模型实例(用于迁移),因此您会收到错误,更不用说元组无论如何都会导致错误。您应该调用该方法,而只是将方法作为默认值传递,而不是返回实例,只返回pk,理想情况下,该方法应该只是一个函数:

    class ScheduleChoice(models.Model):
        """
        This model allows users to define crontab schedules.
        """
        label = models.CharField(max_length=256, verbose_name="Label", unique=True)
        job_schedule = models.CharField(
            max_length=256,
            default="0 0 * * *", verbose_name="Crontab"
        )
        
        @classmethod
        def get_choice_count(cls):
            """
            Returns how many schedule choices have been defined.
            """
            return len(cls.objects.all())
    
        class Meta:
            verbose_name = "schedule choice"
            verbose_name_plural = "schedule choices"
    
        def __str__(self):
            return self.label
    
    
    def get_default_schedule_choice():
        choice, _created = ScheduleChoice.objects.get_or_create(
            label="Every minute",
            job_schedule="* * * * *"
        )
        # Why would programming error occur?
        return choice.pk # Return the pk
    
    class MyOtherModel(models.Model):
        """
        Model using ScheduleChoices.
        """
        job_schedule = models.ForeignKey(
            "ScheduleChoice",
            on_delete=models.SET_DEFAULT,
            default=get_default_schedule_choice, # Don't call the function, only pass it
            verbose_name="Schedule"
        )
        activated = models.BooleanField(default=False, verbose_name="activated")
    

    【讨论】:

    • 这完全正确,谢谢。为什么它应该是一个函数?
    • @PleaseHelp 因为 1) 它不需要真的是一个方法。 2)如果是方法,一些老版本的Django可能无法序列化。
    猜你喜欢
    • 2011-06-24
    • 2017-07-11
    • 1970-01-01
    • 2019-01-28
    • 1970-01-01
    • 2014-01-25
    • 2020-07-26
    • 2012-04-17
    • 2016-10-06
    相关资源
    最近更新 更多