Django Postgres循环manytomany引用 - 在表上插入或更新违反外键约束

时间:2016-05-19 18:21:04

标签: django postgresql foreign-keys circular-reference

BLUF: 如何让poc.poc_employer.add(new_supplier)使用new_supplier对象id?

背景 在使用sqlite3的本地机器上进行开发时,我对以下代码没有任何问题。一旦在Heroku上部署到Postgres就是我第一次遇到这个问题。

该问题涉及两个相互引用的模型。首先是公司,用于代表用户的公司以及用户的供应商公司。第二个是POC,它存储特定供应商组织内用户POC的信息。

型号:

class Company(models.Model):
    ...
    suppliers = models.ManyToManyField('self', db_index=True, symmetrical=True, related_name = "the_suppliers", blank = True)
    POC = models.ManyToManyField('Supplier_POC', db_index=True, symmetrical=True, blank = True)
    ...

    def __str__(self):
        return self.company_name


class Supplier_POC(models.Model):
    poc_employer = models.ManyToManyField('Company', symmetrical=True, blank = True)
    poc_name = models.CharField(blank=True, null=True, max_length=200)
    poc_phone = models.CharField(blank=True, null=True, max_length=200)
    poc_email = models.EmailField(blank=True, null=True, max_length=200)

用户故事涉及通过在表单上输入供应商名称,poc名称,poc电话和poc电子邮件并提交,将供应商添加到其公司批准的供应商列表中。然后,视图获取或创建供应商对象,将其添加到用户的公司对象,创建POC对象,并将供应商作为poc_employer添加到POC对象。在下面的代码中,供应商尚不存在,必须创建。

查看:

#Create and add supplier to Company table as non-member
new_supplier = Company(company_name = supplier, company_domain = domain, member = False)
new_supplier.save()


#Add new supplier to user company's supplier list.  No issues with this.
new_supplier =  Company.objects.get(company_name__iexact = supplier)
company.suppliers.add(new_supplier)



#Save POC to poc table.  No issues with this.
if phone != ""  :
    poc = Supplier_POC( poc_name=name, poc_phone = phone, poc_email = email )

else:
    poc = Supplier_POC( poc_name=name, poc_phone = "", poc_email = email )
    poc.save()

#Add new_supplier to poc object.  Here's the trouble spot
try:
    poc.poc_employer.add(new_supplier)
except Exception as e:
    print('error trying to add supplier to poc object')
    print(e)

来自Heroku日志的错误:

  

在表格上插入或更新" supplyhelix_supplier_poc_poc_employer"违反外键约束" supplyhelix_company_id_19a0767e7b462d_fk_supplyhelix_company_id"

     

详细信息:表中不存在键(company_id)=(11)   " supplyhelix_company"

对于我在Stackoverflow上看到的其他类似问题,解决方案只是添加的对象没有被创建和/或保存。正如您在视图中看到的那样,该对象既被创建又被保存,并且在尝试添加到POC对象时,在错误发生之前将其成功添加到用户的公司对象中。

我已经打印出new_supplier对象的id几次,以比较上面的错误细节,但它们不匹配。例如,产生上述错误的特定测试new_supplier.id为14,但添加显然正在寻找我之前从数据库中删除的id = 11。

不确定为什么,但从我所知道的,而不是简单地使用传递的供应商对象的id,poc.poc_employer.add(new_supplier)只是使用自动递增的id,每次尝试时增加1将供应商对象添加到poc。

我知道这很长。非常感谢你阅读这篇文章!我非常感谢您的时间以及您对我的任何反馈/帮助。

2 个答案:

答案 0 :(得分:0)

我会考虑重做模型。看起来您使用公司模型既是公司又是供应商。我知道供应商是一家公司,但如果您以不同方式对待它们,最好将它们分开。您可以将公司作为基本模型,并且可以从中继承供应商和雇主。

就你的模型而言,我会考虑将它们写成:

class Company(models.Model):
    company_name = ...
    ...

class Employer(Company):
    suppliers = models.ManyToManyField('Supplier', db_index=True, related_name="employers", blank=True)


class Supplier(Company):
    ...

class POC(models.Model):
    name = models.CharField(blank=True, null=True, max_length=200)
    phone = models.CharField(blank=True, null=True, max_length=200)
    email = models.EmailField(blank=True, null=True, max_length=200)
    ...

class SupplierPOC(POC):
    supplier = models.ForeignKeyField('Supplier', blank=True)

对于您的观点,我会考虑将其写成:

#Create and add supplier to Company table as non-member
new_supplier = Supplier(company_name=supplier_name, company_domain=domain, member=False)
new_supplier.save()

#Add new supplier to user company's supplier list. (I wouldn't use names. Instead use id's.)
new_supplier = Supplier.objects.get(company__id=supplier.id)
employer.suppliers.add(new_supplier)

#Save POC to poc table.
poc = SupplierPOC(supplier=new_supplier, name=name, phone=phone, email=email)
poc.save()

忽略所有这些,您的if phone != "" :块中没有保存,这意味着poc将不会被保存或添加。

答案 1 :(得分:0)

首先,感谢Curtis Olson和knelson的时间和建议。对此,我真的非常感激!通过这两项建议,让我在面对极大的挫折时前进。另外,我很抱歉没有尽快发布。我的公婆在镇上,我愚蠢地期望有更多的时间来处理这个问题而不是实际工作。

好的,以下是我所做的更改:

我将Supplier_POC的poc_employer字段作为公司的外键。

poc_employer = models.ForeignKey('Company', blank = True, null = True)

然后在视图中,我按照我在此处找到的示例,明确地将供应商的ID设置为poc_employer:

Django: Set foreign key using integer?

poc = Supplier_POC( poc_name=name, poc_phone = phone, poc_email = email )
poc.poc_employer_id = new_supplier.id
poc.save()  #knelson, thanks for the catch on the save.

这在本地sqlite3和heroku Postgres上都有效。如果我将poc_employer保持在一个很多领域,我不确定自己是否能够做类似的事情,但可能会在以后查看。

再次感谢!