Stop Django from creating migrations if the list of choices of a field changes

余生长醉 提交于 2020-01-01 04:06:09

问题


I have a django core app called "foocore".

There are several optional pluging-like apps. For example "superfoo".

In my case every plugin adds a new choice in a model CharField which belongs to "foocore".

Django migrations detect changes if the list of choices get changed.

I think this is not necessary. At least one other developer thinks the same:

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

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

The code to get the choices:

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

The core "foocore" gets used in several projects and every installation has a different set of plugins. Django tries to create useless migrations ....

Is there a way to work around this?


回答1:


See this bug report and discussion for more info: https://code.djangoproject.com/ticket/22837

The proposed solution was to use a callable as the argument for choices, but it appears this has not been executed for fields but for forms only.

If you really need dynamic choices than a ForeignKey is the best solution.

An alternative solution can be to add the requirement through a custom clean method for the field and/or creating a custom form. Form fields do support callable choices.

See this answer for more info: https://stackoverflow.com/a/33514551/54017




回答2:


I had a similar problem with a custom field that I made for a Django 1.6 project that had the same general structure. I came to the following solution which works alright:

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=[])

That is for python 3, for python 2 you have to specify the metaclass as follows:

class ActivePlugin(models.Model):
    __metaclass__ = ActivePluginMeta

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



回答3:


I had a similar problem. My choices were dynamic (all years from a starting point to the present) and every year the first time makemigrations was run it generated new migrations for the new choice. The solution I found was customizing the field so the choices change wouldn't be detected by makemigrations:

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)


来源:https://stackoverflow.com/questions/31788450/stop-django-from-creating-migrations-if-the-list-of-choices-of-a-field-changes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!