如果字段的选择列表发生更改,则停止 Django 创建迁移

Posted

技术标签:

【中文标题】如果字段的选择列表发生更改,则停止 Django 创建迁移【英文标题】:Stop Django from creating migrations if the list of choices of a field changes 【发布时间】:2015-10-25 15:05:03 【问题描述】:

我有一个名为“foocore”的 django 核心应用程序。

有几个可选的类似插件的应用程序。例如“superfoo”。

在我的例子中,每个插件都在属于“foocore”的模型 CharField 中添加了一个新选择。

如果选项列表发生更改,Django 迁移会检测更改。

我认为这没有必要。至少有一位其他开发者也这么认为:

https://code.djangoproject.com/ticket/22837

class ActivePlugin(models.Model):
    plugin_name = models.CharField(max_length=32, choices=get_active_plugins())

获取选项的代码:

class get_active_plugins(object):
    def __iter__(self):
        for item in ....:
            yield item

核心“foocore”在多个项目中使用,每个安装都有一组不同的插件。 Django 试图创建无用的迁移 ....

有没有办法解决这个问题?

【问题讨论】:

是的,这是一个糟糕的功能。我感觉到你的痛苦。 【参考方案1】:

有关更多信息,请参阅此错误报告和讨论:https://code.djangoproject.com/ticket/22837

建议的解决方案是使用可调用对象作为选择的参数,但似乎这并未针对字段执行,而仅针对表单执行。

如果您真的需要动态选择,那么ForeignKey 是最好的解决方案。

另一种解决方案是通过字段的自定义清理方法和/或创建自定义表单来添加要求。表单字段确实支持可调用的choices

有关更多信息,请参阅此答案:https://***.com/a/33514551/54017

【讨论】:

在这种情况下,您可能无法获得干净的解决方案。我只能想出一些技巧来解决你的问题......修改运行时的选择,如果sys.argv包含makemigrations,则对选择进行硬编码,等等。 我无法让它适用于模型选择,根据文档,它仅适用于 forms.ChoiceFields。尝试向模型选择添加可调用对象会引发 (fields.E004) 'choices' must be an iterable (e.g., a list or tuple). 这不适用于模型,仅适用于表单。见here。 @Andrew:你完全正确,我误读了原始错误报告和建议的解决方案。【参考方案2】:

我遇到了类似的问题。我的选择是动态的(从起点到现在的所有年份),并且每年第一次运行 makemigrations 时,它都会为新选择生成新的迁移。我找到的解决方案是自定义字段,这样makemigrations 就不会检测到choices 更改:

from django.db import models


class YearField(models.IntegerField):

    description = "A year from 2015 to the present"

    def deconstruct(self):
        name, path, args, kwargs = super(YearField, self).deconstruct()
        # Ignore choice changes when generating migrations
        kwargs.pop('choices', None)
        return (name, path, args, kwargs)

【讨论】:

这个解决方案很好但被低估了。我为自己创建了一个不迁移选项和默认值的 char 字段。我可以像使用任何其他 char 字段一样使用它,而不必创建自己的表单或元类 很好的解决方案。我只在 sys.argv 中添加了“if 'makemigrations':” 只是为了确保只有在进行迁移时选择才会消失。【参考方案3】:

我为具有相同一般结构的 Django 1.6 项目制作的自定义字段也遇到了类似的问题。我找到了以下可行的解决方案:

class ActivePluginMeta(ModelBase):
    def __new__(cls, name, bases, attrs):
        # Override choices attr
        cls = models.base.ModelBase.__new__(cls, name, bases, attrs)
        setattr(cls._meta.get_field('plugin_name'), 'choices', cls.plugin_name_choices)
        return cls

class ActivePlugin(models.Model, metaclass=ActivePluginMeta):
    plugin_name_choices = get_active_plugins()
    plugin_name = models.CharField(max_length=32, choices=[])

对于python 3,对于python 2,您必须指定元类,如下所示:

class ActivePlugin(models.Model):
    __metaclass__ = ActivePluginMeta

    plugin_name_choices = get_active_plugins()
    plugin_name = models.CharField(max_length=32, choices=[])

【讨论】:

+1 表示聪明,但遗憾的是,即使这样也不再适用(无论如何是 Django 1.10)。我似乎 Django 现在在进行迁移之前实例化了模型。 =(

以上是关于如果字段的选择列表发生更改,则停止 Django 创建迁移的主要内容,如果未能解决你的问题,请参考以下文章

Django - 发送有关模型更改的电子邮件

Django 管理员更改列表优化查询:选择 field1,field2 而不是 select *

Django - 如果国家/地区字段等于美国,则发送 ajax 请求以将状态选择器设置为美国各州的元组

Django admin - 在显示的用户列表中添加一个字段

根据另一个字段中的选择显示/隐藏 django 管理表单字段

在 Django 中,根据模型中其他字段中选择的值删除选择字段下拉列表中的选项