Django REST框架按字段分组并添加额外内容
我有一个订票模型Django REST框架按字段分组并添加额外内容,django,django-rest-framework,Django,Django Rest Framework,我有一个订票模型 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 Movi
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)
我想用它的用户
字段过滤MovieTicket,并根据它的显示
字段对它们进行分组,并按最近预定的时间订购它们。并使用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}]>
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
]))
>>MovieTicket.objects.filter(用户=23).订购人('-booked_at').值('show').注释(总票数=Count('show'))
但这并不是根据节目进行分组。另外,我如何添加其他相关字段(例如,
show\uu movie\uu name
,show\uu day\uu date
,show\uu time
)您必须按节目分组,然后计算电影票总数
MovieTicket.objects.filter(user=23).values('show').annotate(total_tickets=Count('show')).values('show', 'total_tickets', 'show__movie__name', 'show__time', 'show__day__date'))
将此序列化器类用于上述查询集。它将提供所需的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()
由于按节目分组时,信息丢失,因此无法按预定地点订购。如果我们在group by预订,则会在unique booked_上发生并显示ID,这就是票数为1的原因。没有订单,你会得到正确的计数
编辑:
使用此查询:
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中找到总票数。要计算唯一演出ID的总门票数量,请执行以下操作:
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()
上述相同的序列化程序可用于这些对象
我想用MovieTicket的用户字段过滤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,,
电影:《狮子王》,
时间:"晚上七时",
日期:“2017年4月23日”,
门票总数=2张
},
{
节目:7,,
电影:《消失的女孩》,
时间:"下午二时三十分",
日期:“2017年4月23日”,
总票数=1
}
]
这个json数据与您描述的查询不同。您可以通过扩展注释并将show\u movie\u name
添加到.values
子句中来添加total\u tickets
:这会将分组更改为show+movie\u name,但由于show只有一个movie\u name,因此没有关系
但是,您不能添加show\uuu day\uu date
和show\uu 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')
)
你可以试试这个
Show.objects.filter(movieticket_sets__user=23).values('id').annotate(total_tickets=Count('movieticket_set__user')).values('movie__name', 'time', 'day').distinct()
或
我在数据库模型的图形上更一般地解释它。它可以应用于任何带有额外内容的“分组依据”
+-------------------------+
| MovieTicket (booked_at) |
+-----+--------------+----+
| |
+---------+--------+ +--+---+
| Show (time) | | User |
++----------------++ +------+
| |
+------+-------+ +-----+------+
| Movie (name) | | Day (date) |
+--------------+ +------------+
问题是:如何总结MovieTicket(最顶端的对象),按Show(一个相关对象)分组,并按用户(其他相关对象)筛选,报告来自一些相关深层对象(电影和日期)的详细信息,并按组从最顶端模型聚合的某个字段对这些结果进行排序(截至集团最近一部电影的预定时间):
答案由更一般的步骤解释:
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')
)
- 从最顶层的模型开始:
(MovieTicket.objects…)
- 应用过滤器:
.filter(用户=用户)
- 重要的是,根据最近的相关模型(至少是那些没有被过滤器设置为常量的模型)的
进行分组-这只是“显示”(因为“用户”对象仍然被过滤到一个用户)pk
.values('show_id')
即使所有其他字段在一起都是唯一的(show\u movie\u name、show\u day\u date、show\u time),数据库引擎优化器最好按show\u id对查询进行分组,因为所有其他字段都依赖于show\u id,并且不会影响组数 - 注释必要的聚合函数:
.annotation(总票数=计数('show'),最后一次订票=最大值('booked'at'))
- 添加必需的相关字段:
值('show\u id'、'show\u movie\u name'、'show\u day\u date'、'show\u time')
- 按需要排序:
(从最新到最旧的降序).order\u by('-最后一次预订)
如果不使用聚合函数对最顶层模型的任何字段进行封装,则不输出或对其进行排序是非常重要的。(
和Min
函数适用于从组中取样。每个未使用聚合函数封装的字段都将添加到“分组依据”中列出,这将打破预定的群体。可以逐步为朋友预订同一节目的更多门票,但应一起计算,并在最新预订中报告。)Max
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')
)
queryset可以很容易地转换为JSON如何在他的答案中演示,或者直接用于对django rest框架不感兴趣的人:
>>> 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}]>
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
]))
验证查询: 打印(str(qs.query))
选择app\u movieticket.show\u id、app\u movie.name、app\u day.date、app\u show.time、,
将(app\u movieticket.show\u id)计入总票数,
MAX(app_movieticket.booked_at)作为最后一次预订
来自app_movieticket
内部加入app\u show ON(app\u movieticket.show\u id=app\u show.id)
内部加入app\u movie ON(app\u show.movie\u id=app\u movie.id)
内部加入app_day ON(app_show.day_id=app_day.id)
其中app_movieticket.user_id=23
按app_movieticket.show_id、app_movie.name、app_day.date、app_show.time分组
按上次预订说明订购
注意事项:
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')
)
- 模型图类似于ManyToMa