在 Django 中注释相关的和多重过滤的对象
Annotating related and multi-filtered objects in Django
我有一个配置文件查询集:
型号:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, unique=True)
...
查看:
Profile.objects.select_related('user')
每个 user/profile 每天可以注册多个活动:
型号:
class Event(models.Model):
title = models.CharField(max_length=120)
date = models.DateField(default=default_event_date)
...
class Registration(models.Model):
event = models.ForeignKey(Event)
student = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
block = models.ForeignKey(Block, on_delete=models.CASCADE)
....
给定一个日期,我如何注释(?我想这就是我想要的?)每个块一个注册对象(在 user/profile 和 Event__Date 上过滤)
最后,我想在我的模板中输出的是这样的:
For Date: 19 Dec 2016
User/Profile Block A Block B ...
user1 None None
user2 Event1 Event2
user3 Event3 None
...
编辑
尝试 1. 这是我第一次尝试完成此操作。我怀疑这是非常低效的,并且在生产中会非常缓慢,但至少它有效。如果有人能提供更高效、更优雅的解决方案,我们将不胜感激! (请注意,这还包括针对 homeroom_teacher 的用户配置文件模型的过滤器,该过滤器未包含在原始问题中,但我离开这里是因为这是有效的代码)
注册模型管理员
class注册管理器(models.Manager):
def homeroom_registration_check(self, event_date, homeroom_teacher):
students = User.objects.all().filter(is_staff=False, profile__homeroom_teacher=homeroom_teacher)
students = students.values('id', 'username', 'first_name', 'last_name')
# convert to list of dicts so I can add 'annotate' dict elements
students = list(students)
# get queryset with events? optimization for less hits on db
registrations_qs = self.get_queryset().filter(event__date=event_date, student__profile__homeroom_teacher=homeroom_teacher)
# append each students' dict with registration data
for student in students:
user_regs_qs = registrations_qs.filter(student_id=student['id'])
for block in Block.objects.all():
# add a new key:value for each block
try:
reg = user_regs_qs.get(block=block)
student[block.constant_string()] = str(reg.event)
except ObjectDoesNotExist:
student[block.constant_string()] = None
return students
模板
请注意 block.constant_string() --> "ABLOCK"、"BBLOCK" 等,这是在 block.constant_string() 方法中硬编码的,我不确定如何解决这个问题要么。
{% for student in students %}
<tr >
<td>{{ student.username }}</td>
<td>{{ student.first_name }}</td>
<td>{{ student.last_name }}</td>
<td>{{ student.ABLOCK|default_if_none:'-' }}</td>
<td>{{ student.BBLOCK|default_if_none:'-' }}</td>
</tr>
{% endfor %}
可能您不需要使用注释,但可以在模板中使用 regroup 标签。
从最后开始。您要在页面上显示的所有信息都可以从注册模型中检索。
获取按活动日期筛选的所有注册:
registrations = Registration.objects.filter(event__date = date)
之后你必须 regroup 由用户在模板中。
但是我发现了以下问题:
- 我不确定重组标签是否与查询集一起正常工作。所以可能你必须将数据转换成列表。但是,我发现这个 answer 其中使用了查询集。
- 即使您要重组,您也需要在模板中使用一些逻辑来定义事件的块。
为了解决硬编码名称的问题,我会稍微修改您的解决方案,使其看起来像这样:
def homeroom_registration_check(event_date, homeroom_teacher):
students = User.objects.filter(
is_staff=False,
profile__homeroom_teacher=homeroom_teacher,
)
block_ids = Block.objects.values('id')
for student in students:
table_row = []
for block_id in block_ids:
try:
reg = Registration.objects.get(
student=student,
block=block_id,
event__date=event_date,
)
table_row.append(reg.event)
except ObjectDoesNotExist:
table_row.append(None)
yield (student, table_row)
我会从模型管理器中取出并放入 views.py 或单独的文件(如 table.py )。对我来说似乎更清晰,但这只是一种意见 - 你可以将这段代码放在模型管理器中,它无论如何都会 运行。
然后在您的模板中:
{% for student, row in homeroom_reg_check %}
<tr>
<td>{{ student.username }}</td>
<td>{{ student.other_data }}</td>
{% for event in row %}
<td>{{ event.name|default_if_none:'-' }}</td>
{% endfor %}
</tr>
{% endfor %}
我有一个配置文件查询集:
型号:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, unique=True)
...
查看:
Profile.objects.select_related('user')
每个 user/profile 每天可以注册多个活动:
型号:
class Event(models.Model):
title = models.CharField(max_length=120)
date = models.DateField(default=default_event_date)
...
class Registration(models.Model):
event = models.ForeignKey(Event)
student = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
block = models.ForeignKey(Block, on_delete=models.CASCADE)
....
给定一个日期,我如何注释(?我想这就是我想要的?)每个块一个注册对象(在 user/profile 和 Event__Date 上过滤)
最后,我想在我的模板中输出的是这样的:
For Date: 19 Dec 2016
User/Profile Block A Block B ...
user1 None None
user2 Event1 Event2
user3 Event3 None
...
编辑
尝试 1. 这是我第一次尝试完成此操作。我怀疑这是非常低效的,并且在生产中会非常缓慢,但至少它有效。如果有人能提供更高效、更优雅的解决方案,我们将不胜感激! (请注意,这还包括针对 homeroom_teacher 的用户配置文件模型的过滤器,该过滤器未包含在原始问题中,但我离开这里是因为这是有效的代码)
注册模型管理员
class注册管理器(models.Manager):
def homeroom_registration_check(self, event_date, homeroom_teacher):
students = User.objects.all().filter(is_staff=False, profile__homeroom_teacher=homeroom_teacher)
students = students.values('id', 'username', 'first_name', 'last_name')
# convert to list of dicts so I can add 'annotate' dict elements
students = list(students)
# get queryset with events? optimization for less hits on db
registrations_qs = self.get_queryset().filter(event__date=event_date, student__profile__homeroom_teacher=homeroom_teacher)
# append each students' dict with registration data
for student in students:
user_regs_qs = registrations_qs.filter(student_id=student['id'])
for block in Block.objects.all():
# add a new key:value for each block
try:
reg = user_regs_qs.get(block=block)
student[block.constant_string()] = str(reg.event)
except ObjectDoesNotExist:
student[block.constant_string()] = None
return students
模板 请注意 block.constant_string() --> "ABLOCK"、"BBLOCK" 等,这是在 block.constant_string() 方法中硬编码的,我不确定如何解决这个问题要么。
{% for student in students %}
<tr >
<td>{{ student.username }}</td>
<td>{{ student.first_name }}</td>
<td>{{ student.last_name }}</td>
<td>{{ student.ABLOCK|default_if_none:'-' }}</td>
<td>{{ student.BBLOCK|default_if_none:'-' }}</td>
</tr>
{% endfor %}
可能您不需要使用注释,但可以在模板中使用 regroup 标签。
从最后开始。您要在页面上显示的所有信息都可以从注册模型中检索。
获取按活动日期筛选的所有注册:
registrations = Registration.objects.filter(event__date = date)
之后你必须 regroup 由用户在模板中。
但是我发现了以下问题:
- 我不确定重组标签是否与查询集一起正常工作。所以可能你必须将数据转换成列表。但是,我发现这个 answer 其中使用了查询集。
- 即使您要重组,您也需要在模板中使用一些逻辑来定义事件的块。
为了解决硬编码名称的问题,我会稍微修改您的解决方案,使其看起来像这样:
def homeroom_registration_check(event_date, homeroom_teacher):
students = User.objects.filter(
is_staff=False,
profile__homeroom_teacher=homeroom_teacher,
)
block_ids = Block.objects.values('id')
for student in students:
table_row = []
for block_id in block_ids:
try:
reg = Registration.objects.get(
student=student,
block=block_id,
event__date=event_date,
)
table_row.append(reg.event)
except ObjectDoesNotExist:
table_row.append(None)
yield (student, table_row)
我会从模型管理器中取出并放入 views.py 或单独的文件(如 table.py )。对我来说似乎更清晰,但这只是一种意见 - 你可以将这段代码放在模型管理器中,它无论如何都会 运行。
然后在您的模板中:
{% for student, row in homeroom_reg_check %}
<tr>
<td>{{ student.username }}</td>
<td>{{ student.other_data }}</td>
{% for event in row %}
<td>{{ event.name|default_if_none:'-' }}</td>
{% endfor %}
</tr>
{% endfor %}