首页 > 解决方案 > Django ForeignKey 接受两种模型

问题描述

我正在使用 Django 进行这个大项目,我必须更新数据库。我必须添加另一个表,稍后将替换另一个表。

所以我想在一个模型中添加一个可能有一个字段,在那里我可以拥有旧模型或新模型。

这是旧模型的代码:

class Harvests(models.Model):
    ident_culture = models.IntegerField(primary_key=True)
    intitule_culture = models.CharField(max_length=50, blank=True)
    nom_fertiweb = models.CharField(max_length=50, blank=True, null = True)
    affichage_quintaux_tonne = models.CharField(max_length=1, 
                choices=RENDEMENT_CHOICES, default = 'T')
    type_culture = models.ForeignKey("TypeCulture", null=True)
    slug = models.SlugField(null=True, blank=True)
    image = models.ImageField(upload_to = 'images_doc_culture/', 
                  null=True, blank = True)
    affichage = models.BooleanField(default = True)

    class Meta:
        verbose_name = "Liste - Culture"
        verbose_name_plural = "Liste - Cultures"
        ordering = ['intitule_culture']

    def __str__(self):
        return self.intitule_culture

    def label(self):
        return self.intitule_culture or ''

    @classmethod
    def get_choices(cls):
        choices = [('', corp.EMPTY_CHOICE_LBL)]
        c_category_lbl, c_category = '', []
        for item in cls.objects.all():
            choices.append((item.pk, item.intitule_culture))
        return choices

还有我创建的新代码:

class Crops(models.Model):
    intitule_culture = models.CharField(max_length=75, blank=True)
    affichage_quintaux_tonne = models.CharField(max_length=2, 
                choices=RENDEMENT_CHOICES, default = 'T')
    type_culture = models.ForeignKey("TypeCulture", null=True)
    ident_culture = models.IntegerField(primary_key=True)
    affichage = models.BooleanField(default = True)
    id_marle = models.IntegerField(null=True)

    class Meta:
        verbose_name = "Liste - Culture 2019"
        verbose_name_plural = "Liste - Cultures 2019"
        ordering = ['intitule_culture']

    def __str__(self):
        return self.intitule_culture

    def label(self):
        return self.intitule_culture or ''

    @classmethod
    def get_choices(cls):
        choices = [('', corp.EMPTY_CHOICE_LBL)]
        c_category_lbl, c_category = '', []
        for item in cls.objects.all():
            choices.append((item.pk, item.intitule_culture))
        return choices

我想culture在这个模型中接受这两个模型:

class CompanyHarvest(models.Model):
    company = models.ForeignKey('corp.Company', verbose_name='Exploitation',
                related_name ='cultures')
    culture = models.ForeignKey(Harvests, verbose_name ='Culture')
    precision = models.CharField(max_length=255, blank=True)
    saison_culture = models.CharField(max_length=1, choices=SAISON_CHOICES, 
                      default = 'P')

    class Meta:
        verbose_name = "Expl. - Culture"
        verbose_name_plural = "Expl. - Cultures"
        unique_together = ('company', 'culture', 'precision', 'saison_culture')

    def __str__(self):
        return str(self.culture) + ' ' + self.precision + \
            ' ' + str(self.get_saison_culture_display() )

    @property
    def slug(self):
        return "_".join([slugify(str(self.culture or '')),
                                      slugify(str(self.precision or ''))]
                                      )

我是 Django 新手,有人可以帮我吗?(^-^)

标签: pythondjango

解决方案


这是不可能的——至少不是这样。这不是 Django 的限制,而是 SQL 的限制,外键不能引用一个表或另一个表。

这里一个可能且简单明显的解决方案是在 CompanyHarvest 中有两个外键 - 一个用于旧模型和新模型中的每个 - 每个都带有blank=Trueet default=None,但它很快就会弄乱所有客户端代码(所有代码都使用CompanyHarvest)。

更好的解决方案是要么只保留现有模型(向其中添加任何新字段/功能并最终隐藏过时的),要么将所有旧模型记录迁移到新模型(这可以与天真的“两个外键”结合使用)解决方案,以便您可以在必要时将旧表和记录保留为档案)。

另外 - 完全不相关,但是 -,这个:

@classmethod
def get_choices(cls):
    choices = [('', corp.EMPTY_CHOICE_LBL)]
    c_category_lbl, c_category = '', []
    for item in cls.objects.all():
        choices.append((item.pk, item.intitule_culture))
    return choices

1/ 应该在管理器上定义(参见https://docs.djangoproject.com/en/2.1/topics/db/managers/#adding-extra-manager-methods

2/ 应该使用.values()queryset 编写(这将节省 db 查询和构建完整的实例,没有充分的理由):

for item in cls.objects.values("pk", "intitule_culture"):
    choices.append(item)

3/ 并且很可能(我必须看看它是如何使用的)ModelChoiceField在调用代码中被替换为 a 。

哦,是的:如果您允许文本字段使用空格,您很可能希望将空字符串强制为默认值,因此NULL当没有给出值时,您不会出现两种可能(且不兼容)的情况(sql 和空字符串)。


推荐阅读