【问题标题】:Fetching reverse foreign key of reverse foreign key in django while still taking advantage of prefetch_related在 django 中获取反向外键的反向外键,同时仍然利用 prefetch_related
【发布时间】:2018-11-01 15:55:39
【问题描述】:

我有以下模型(这些模型已大大简化):

class Job(models.Model):
    name = models.CharField(max_length=255)

    def get_date_details(self):
        pass
        #return a list of all jobdate --> details


class JobDate(models.Model):
    job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='dates')
    date = models.DateField()
    # ... and a bunch of other fields


class JobDateDetail(models.Model):
    job_date = models.ForeignKey(JobDate, on_delete=models.CASCADE, related_name='details')
    detail = models.CharField(max_length=255)
    # ... and a bunch of other fields

并且经常需要为数据库中的所有作业(几千个)处理JobDateDetails。我知道我可以使用如下查询预取所有数据:

q = Job.objects.all().prefetch_related('dates', 'dates__details')

但是我不确定如何最好地利用Job.get_date_details() 中的预取数据。一种选择是执行以下操作:

class Job(models.Model):
    def get_date_details(self):
        details = []
        for job_date in self.dates.all():
            details += [detail for detail in job_date.details.all()]
        return details

但是,我怀疑可能有一种方法可以直接查询所有这些 JobDateDetail 对象。需要注意的一件事——如前所述,我意识到我可以通过另一个方向来一次获取所有细节,但是在这种情况下,我使用的是 Django Rest Framework,并且需要将细节组装为 Job 序列化程序中的一个字段,要求我从这个方向走。想法?

【问题讨论】:

  • 您可以尝试将Prefetch objectto_attrvalues_list() 结合使用。例如,您的for 循环几乎等于chain.from_terable(list(job_date.details.all().values_list('detail')))
  • 除非我误解你的意思,否则我的印象是.values.values_list 与预取不兼容:code.djangoproject.com/ticket/26565
  • 这个想法是预取JobDate,然后使用values()。我刚刚意识到get_date_detailsJob 的一种方法,而不是JobDate 的一种方法,而且我目前还没有安装Django。
  • q = Job.objects.all().prefetch_related() 提供表级功能,而get_date_detals() 提供row-level functionality。你想把它们结合起来吗?您对 DRF 的看法是什么?我猜它适合工作及其日期详细信息。
  • 您不能在JobSerializer 上使用SerializerMethodField,它可以一次性检索所有详细信息,但仍允许您将job_details 指定为JobSerializer 上的字段吗?

标签: django orm django-rest-framework foreign-keys


【解决方案1】:

我输入了一些随机数据来演示查询。这里有两个选项:

  • 第一个是表级的,1db hit

    In [23]: Job.objects.all().values_list('id', 'dates__details__detail')                                                                                                                                             
    Out[23]: <QuerySet [(1, '132'), (1, '4324gg'), (1, 'hrthrthrth'), (1, 'erhehrgnfgnmfgghmn'), (1, 'herhehrnfn'), (1, 'erg eg cvb dfg vb'), (1, 'greg egr erg erg erg'), (1, 'ewrg erg db cvb'), (2, None), (3, None)]>
    
  • 第二个是行级2db hits(一个用于获取对象,另一个用于获取其日期详细信息):

    In [36]: Job.objects.all().first().dates.all().values_list('details__detail')                                                                                                                                      
    Out[36]: <QuerySet [('132',), ('4324gg',), ('hrthrthrth',), ('erhehrgnfgnmfgghmn',), ('herhehrnfn',), ('erg eg cvb dfg vb',), ('greg egr erg erg erg',), ('ewrg erg db cvb',)]>
    

第二个,如果从模型方法get_date_details调用,相当于self.dates.all().values_list('details__detail'),可以带来以下结果 1db hit

In [30]: from itertools import chain
In [31]: list(chain.from_iterable( 
    ...:     Job.objects.all().first().dates.all().values_list('details__detail') 
    ...: ))        
Out[31]: 
['132',
 '4324gg',
 'hrthrthrth',
 'erhehrgnfgnmfgghmn',
 'herhehrnfn',
 'erg eg cvb dfg vb',
 'greg egr erg erg erg',
 'ewrg erg db cvb']

因此你的模型方法可以变成:

class Job(models.Model):
    def get_date_details(self):
        return (
            list(chain.from_iterable(
                self.dates.all().values_list('details__detail')
            ))                                                                                                           
        )

【讨论】:

    猜你喜欢
    • 2018-10-16
    • 2012-11-16
    • 2019-12-01
    • 2014-06-19
    • 1970-01-01
    • 2023-01-30
    • 2013-02-24
    • 2022-01-04
    • 1970-01-01
    相关资源
    最近更新 更多