beugene
@beugene
Oracle Developer, Delphi, Python, Django

Возможно ли в шаблоне использовать косвенное обращение к переменной контекста?

Суть задачи - необходимо вывести таблицу с итогами по категориям. Например:
bacbdb533b9846039c095e87d6577d2e.jpg
Каждый пользователь выбирает свои категории. По каждой категории можно добавить несколько признаков. В пересечении пользователя и категории отображается количество признаков.
Для построения таблицы применяю SQL запрос и создаю переменную контекста.
sql = 'select pu.id, pu.user_id, '
for section in project.sections.all():
    sql += '  (select COUNT(*) from section ...
    sql += '   where pus.section_id = ' str(section.id)
    sql += '       and pus.id = pusp.profile_user_section_id ) count_in_section_' + str(section.id) + ','
sql += ' u.first_name, u.last_name '
sql += ' from ...'
sql += ' where ... '
users_list = list(project.profile_users.raw(sql, params=[project_id]))
context = {'project': project,
               'users_list': users_list,
               }
return render(request, template, context)

Таким образом создаются колонки с итогами.
У меня вопрос, как обратиться в шаблоне к соответствующей колонке категории по ее названию сохраненному в переменной контекста, например: column_name_1 ='item.count_in_section_' + str(section.id)
...
{% for item in users_list %}
                <tr>
                <td>{{ item.id }}</td>
                <td>{{ item.first_name }}</td>
                <td>{{ item.last_name }}</td>
                   {% for section in project.sections.all %}
                       <td>
                      <b> {{ column_name_1 }}</b>  <!-- ??? Нужна косвенная адресация -->
                       </td>
                   {% endfor %}
                </tr>
{% endfor %}
...

Возможно это подход не имеет решения, но я надеюсь, что существует способ получения результата. Если есть идеи прошу поделиться с ними. Я нашел одно решение, без "косвенной адресации", но мне оно не нравится.

Дополняю вопрос моделями. Файл models.py:
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

# Create your models here.


class Section(models.Model):
    name = models.CharField(max_length=25, verbose_name=_('Категория'))


class Criterion(models.Model):
    name = models.CharField(max_length=250, verbose_name=_('Критерий'))


class ProfileUser(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, verbose_name=_('Пользователь'), unique=True)
    user_info = models.CharField(max_length=100)


class Project(models.Model):
    profile_users = models.ManyToManyField(ProfileUser, verbose_name=_('Участники'), through='ProjectProfileUser')


class ProjectProfileUser(models.Model):
    profile_user = models.ForeignKey(ProfileUser, on_delete=models.CASCADE, verbose_name=_('Пользователь'))
    project = models.ForeignKey(Project, on_delete=models.CASCADE, verbose_name=_('Проект'))
    sections = models.ManyToManyField(Section, through='ProfileUserSection', verbose_name=_('Категории'))


class ProfileUserSection(models.Model):
    project_profile_user = models.ForeignKey(ProjectProfileUser, on_delete=models.CASCADE, verbose_name=_('Пользователь'))
    section = models.ForeignKey(Section, on_delete=models.CASCADE, verbose_name=_('Категория'))
    сriterions = models.ManyToManyField(Criterion, verbose_name=_('Критерии'), through='ProfileUserSectionCriterion')


class ProfileUserSectionCriterion(models.Model):
    criterion = models.OneToOneField(Criterion, on_delete=models.CASCADE, verbose_name=_('Критерий'))
    profile_user_section = models.ForeignKey(ProfileUserSection, on_delete=models.CASCADE, verbose_name=_('Категория'))
  • Вопрос задан
  • 298 просмотров
Решения вопроса 2
Если правильно понимаю, каждая строка отвечает одному `ProfileUser`, а каждый столбец одной `Section`. Причём не всем, а только тем `Section`, которые принадлежат текущему проекту. Каждая ячейка, следовательно, есть число всех объектов типа `ProfileUserSection`, отвечающих соответствующим `ProfileUser` и `Section`. Правильно я понимаю?

Адресация в шаблоне невозможна, это умышленное ограничение. Поэтому вам придётся передать в шаблон уже готовую матрицу ячеек. Ну например следующим образом - не ручаюсь за оптимальность, впрочем.

sections = list(Section.objects.filter(project=project, ...))
users = list(ProfileUser.objects.filter(...))

cells = [[user] + [
    ProfileUserSection.objects.filter(user=user, section=section).count() for section in sections
] for user in users]

context = {
    'sections': sections,
    'rows': cells
}


Шаблон:
{% for row in rows %}
<tr>
    {% for cell in row %}
        <td>{{ cell }}</td>
    {% endfor %}
</tr>
Ответ написан
beugene
@beugene Автор вопроса
Oracle Developer, Delphi, Python, Django
@Anatoly Scherbakov
Идею понял. Спасибо за ответ.
[[
ProfileUserSection.objects.filter(user=user, section=section).annotate(count_сriterions=Count('сriterions')) for section in sections
] for user in users]

Хочу написать свое решение и еще раз поблагодарить Анатолия за верное направление в решении.
Ответ на вопрос: косвенная адресация недоступна и ненужна. SQL - решение в лоб. В django есть средства для красивого решения.

Итак:
views.py
# coding: utf-8
from django.shortcuts import render

# Create your views here.
def report(request, project_id="1")
    project = Project.objects.get(pk=project_id)
    # Получить список пользователей проекта
    users_list = list(ProjectProfileUser.objects.filter(project=project_id))
    # Получить список категорий проекта
    sections_list = list(project.sections.all())
    # Получить итоги (кол-во критериев на пересечении пользователя и категории) + совместить с информацией по пользователю
    result_list = [list(map(lambda x: list(x), ProfileUser.objects.filter(project=project_id, projectprofileuser=user).values_list(
        'user__id', 'user__first_name', 'user__last_name'))) +
                   [ProfileUserSection.objects.filter(section=section,
                                                      project_profile_user=user).aggregate(
                                                      count_criterions=Count('criterions'))['count_criterions']
                    for section in sections_list] for user in users_list]
    # print('res=', result_list)
    context = {'project': project,
               'result_list': result_list,
               'rows_count': len(result_list),
               }
    template = 'report.html'
    return render(request, template, context)


report.html
...
       <table class="table">
            <thead>
                <tr>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td align="center" colspan="{{ project.sections.all.count }}"><b>{% trans 'Категория' %}</b></td>
                </tr>
                <tr>
                   <td><b>{% trans 'ID' %}</b></td>
                   <td><b>{% trans 'Фамилия' %}</b></td>
                   <td><b>{% trans 'Имя' %}</b></td>
                   {% for section in project.sections.all %}
                       <td align="center"><b>'{{ section.caption }}'</b></td>
                   {% endfor %}
                </tr>
            </thead>
            <tbody>
            {% for row in result_list %}
                <tr>
                {% for item in row %}
                    {% if forloop.counter == 1 %}
                        {% for cell in item %}
                            <td>{{ cell }}</td>
                        {% endfor %}
                    {% else %}
                        <td align="center">{{ item }}</td>
                    {% endif %}
                {% endfor %}
                </tr>
            {% endfor %}
            </tbody>
        </table>
...
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы