在Django中注释相关和多重过滤的对象

时间:2016-12-19 23:33:55

标签: django django-queryset django-annotate

我有一个配置文件的查询集:

型号:

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, unique=True)
    ...

查看:

Profile.objects.select_related('user')

每个用户/个人资料可以每天注册多个事件:

型号:

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)
    ....

给定一个Date我怎样才能注释(?我认为这就是我想要的?)每个块一个注册对象(在用户/配置文件和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的用户配置文件模型的过滤器,该模型未包含在原始问题中,但我已离开此处,因为这是正在运行的代码)

注册模型管理员

类RegistrationManager(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 %}

2 个答案:

答案 0 :(得分:1)

要解决名称被编码的问题,我会稍微修改你的解决方案,如下所示:

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 %}

答案 1 :(得分:0)

可能您不需要使用注释,但可以在模板中使用regroup标记。

从最后开始。您可以从注册模型中检索要在页面上显示的所有信息。

获取按事件日期过滤的所有注册:

registrations = Registration.objects.filter(event__date = date)

之后您必须在模板中由用户regroup

但是我看到以下问题:

  1. 我不确定重新组合标记是否与查询集正常工作。所以可能你必须将数据转换成列表。但是,我发现这个answer使用了查询集。
  2. 即使您要重新组合,您也需要在模板中使用一些逻辑来定义事件的块。