简体   繁体   English

Django中的反向一对多关系

[英]Reverse One-to-Many-Relationship in Django

My app links invoices, contracts and services with Many-to-One-Relationships:我的应用程序将发票、合同和服务与多对一关系联系起来:

class Invoice(models.Model):
    contract = models.ForeignKey(Contract, on_delete=models.CASCADE)

class Contract(models.Model):
    service = models.ForeignKey(Service, on_delete=models.CASCADE)

Whenever a new invoice is registered, it can be linked to a service and split/billed internally.每当注册新发票时,它都可以链接到服务并在内部拆分/计费。 Unfortunately, some contracts/invoices need to be linked to more than one service according to a fixed split (eg 30/70).不幸的是,一些合同/发票需要根据固定的拆分(例如 30/70)链接到不止一项服务。

For this to work on the surface, I could to reverse the relationship between contracts and services –为了让它在表面上起作用,我可以颠倒合同和服务之间的关系——

class Service(models.Model):
    contract = models.ForeignKey(Contract, on_delete=models.CASCADE)

– or change the ForeignKey field on the Contract class to a ManyToManyField . – 或者将Contract class 上的ForeignKey字段更改为ManyToManyField

But in both cases, I will not be able to get back from the invoice to the service easily anymore, as with the following statement:但在这两种情况下,我将无法再轻松地从发票返回到服务,如以下声明:

invoices = Invoice.objects.filter(models.Q(contract__service__building=self.tenant.unit.building), models.Q(begin__lte=self.begin, end__gt=self.begin) | models.Q(begin__gt=self.begin, begin__lt=self.end))

Is it wise to insert an intermediate helper model ( ContractService ) with two ForeignKey fields to keep the current app logic and add the option to link a contract to more than one service?插入一个带有两个ForeignKey字段的中间助手 model ( ContractService ) 以保持当前应用程序逻辑并添加将合同链接到多个服务的选项是否明智?

Ok just to clarify one example:好的,只是为了澄清一个例子:

  • Contract is "Cleaning of House"合同是“打扫房子”
  • Services are "Cleaning of first floor" and "Cleaning of second floor"服务是“一楼保洁”和“二楼保洁”
  • Invoices are "Invoice1", "Invoice2", ...发票是“Invoice1”、“Invoice2”、...

You want a relationship that "Invoice1" can be linked to "Cleaning of first floor" AND "Cleaning of second floor".您想要一种关系,即“发票 1”可以链接到“一楼清洁”和“二楼清洁”。

models.py模型.py

class Contract(models.Model):
    """Can hold multiple Services"""
    pass

class Service(models.Model):
    """Is linked to one specific Contract"""
    contract = models.ForeignKey(Contract, on_delete=models.CASCADE)

class Invoice(models.Model):
    """Can hold multiple Services and one Service can hold multiple Invoices"""
    service = models.ManyToManyField(Service)

Now your question is: "I can easily get the contract when I have the Service object, but how can I get the Service when I have the Contract object?现在你的问题是:“当我有服务object时,我可以很容易地获得合同,但是当我有合同object时,我如何获得服务?

con = Contract.objects.all().first()
queryset = con.service_set.all()  # gives you all related Services for that specific Contract

Read more about ManytoOne阅读更多关于ManytoOne 的信息

And: "How can I get the Invoice when I have the Service object?"并且:“当我有服务 object 时,我怎样才能得到发票?”

ser = Service.objects.all().first()
queryset = ser.invoice_set.all() # gives you all related Invoices for that specific Service

Read more about ManyToMany阅读更多关于ManyToMany的内容

Let me know how it goes让我知道事情的后续

Thanks to @Tarquinius for your help – I found a solution based on his suggestion.感谢@Tarquinius 的帮助——我根据他的建议找到了解决方案。 The three models in question are connected as follows:所讨论的三个模型的连接如下:

class Service(models.Model):
    pass

class Contract(models.Model):
    services = models.ManyToManyField(Service)

class Invoice(models.Model):
    contract = models.ForeignKey(Contract, on_delete=models.CASCADE)

The query quoted above did not even need to be modified (apart from the different fieldname) to reflect the possibility of more than one service per contract, I just had to add the distinct() function:上面引用的查询甚至不需要修改(除了不同的字段名)以反映每个合同有多个服务的可能性,我只需要添加 distinct() function:

invoices = Invoice.objects.filter(models.Q(contract__services__building=self.tenancy_agreement.unit.building), models.Q(begin__lte=self.begin, end__gt=self.begin) | models.Q(begin__gt=self.begin, begin__lt=self.end)).distinct()

In hindsight, this is quite obvious (and simple).事后看来,这是很明显的(也很简单)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM