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

时间:2018-11-12 17:55:51

标签: python django django-models

如果很难回答这个问题,请提前道歉–我不确定该如何表达。基本上,我试图在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     |

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

首先,我可以使用自定义吸气剂将Room类中的customer转换为@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的{​​{1}}时,它们全部停止工作。我可以将它们全部更改为Room,这很好用,但是更改不会是“完全透明的”。

我做了一些研究,并尝试实现自定义管理器并注释“房间”的查询集:

room__dog__customer

这样做时,我可以在查询中使用class RoomManager(models.Manager): def get_queryset(self): return super().get_queryset().annotate(customer=F('dog__customer')) ,但不能在room__customer中使用,我想是因为room__customer__name返回的是主键值而不是模型实例({{3 }}。

所以我的问题是,有没有办法使我不了解的这项工作?一种使Room像它与客户具有直接外键关系,而无需将F()存储在Rooms表中的方式?

1 个答案:

答案 0 :(得分:1)

我猜您的经理关于“完全透明”的说法仅仅是一种说法,“无论您进行什么更改都不会破坏其他业务逻辑。”换句话说,该应用程序应该像今天一样继续工作。我非常怀疑他是否在乎您编辑多少个文件(不过,在这种情况下,他可能是一个微型经理。很抱歉)。

也就是说,我认为将room__customer更改为room__dog__customer的解决方案是最好的做法。更改使其他Django开发人员可以明显看出正在发生的事情(正在逐步发展这种关系),并且这是一个相当简单的更改。是的,您可能最终不得不触摸许多文件,但是当您弄乱了后端架构时,这就是生活。

但是,您需要考虑此更改的一件事是潜在的性能影响。您可能需要向您的查询引入select_related调用,以确保正确连接表。否则,进行room -> dog -> customer查找可能会变得很昂贵(每次查询都要进行一次额外的数据库往返行程 ;这确实是一个循环。)

祝你好运!