追溯性地将新的ManyToManyField默认值设置为现有模型

时间:2017-06-10 20:38:33

标签: python django model

我有一个Django模型(称为BiomSearchJob),它目前正在运行,我想添加一个新的多对多关系,以使系统更易于为用户定制。以前,用户可以在不指定一组TaxonomyLevelChoices的情况下提交作业,但是为了向系统添加更多功能,用户现在应该能够选择他们自己的分类级别。

以下是模型:

class TaxonomyLevelChoice(models.Model):
    taxon_level = models.CharField(
        verbose_name="Taxonomy Chart Level", max_length=60)
    taxon_level_proper_name = models.CharField(max_length=60)

    def __unicode__(self):
        return self.taxon_level_proper_name

class BiomSearchJob(models.Model):
    ...
    # The new many-to-many relation
    taxonomy_levels = models.ManyToManyField(
        'TaxonomyLevelChoice', blank=False, max_length=3,
        default=["phylum", "class", "genus"])

    name = models.CharField(
        null=False, blank=False, max_length=100, default="Unnamed Job",
        validators=[alphanumeric_spaces])
    ...

目前,所有现有BiomSearchJobs隐含地具有default=项中列出的三个分类级别(用户无法选择),因此在数据库中都是相同的。运行migrate后,我发现以前的作业没有立即拥有三个分类级别关系,它们只在调用job.taxonomy_levels.all()时返回一个空集(如果job是{的实例{ {1}})。

有没有办法追溯性地添加这种关系而无需手动浏览所有内容?理想情况下,只需运行BiomSearchJob,我希望现有migrateBiomSearchJobs属性中列出phylumclassgenus

2 个答案:

答案 0 :(得分:8)

我认为您正在寻找数据迁移,这是一种允许仅对数据库进行数据更改的迁移。

您可以这样创建:

python manage.py makemigrations <your app> --name=retroactively_add_levels

然后将此代码插入刚刚创建的迁移文件中:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations


def add_taxonomy_levels(apps, schema_editor):
    BiomSearchJob = apps.get_model('<your app>', 'BiomSearchJob')
    TaxonomyLevelChoice = apps.get_model('<your app>', 'TaxonomyLevelChoice')
    for job in BiomSearchJob.objects.all():
        for choice in TaxonomyLevelChoice.objects.filter(taxon_level_proper_name__in=["phylum", "class", "genus"]):
            job.taxonomy_levels.add(choice)

class Migration(migrations.Migration):

   dependencies = []

operations = [
    migrations.RunPython(add_taxonomy_levels, reverse_code=migrations.RunPython.noop)
]

它几乎可以像SQL查询那样工作,但它可以利用Django ORM。

希望这有帮助。

答案 1 :(得分:3)

您的方法无法正常工作,因为您隐式想要对实例属性进行查询:Django无法猜测。

Django's Doc开始,默认可以是一个函数,而

  

对于像映射到模型实例的ForeignKey这样的字段,默认值应该是它们引用的字段的值(除非设置了to_field,否则为pk)而不是模型实例。

所以....要么你传递PK(例如ID)数组,要么使用函数来获取查询集。

class TaxonomyLevelChoice(models.Model):
    taxon_level = models.CharField(
        verbose_name="Taxonomy Chart Level", max_length=60)
    taxon_level_proper_name = models.CharField(max_length=60)

    def __unicode__(self):
        return self.taxon_level_proper_name

def get_default_taxonomy_levels():
    ...
    return YourQuerySet


class BiomSearchJob(models.Model):
    ...
    # The new many-to-many relation
    taxonomy_levels = models.ManyToManyField(
        'TaxonomyLevelChoice', blank=False, max_length=3,
        default=get_taxonomy_levels_default)

    name = models.CharField(
        null=False, blank=False, max_length=100, default="Unnamed Job",
        validators=[alphanumeric_spaces])
    ...

如果您在尚未创建TaxonomyLevelChoice个实例之前进行迁移,我猜您会遇到迁移问题。

由于前面的句子,我会使用一些伪缓存方法,因为每次创建BiomSearchJob时进行查询都不是一个可接受的解决方案。

我会这样做:

DEFAULT_TAXONOMY_LEVELS = None

def get_default_taxonomy_levels():
    if DEFAULT_TAXONOMY_LEVELS:
        return DEFAULT_TAXONOMY_LEVELS
    ...
    DEFAULT_TAXONOMY_LEVELS = YourQuerySet
    return YourQuerySet

编辑:由于问题是回溯主动设置多对多的默认值,我建议在现有实例上执行此命令,因为我认为迁移不会为您处理。