【问题标题】:Django - Select related or join on two different base modelsDjango - 选择相关或加入两个不同的基本模型
【发布时间】:2018-06-17 11:27:19
【问题描述】:

我有两个基础模型 SiteData 和 Showoroom Service,它们的模型结构如下所示。

我需要 SiteData 信息,但如果有匹配的 ID,我还想从 showroomservice 模型中获取 link_type。

到目前为止,我尝试了一些事情,但都没有得到我需要的东西,实现这一目标的最佳方法是什么?

谢谢

选择相关

>>> nd = ShowroomService.objects.select_related('site').all()
>>> nd
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 229, in __repr__
    return '<%s %r>' % (self.__class__.__name__, data)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/base.py", line 590, in __repr__
    u = six.text_type(self)
TypeError: __str__ returned non-string (type SiteData)

结合:

>>> complete_data = site_data | monitoring_data
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 310, in __or__
    combined.query.combine(other.query, sql.OR)
  File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/query.py", line 529, in combine
    "Cannot combine queries on two different base models."
AssertionError: Cannot combine queries on two different base models.

链接

>>> final_data = chain(monitoring_data, site_data)
>>> for i in final_data:
...  '{} {}'.format(i.location,i.link_Type)
...
Traceback (most recent call last):
  File "<console>", line 2, in <module>
AttributeError: 'ShowroomService' object has no attribute 'location'

sites.models.py

class SiteData(models.Model):
    location = models.CharField(max_length=50)
    site_type = models.ForeignKey(SiteTypes, verbose_name="Site Type", \
                on_delete=models.PROTECT)
    subnet = models.GenericIPAddressField(protocol='IPv4')
    routed_subnet = models.GenericIPAddressField(protocol='IPv4', \
                    verbose_name="Routed Link Subnet", blank=True, null=True)
    bgp_as = models.CharField(max_length=6, verbose_name="BGP AS Number")
    opening_date = models.DateField(verbose_name="Showroom opening date")
    last_hw_refresh_date = models.DateField(verbose_name="Date of latest hardware refresh", \
                           blank=True, null=True)
    is_live = models.BooleanField(default=False, verbose_name="Is this a live site?")
    tel = models.CharField(max_length=20, blank=True, null=True)
    address = models.CharField(max_length=255, blank=True, null=True)
    town = models.CharField(max_length=255, blank=True, null=True)
    ...
    class Meta:
        verbose_name = "Site Data"
        verbose_name_plural = "Site Data"
        ordering = ('location',)
        permissions = (
            ("can_view", "Can View"),
            ("can_view_mgmt", "Can View Management"),
        )

    def __str__(self):
        return self.location

monitoring.models.py

from sites.models import SiteData

class ShowroomService(models.Model):
    site = models.ForeignKey(SiteData, verbose_name="Site", \
        on_delete=models.PROTECT)
    link_type = models.CharField(max_length=200, blank=True, null=True)
    preference = models.CharField(max_length=200, blank=True, null=True)
    timestamp = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    dashboard = models.BooleanField(default=True, verbose_name="display on monitoring dashboard?")

    class Meta:
        verbose_name = "Showroom Service Data"
        verbose_name_plural = "Showroom Service Data"

    def __str__(self):
        return self.site

【问题讨论】:

  • 只是为了确保我理解正确的问题,您有一个 SiteData 模型列表,并且您想向后跟踪外键并获取所有子 ShowroomService 记录?
  • 您的第一个错误与您的问题无关,但正如消息所述,因为您返回的是 Site 对象,而不是来自 __str__ 方法的字符串。
  • 是的,我想在模板中显示所有站点数据信息以及来自 showroomservice 的链接类型。当我在模板中使用集合时,查看调试工具栏时,由于 set 命令,我正在运行 90 多个查询,因此我想在将它们发送到模板之前将它们组合起来以保存查询
  • 所以你试图避免使用我在答案中发布的*_set 属性?
  • 是的,该集合正在运行 90 多个查询(每个站点一个),这似乎非常低效,我想如果我得到这两个表,我可以在 site.id 上加入它们并改为在两个表中执行90

标签: python django


【解决方案1】:

您可以使用 django 的“相关管理器”拉取所有相关对象。文档可在此处获得:https://docs.djangoproject.com/en/2.0/ref/models/relations/#related-objects-reference

通过在SiteData 模型上调用showroom_service_set,您可以获得每个SiteData 的子记录集。

只是为了解释为什么列出的尝试也失败了:

  1. 您的模型上的__str__ 方法需要返回字符串。如果他们不这样做,你就会得到那个例外。
  2. 管道运算符| 用于OR 在django 中一起查询。该代码块试图组合对 2 种不同类型模型的查询。
  3. 通过链式尝试,您创建了一个包含两种不同类型模型的列表。其中一个没有location 属性。

这是获取附加到 SiteData 模型的所有 ShowroomService 模型的链接类型的代码块:

for site_data in SiteData.objects.all():
    for showroom in site_data.showroom_service_set.all():
        print showroom.link_type

我不确定 django 如何处理带有相关对象的驼峰式外壳,所以showroom_service_set 是我的最佳猜测。您可能需要为此做一些工作才能弄清楚实际的集合是什么。

编辑:有一种叫做 prefetch_related 的东西。这是一个关于它的答案;我认为它会为您提供所需的内容:https://stackoverflow.com/a/13096423/769971

【讨论】:

  • 与预取相关,29 毫秒内 6 次查询,而不是现在 150 毫秒内 100 次 :)
猜你喜欢
  • 1970-01-01
  • 2011-10-07
  • 1970-01-01
  • 1970-01-01
  • 2013-06-24
  • 1970-01-01
  • 2017-03-16
  • 2018-04-15
  • 1970-01-01
相关资源
最近更新 更多