【问题标题】:How to implement cross join in django for a count annotation如何在 django 中实现交叉连接以进行计数注释
【发布时间】:2021-01-18 21:34:03
【问题描述】:

我提出了我的问题的简化版本。我有场地和时间段以及用户和预订,如下面的模型描述所示。时间段对所有场地通用,用户可以在场地预订一个时间段,直到达到场地容量。

class Venue(models.Model):

    name = models.Charfield(max_length=200)
    capacity = models.PositiveIntegerField(default=0)


class TimeSlot(models.Model):

    start_time = models.TimeField()
    end_time = models.TimeField()


class Booking(models.Model):

    user = models.ForeignKey(User)
    time_slot = models.ForeignKey(TimeSlot)
    venue = models.ForeignKey(Venue)

现在我想尽可能高效地获取场地和时间段的所有可能组合,并注释每个组合的预订计数,包括预订数量为 0 的情况。

我已经设法在原始 SQL 中使用 Venue 和 TimeSlot 表上的交叉连接来实现这一点。大意如下。然而,尽管进行了详尽的搜索,但仍未找到 django 等价物。

SELECT venue.name, timeslot.start_time, timeslot.end_time, count(booking.id)
FROM myapp_venue as venue
CROSS JOIN myapp_timeslot as timeslot
LEFT JOIN myapp_booking as booking on booking.time_slot_id = timeslot.id
GROUP BY venue.name, timeslot.start_time, timeslot.end_time

我还可以对查询进行注释,以检索该组合的预订确实存在的预订计数。但那些预订量为 0 的组合被排除在外。示例:

qs = Booking.objects.all().values(
            venue=F('venue__name'),
            start_time=F('time_slot__start_time'),
            end_time=F('time_slot__end_time')
        ).annotate(bookings=Count('id')) \
            .order_by('venue', 'start_time', 'end_time')

如何使用django ORM实现CROSS JOIN查询的效果?

【问题讨论】:

    标签: python django join orm annotations


    【解决方案1】:

    我不相信 Django 有能力在不还原为原始 SQL 的情况下进行交叉连接。不过,我可以为您提供两个可以为您指明正确方向的想法:

    1. 查询和 python 循环的组合。

      venues = Venue.objects.all()
      time_slots = TimeSlot.objects.all()
      qs = ** your customer query above **
      
      # Loop through both querysets, to create a master list.
      venue_time_slots = []
      for venue in venues:
          for time_slot in time_slots:
              venue_time_slots.append(venue.name, time_slot.start_time, time_slot.end_time, 0)
      
      # Loop through master list and then compare to custom qs to update the count.
      for venue_time in venue_time_slots:
          for vt in qs:
              # Check if venue and time found. 
              if venue_time[0] == qs.venue and venue_time[1] == qs.start_time:
                  venue_time[3] += qs.bookings
                  break
      
    2. 我没有解决方案的更难的是使用过滤器、排除和联合的组合。我只使用了 3 个表(两个父母有一个子链接表),其中有 4 个,包括用户。所以我只能提供逻辑,不能举例。

      # Get all results that exist in table using .filter().
      first_query.filter()
      
      # Get all results that do not exist by using .exclude().
      # You can use your results from the first query to exclude also, but
      # would need to create an interim list.
      exclude_ids = [fq_row.id for fq_row in first_query]
      second_query.exclude(id__in=exclude_ids)
      
      # Combine both queries
      query = first_query.union(second_query)
      
      return query
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-11
      • 2015-08-25
      • 2012-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      相关资源
      最近更新 更多