忽略CharField"选择"中的更改在Django迁移创建过程中

时间:2015-07-28 13:44:14

标签: python django django-models django-migrations

我正在构建一个开源可重用应用,需要该应用的用户才能在choices中的某些CharField字段上设置自己的settings

让Django忽略对choices字段选项的更改是否可能/合理,从不(如果要添加该功能)实现数据库级选择选择?

这不是实际代码,但

这将在models.py

class Owner(models.Model):
    city = models.CharField(
        verbose_name=u'City',
        max_length=255,
        blank=True,
        choices=settings.CITIES,
        db_index=True)

这将在settings.py

CITIES = (
    ('chicago', 'Chicago, IL'),
    ('milwaukee', 'Milwaukee, WI')
)

这将导致此迁移

class Migration(migrations.Migration):
    operations = [
        migrations.CreateModel(
            name='owner',
            fields=[
                ('city', models.CharField(blank=True, max_length=3, db_index=True, choices=[(b'chicago', b'Chicago, IL'), (b'milwaukee', b'Milwaukee, WI')])),
    ]

现在,假设最终用户希望更改他们的应用,而不是在settings.py

CITIES = (
    ('los_angeles', 'Los Angeles, CA'),
    ('san_fransisco', 'San Fransisco, CA')
)

这将导致使用python manage.py makemigrations创建另一个迁移,如下所示:

class Migration(migrations.Migration):

    dependencies = [
        ('appname', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='user',
            name='city',
            field=models.CharField(blank=True, max_length=255, verbose_name='City', db_index=True, choices=[(b'los_angeles', b'Los Angeles, CA'), (b'san_fransisco', b'San Fransisco, CA')]),
        ),
    ]

即使该应用的其他用户可能拥有完全不同的受支持城市列表。

当我发布具有0002迁移号的开源应用程序的新版本时,这可能会导致冲突,并且如果存在数据库级别的执行选择,则可能会造成严重破坏。

在迁移创建期间,是否可以让Django忽略对choices字段的更改?扩展CharField似乎是合理的。

或者我是否必须使用永不更改的ForeignKey重构此内容并将select_related()添加到管理器中?

供参考,here's the Django makemigrations inspector code

2 个答案:

答案 0 :(得分:2)

It turns out this is not enabled for a reason (from this answer)

This is by design. There are several reasons, not least of which for me that datamigrations at points in history need to have a full accurate representation of the models, including all their options not just those which affect the database.

However, you can go back to the initial migration and do

Time#round

Not very pretty and it could theoretically come back to bite you down the line, so refactoring with a class Migration(migrations.Migration): operations = [ migrations.CreateModel( name='owner', fields=[ ('city', models.CharField(choices=settings.CITIES, blank=True, max_length=3, db_index=True)), ] and ForeignKey in the select_related('cities') in the manager might be the safest/least-hacky way to go about this, but this should never result in a new migration happening with a get_queryset() change that results in a settings change.

答案 1 :(得分:0)

I wouldn't make it that difficult on me to validate everything database/ORM wise, but do the validating in the controller/the form.

Here's what I would do building upon your sample code:

models.py:

class Owner(models.Model):
    city = models.CharField(
        verbose_name=u'City', max_length=255, blank=True, db_index=True
    )

(No choices here!)

views.py:

class CitySelectionView(FormView):
    template_name = "city_selection.html"
    form_class = forms.CitySelectionForm

     def form_valid(self, form):
         obj = models.Owner(city=form.cleaned_data['city']
         obj.save()
         return redirect('index')

forms.py:

class CitySelectionForm(forms.Form):
    city = forms.MultipleChoiceField(choices=settings.CITIES)

If cities now is

CITIES = (('dusseldorf', 'Düsseldorf, Germany'), ('berlin', 'Berlin, Germany'))

the form will show Düsseldorf and Berlin, and if it's

CITIES = (('london', 'London, UK'), ('paris', 'Paris, France'))

the form will show London and Paris.