Django反向外键

时间:2019-07-23 17:33:27

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

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

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对象,包括其先前的作业。我只想向工人查询最新的工作地点和开始日期。

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)

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

3 个答案:

答案 0 :(得分:1)

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

    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进行单个查询,但BUT将仅对最新工作进行单个查询,其他解决方案将使查询成为PER工作者,这效率低下且速度慢。

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

答案 1 :(得分:0)

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

例如,如果工人的名字是foobar,则可以执行以下操作:

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

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

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

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

答案 2 :(得分:0)

例如,您可以对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)