【问题标题】:Django reverse foreign keyDjango反向外键
【发布时间】:2019-12-01 19:29:37
【问题描述】:

我现在使用 python/django 1 个月了,我需要帮助,因为我找不到任何类似的问题,我正在寻找答案。我有工人模型和工作模型。一个工人可以有很多工作。我需要查询 Worker 的最新工作。

class Worker(models.Model):
   name = models.CharField(max_length=128)

class Job(models.Model):
   worker = models.ForeignKey(Worker)
   type = models.CharField(max_length=64)
   position = models.CharField(max_length=64)
   location = models.CharField(max_length=128)
   start_date = models.DateField()

我目前正在使用此示例代码。问题是这会返回类似的 Worker 对象,包括它以前的作业。我只想查询 Worker 最新的工作地点和开始日期。

for employee in Job.objects.all():
   print(employee.worker, employee.location, employee.start_date)

样本输出

(A, north, 2018-01-21)
(A, south, 2018-09-13)
(A, east, 2019-05-11)
(B, west, 2019-01-01)

有没有办法对 Worker.job_set.all() 这样的查询使用 for 循环来获得预期的输出

(A, east, 2019-05-11)
(B, west, 2019-01-01)

希望你能帮助像我这样的新手。先感谢您! :)

【问题讨论】:

    标签: python django python-2.7 django-models django-views


    【解决方案1】:

    您可以对工作人员的过滤和排序(按start_date,按升序)查询集使用last 方法。

    例如,如果工人的名字是foobar,你可以这样做:

    Job.objects.filter(worker__name='foobar').order_by('start_date').last()
    

    这将为您提供名为foobar 的工人的最后一个Job(基于start_date)。

    如果您按start_date 的降序排序,您还可以获得first 元素:

    Job.objects.filter(worker__name='foobar').order_by('-start_date').first()
    

    【讨论】:

      【解决方案2】:

      例如,您可以对 job_set 查询集执行排序。您正在寻找类似的东西吗?

      for worker in Worker.objects.all():
           latest_job = worker.job_set.latest('start_date')
           print(worker.name, latest_job.location, latest_job.start_date)
      

      【讨论】:

      • 修正你的错字,它的worker.job_set.latest 额外的() 会导致错误。
      【解决方案3】:

      我认为其他两个答案非常简单。他们俩都在解决您现在要解决的问题,但是一旦您拥有越来越多的工人/工作,这些解决方案就会失败,因为他们的所有解决方案都是 O(N*N)。这个解是 O(N)。

          subqry = models.Subquery(Job.objects.filter(worker_id=models.OuterRef('worker_id'))
                                              .order_by('-start_date').values('id')[:1])
          workers = Worker.objects.prefetch_related(models.Prefetch('job_set',
                                                                    queryset=Job.objects.filter(id__in=subqry)))
      
          for worker in workers:
              # no matter what this will always have 1 // or nothing, depending on your logic; if nothing, fix this.
              latest_job = list(worker.job_set.all())[0]  
              print(worker.name, latest_job.location, latest_job.start_date)
      

      这将对Worker 进行一次查询,就像其他的一样,但只会对最新的作业进行一次查询,其他解决方案将查询每个工作人员,这是低效且缓慢的。

      有关我如何测试所有这些的更多背景信息,请参阅此示例。 https://gist.github.com/kingbuzzman/ac2ada9c27196fc90c1b75f2d01a6271#file-django_prefetch_limit-py-L163

      【讨论】:

      • 您是对的,通过将操作减少到单个 SQL 查询中,您可以获得数量级的更好性能。但是数据库仍然需要完成这项工作,我很确定 O(1) 是不可能的。
      • @HåkenLid 检查我的测试,它的 O(1)。与每个工作人员进行查询相比,数据库上的负载是最小的。这将进行 2 次查询,无论您有多少 Workers/Jobs。
      • @dahyunism 我猜你在使用 django djangoproject.com/download/#supported-versions 和对 1.11 的支持将于 2020 年 1 月 1 日与 python 2.7 一起被删除——我谦虚地建议你尽快升级。
      • @HåkenLid 你是对的,这个问题的 O(1) 是不可能的,它是 O(N)。阅读这个网站让我知道我是多么愚蠢cooervo.github.io/Algorithms-DataStructures-BigONotation/…
      • 酷。我不想在 cmets 中开始讨论它,因为它与原始问题无关。无论如何,您的解决方案更快,因为关系数据库管理系统执行查询操作比在 django/python 中处理数据要快得多。即使大 O 中的算法复杂度相同,但实际上两种实现之间可能存在巨大差异。
      猜你喜欢
      • 2013-02-24
      • 2022-01-04
      • 1970-01-01
      • 1970-01-01
      • 2014-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-30
      相关资源
      最近更新 更多