我有票预订模式
class Movie(models.Model):
name = models.CharField(max_length=254, unique=True)
class Show(models.Model):
day = models.ForeignKey(Day)
time = models.TimeField(choices=CHOICE_TIME)
movie = models.ForeignKey(Movie)
class MovieTicket(models.Model):
show = models.ForeignKey(Show)
user = models.ForeignKey(User)
booked_at = models.DateTimeField(default=timezone.now)
我想用其user
字段过滤MovieTicket,并根据其show
字段对它们进行分组,并按照最近预订的时间对它们进行排序。并使用Django REST框架回复json
数据,如下所示:
[
{
show: 4,
movie: "Lion king",
time: "07:00 pm",
day: "23 Apr 2017",
total_tickets = 2
},
{
show: 7,
movie: "Gone girl",
time: "02:30 pm",
day: "23 Apr 2017",
total_tickets = 1
}
]
我试过这种方式:
>>> MovieTicket.objects.filter(user=23).order_by('-booked_at').values('show').annotate(total_tickets=Count('show'))
<QuerySet [{'total_tickets': 1, 'show': 4}, {'total_tickets': 1, 'show': 4}, {'total_tickets': 1, 'show': 7}]>
但根据节目不分组。另外,我如何添加其他相关字段(例如show__movie__name
,show__day__date
,show__time
)
答案 0 :(得分:2)
我正在绘制数据库模型,让人难以忘怀。我还将以下内容写成“GROUP BY”的一般示例,并附加了额外的内容。
+-------------+
| MovieTicket |
++-----------++
| |
+-----+-----+ +--+---+
| Show | | User |
++---------++ +------+
| |
+---+---+ +--+--+
| Movie | | Day |
+-------+ +-----+
问题是:如何汇总按用户(其他相关对象)过滤的Show(一个相关对象)分组的MovieTicket(最顶层对象),并根据{{1}报告来自相关对象的视频并按分组中的某些聚合字段(按组中最近的MovieTicket的预订时间)对这些结果进行排序?
<强>答案:强>
Show
(MovieTicket.objects ...)
.filter(user=user)
进行分组(至少那些过滤器不能保持不变的模型) - 它只是“显示”(因为“用户”对象仍被过滤为一个用户)pk
.values('show_id')
.annotate(total_tickets=Count('show'), last_booking=Max('booked_at'))
.values('show_id', 'show__movie__name', 'show__day__date', 'show__time')
(从最新到最老的降序).order_by('-last_booking')
和Min
函数适合从组中采样),因为每个字段都没有被封装聚合将被添加到“分组依据”列表中,这将打破预期的组。把它放在一起:
Max
查询集可以很容易地转换为JSON 如何在他的答案中演示zaphod100.10,或者直接对于那些对django-rest框架不感兴趣的人:
from django.db.models import Max
qs = (MovieTicket.objects
.filter(user=user)
.values('show_id', 'show__movie__name', 'show__day__date', 'show__time')
.annotate(total_tickets=Count('show'), last_booking=Max('booked_at'))
.order_by('-last_booking')
)
验证查询:
from collections import OrderedDict
import json
print(json.dumps([
OrderedDict(
('show', x['show_id']),
('movie', x['show__movie__name']),
('time', x['show__time']), # add time formatting
('day': x['show__day__date']), # add date formatting
('total_tickets', x['total_tickets']),
# field 'last_booking' is unused
) for x in qs
]))
>>> print(str(qs.query))
备注:强>
模型图类似于ManyToMany关系,但MovieTickets是单个对象,可能包含座位号。
通过一个查询为更多用户提供类似的报告很容易。字段'user_id'和名称将添加到“values(...)”。
相关的模型日并不直观,但很明显,它有一个字段SELECT app_movieticket.show_id, app_movie.name, app_day.date, app_show.time,
COUNT(app_movieticket.show_id) AS total_tickets,
MAX(app_movieticket.booked_at) AS last_booking
FROM app_movieticket
INNER JOIN app_show ON (app_movieticket.show_id = app_show.id)
INNER JOIN app_movie ON (app_show.movie_id = app_movie.id)
INNER JOIN app_day ON (app_show.day_id = app_day.id)
WHERE app_movieticket.user_id = 23
GROUP BY app_movieticket.show_id, app_movie.name, app_day.date, app_show.time
ORDER BY last_booking DESC
,并且希望也有一些非平凡的字段,对于像电影假期这样的事件安排节目可能很重要。将字段'date'设置为date
模型的主键并在许多查询中经常备用关系会很有用。
遗憾的是,所有必要的一切都可以在最早的两个答案中找到(Todor和zaphod100.10),但它们并没有结合在一起得到一个完整的答案,而不是由任何人投票,甚至这个问题有很多选票。
答案 1 :(得分:1)
您必须按节目分组,然后计算电影票的总数。
MovieTicket.objects.filter(user=23).values('show').annotate(total_tickets=Count('show')).values('show', 'total_tickets', 'show__movie__name', 'show__time', 'show__day__date'))
将此serilizer类用于上述查询集。它将提供所需的json输出。
class MySerializer(serializers.Serializer):
show = serailizer.IntegerField()
movie = serializer.StringField(source='show__movie__name')
time = serializer.TimeField(source='show__time')
day = serializer.DateField(source='show__day__date')
total_tickets = serializer.IntegerField()
订单_by scheduled_at是不可能的,因为当我们按show分组时,该信息会丢失。如果我们按scheduled_at group by订购将发生在唯一的registered_at次和show ids上,这就是票数计数的原因1.如果没有order_by,你将获得正确的计数。
编辑:
使用此查询:
queryset = (MovieTicket.objects.filter(user=23)
.order_by('booked_at').values('show')
.annotate(total_tickets=Count('show'))
.values('show', 'total_tickets', 'show__movie__name',
'show__time', 'show__day__date')))
您无法在带注释的字段上进行注释。所以你将在python中找到总票数。计算唯一show id的total_tickets计数:
tickets = {}
for obj in queryset:
if obj['show'] not in tickets.keys():
tickets[obj['show']] = obj
else:
tickets[obj['show']]['total_tickets'] += obj['total_tickets']
您需要的最终对象列表是tickets.values()
上面的同一个序列化程序可以与这些对象一起使用。
答案 2 :(得分:1)
我想用其用户字段过滤MovieTicket并将它们分组 根据其显示字段,并按最近预定的时间订购。
此queryset
会为您提供您想要的内容:
tickets = (MovieTicket.objects
.filter(user=request.user)
.values('show')
.annotate(last_booking=Max('booked_at'))
.order_by('-last_booking')
)
使用Django rest框架回复json数据,如下所示: [ { 显示:4, 电影:“狮子王”, 时间:“07:00 pm”, 日:“2017年4月23日”, total_tickets = 2 }, { 显示:7, 电影:“走了的女孩”, 时间:“02:30 pm”, 日:“2017年4月23日”, total_tickets = 1 } ]
这个json数据与你描述的查询不一样。您可以通过将注释和total_tickets
扩展到show__movie__name
子句来添加.values
:这会将分组更改为show + movie_name,但由于show只有一个movie_name,因此无关紧要。
但是,您无法添加show__day__date
和show__time
,因为一个节目有多个日期时间,那么您想从群组中获得哪一个?例如,您可以获取最大day
和time
,但这并不能保证您在当天+时间会有节目,因为这些是不同的字段,彼此不相关。所以最后的尝试可能如下:
tickets = (MovieTicket.objects
.filter(user=request.user)
.values('show', 'show__movie__name')
.annotate(
last_booking=Max('booked_at'),
total_tickets=Count('pk'),
last_day=Max('show__day'),
last_time=Max('show__time'),
)
.order_by('-last_booking')
)
答案 3 :(得分:1)
你可以试试这个。
Show.objects.filter(movieticket_sets__user=23).values('id').annotate(total_tickets=Count('movieticket_set__user')).values('movie__name', 'time', 'day').distinct()
OR
Show.objects.filter(movieticket_sets__user=23).values('id').annotate(total_tickets=Count('id')).values('movie__name', 'time', 'day').distinct()