如果字段的选择列表发生更改,则停止 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 管理员更改列表优化查询:选择 field1,field2 而不是 select *
Django - 如果国家/地区字段等于美国,则发送 ajax 请求以将状态选择器设置为美国各州的元组
Django admin - 在显示的用户列表中添加一个字段