【问题标题】:Ignore changes in CharField "choices" during Django migration creation在 Django 迁移创建期间忽略 CharField“选择”的变化
【发布时间】:2015-10-19 02:46:24
【问题描述】:

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

让 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

【问题讨论】:

  • 这是设计使然,这就是我仍然使用 South 进行迁移的原因。这是对您的问题的更好解释stackoverflow.com/questions/26152633/…
  • 该死的,害怕那个。在我阅读了那个答案/那些票并添加了那个答案之后,今天中午找到了一个 hacky 方法。感谢您的提醒!

标签: python django django-models django-migrations


【解决方案1】:

事实证明,由于某种原因未启用此功能(来自this answer

这是设计使然。有几个原因,尤其是对我而言,历史上的数据迁移需要对模型进行完全准确的表示,包括它们的所有选项,而不仅仅是影响数据库的选项。

但是,您可以返回初始迁移并执行

class Migration(migrations.Migration):
    operations = [
        migrations.CreateModel(
            name='owner',
            fields=[
                ('city', models.CharField(choices=settings.CITIES, blank=True, max_length=3, db_index=True)),
    ]

不是很漂亮,理论上它可能会回来咬你,所以在经理的get_queryset() 中使用ForeignKeyselect_related('cities') 进行重构可能是最安全/最少hacky 的方式这个,但这绝不应该导致发生settings 更改的新迁移,从而导致choices 更改。

【讨论】:

    【解决方案2】:

    我不会让我在验证所有数据库/ORM 方面变得那么困难,但是在控制器/表单中进行验证。

    以下是我将根据您的示例代码做的事情:

    models.py:

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

    (这里没有选择!)

    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)
    

    如果现在的城市是

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

    表格将显示杜塞尔多夫和柏林,如果是的话

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

    表格将显示伦敦和巴黎。

    【讨论】:

    • 对,这适用于表单。但是owner.get_city_display()(在应用程序代码中)/{{ owner.get_city_display }}(在模板中)在这种情况下不起作用。
    猜你喜欢
    • 2014-11-26
    • 1970-01-01
    • 2017-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-05
    • 2021-09-25
    • 1970-01-01
    相关资源
    最近更新 更多