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
变成@property
的Room
时,它们都停止工作。我可以将它们全部改为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模型字段实际上是对相关模型中的字段的引用的主要内容,如果未能解决你的问题,请参考以下文章