【问题标题】:Django - join between different models without foreign keyDjango - 在没有外键的不同模型之间加入
【发布时间】:2018-07-01 09:11:22
【问题描述】:

想象一下我有两个简单的模型(这不是我真正拥有的,但这会做):

Class Person(models.Model):
    person_id = models.TextField()
    name = models.TextField()
    #...some other fields

Class Pet(models.Model):
    person_id = models.TextField()
    pet_name = models.TextField()
    species = models.TextField()
    #...even more fields

这是这个例子和我读到的其他一些问题的主要区别:我的模型不强制使用外键,所以我不能使用select_related()

我需要创建一个视图来显示两个查询集之间的连接。所以,假设我想要一个视图,所有名为 John 的所有者都带着一条狗。

# a first filter
person_query = Person.objects.filter(name__startswith="John")
# a second filter
pet_query = Pet.objects.filter(species="Dog")
# the sum of the two
magic_join_that_i_cant_find_and_possibly_doesnt_exist = join(person_query.person_id, pet_query.person_id)  

现在,我可以用任何函数加入这两个非常简单的查询集吗?

或者我应该使用原始的?

SELECT p.person_id, p.name, a.pet_name, a.species
FROM person p 
LEFT JOIN pet a ON 
    p.person_id = a.person_id AND
    a.species = 'Dog' AND
    p.name LIKE 'John%'

这个查询可以吗?该死的,我不确定了......这是我的查询问题。一切都是一次。但是连续的查询看起来那么简单……

如果我在模型类中引用“外键”(供select_related() 使用),迁移后是否会在数据库中强制执行? (我需要它不会发生)

【问题讨论】:

  • 你应该解释为什么你不使用外键,并改变你的模型以便你这样做。对所有字段使用 TextFields(即 blob)是低效且不必要的。
  • 嗯,这是问题的一个特征,它是黑匣子。数据库已经是这样了,我不应该弄乱设计(尽管我可以)。无论哪种方式,数据库都具有 OLAP 特性,因此触发器和约束都不是最佳的。文本字段是遗留数据库集成功能的映射方式。
  • 好吧,你不必相信 inspectdb 的输出——它甚至说你需要编辑它。如果一个字段是另一个模型中 ID 的表示,它就是一个外键。
  • person_id 是否包含整数值?然后尝试在 Django 中创建一个外键,尽管有TextField。也许它只是工作......
  • 但是,Daniel,我再问一下最后是什么:如果我在models.py中写这个和这个其他字段是外来的,当我迁移时,这个约束是否会写入数据库?

标签: sql django django-models


【解决方案1】:

制作一个models.ForeignKey,但使用db_constraint=False

https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.ForeignKey.db_constraint

另外,如果这个模型是managed=False,即它是一个遗留数据库表并且你没有使用 Django 迁移,那么一开始就不会进行约束,这很好。

【讨论】:

    【解决方案2】:

    如果您在模型中创建 FK,Django 将创建迁移约束,因此您希望避免这种情况。

    如果您不将要加入的字段声明为外键,我认为没有办法在 Django 中加入数据库。您唯一能做的就是在 Python 中进行连接,这可能会也可能不会。认为prefetch_related 正是这样做的。

    代码类似于:

    person_query = Person.objects.filter(name__startswith="John")
    person_ids = [person.id for person in person_query]
    pet_query = Pet.objects.filter(species="Dog", person_id__in=person_ids).order_by('person_id')
    pets_by_person_id = {person_id: pet_group for person_id, pet_group in itertools.groupby(pet_query, lambda pet: pet.person_id)}
    
    # Now everytime you need the pets for a certain person
    pets_by_person_id(person.id)
    
    # You can also set it in all objects for easy retrieval
    for person in person_query:
        person.pets = pets_by_person_id(person.id)
    

    代码可能不是 100% 准确,但我希望你明白。

    【讨论】:

    • 我确实找到了一个使用类似方法的网站。实际上,我从那里开始使用“宠物”“所有者”模型示例,但对我来说并不是很清楚。如果有很多记录,仍然有相当多的重机使用,不是吗?如果我做一个 get inside a for ,它将对数据库造成疯狂的次数。如果我这样做,所有变量都将存储在 RAM 中?对吗?
    • 使用这种方法,每个模型只需访问数据库一次,因此在本例中是 2 次。是的,所有记录都将存储在内存中——但您可以使用queryset.only(*fields)queryset.values(*fields) 可能在很大程度上缓解这种情况。使用这种方法无法避免的是 Python 中的连接,这可能是一个问题,但只有当您可能处理大量记录时。
    • 现在我采取了简单的方法,在for 中写了一个get。可怕的选择,我知道。它似乎有点慢。接下来我会尝试你的方法,看看哪一个需要更少的时间。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    • 1970-01-01
    • 2018-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-25
    相关资源
    最近更新 更多