Django模型字段实际上是对相关模型中的字段的引用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django模型字段实际上是对相关模型中的字段的引用相关的知识,希望对你有一定的参考价值。

如果这个问题难以理解,请提前道歉 - 我不确定如何用它来表达。基本上,我试图在Django模型中创建一种“伪字段”,其工作方式与任何其他Django字段完全相同,除了它实际上是对相关模型上的字段的引用。

举个例子,假设我正在为狗经营酒店。在我的酒店,我有房间,每个房间都分配给一个客户,并包含一只狗。

class Customer(Model):
    name = models.CharField(max_length=256, null=False)

class Dog(Model):
    name = models.CharField(max_length=256, null=False)
    customer = model.ForeignKey(Customer, null=True, on_delete=models.CASCADE) # The dog's owner

class Room(Model):
    customer = model.ForeignKey(Owner, null=True, on_delete=models.SET_NULL)
    dog = model.ForeignKey(Dog, null=True, on_delete=models.SET_NULL)

数据库中的相应表看起来像这样

CUSTOMER:

| ID | NAME       |
___________________
| 01 | John Smith |
| 02 | Jane Doe   |


DOG:

| ID | NAME  | CUSTOMER_ID |
____________________________
| 01 | Rover |     01      |
| 02 | Fido  |     01      |
| 03 | Spot  |     02      |


ROOM:

| ID | DOG_ID | CUSTOMER_ID |
_____________________________
| 01 |   01   |      01     |
| 02 |   03   |      02     |

所以,我的老板注意到我们正在将冗余数据存储在数据库中:房间表并不需要有自己的客户ID列:客户始终是常驻狗的所有者,每个房间只包含一只狗,并且每只狗都有一个主人,所以我们总是可以通过去狗桌并查找主人来将顾客分配到房间。我被要求从房间表中删除客户ID列,其方式与代码库的其余部分“完全透明”。

首先,我可以使用自定义getter将customer转换为Room类中的@property

class Room(Model):
    dog = model.ForeignKey(Dog, null=True, on_delete=models.SET_NULL)

    @property
    def customer(self):
        return self.dog.customer

所以现在我们在代码中的所有地方都像c = room.customer一样,它继续工作。问题是,代码库充满了像room__customer这样的Django字段查询,当我将customer变成@propertyRoom时,它们都停止工作。我可以将它们全部改为room__dog__customer,它工作正常,但随后改变不会“完全透明”。

我做了一些研究,我尝试实现一个自定义管理器并为Rooms注释查询集:

class RoomManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().annotate(customer=F('dog__customer'))

当我这样做时,我可以在查询中使用room__customer,但不能使用room__customer__name,我想因为F()返回主键值而不是模型实例(https://docs.djangoproject.com/en/2.1/ref/models/expressions/#using-f-with-annotations)。

所以我的问题是,有没有办法让我不知道的这项工作?一种方法,使房间行为像它与客户有直接的外键关系,没有customer_id存储在房间的桌子?

答案

我猜你的经理关于“完全透明”的界限只是一种说法,“无论你改变什么都不应该搞砸其他商业逻辑。”换句话说,应用程序应该像今天一样继续工作。我非常怀疑他关心你编辑了多少文件(不过,他可能是一名微型经理,在这种情况下......我很抱歉)。

也就是说,我认为你将room__customer改为room__dog__customer的解决方案是最好的行动方案。这一变化使得其他Django开发人员明白正在发生的事情(你正在走这段关系),这是一个相当直接的改变。是的,您可能最终不得不触摸许多文件,但是当您使用后端架构时,这就是生活。

但是,您需要考虑这一变化的一个方面是潜在的性能影响。您可能需要为查询引入select_related调用,以确保正确连接表。否则,执行room -> dog -> customer查找可能会变得昂贵(每次查找额外的数据库往返;这实际上是在一个循环中加起来)。

祝你好运!

以上是关于Django模型字段实际上是对相关模型中的字段的引用的主要内容,如果未能解决你的问题,请参考以下文章

Django模板按“不相关”模型的字段排序

从 Django 中的文本字段填充管理表单

相关领域的 Django 模型验证

参考模型层中的相关模型字段

Django 1.8.3 - 具有相关对象的模型字段验证

具有多个字段的模型中的查询集(Django)