Шаблон Django работает медленно при создании отчета (много переменных, много моделей, много списков) - PullRequest
0 голосов
/ 10 января 2019

Я новичок, пытающийся использовать Django для создания отчета об участниках команды, и я делаю что-то в корне неверное и потратил несколько часов, пытаясь выяснить, что происходит не так - отчет все еще попадает в БД около 4000+ раз. Сокращенная версия того, что я делаю, приведена ниже.

& # x200B;

Любая помощь, указатели или другие ресурсы будут много приветствуется!

& # x200B;

Для модели команды:

class Team(BenchmarkData):
    year = models.ForeignKey(Period, on_delete=models.CASCADE, null=True)
    sponsor = models.ForeignKey(Person, on_delete=models.CASCADE)
    goal = models.ForeignKey(Goal, on_delete=models.CASCADE, null=True)
        ...other non-relational properties...

    def _participation_queryset(self):
        from .participation import Participation
        pp = Participation.objects.filter(team=self.id)
                return pp

    @cached_property
    def average_points(self):
        list_participation = [participation.calculated_yearly_points_per_hour for participation in self._participation_queryset()] 
        try: return mean(list_participation)

    @cached_property
    ...

Для модели участия:

class Participation(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE, null=True)
    team = models.ForeignKey(Team, on_delete=models.CASCADE, null=True)
    year = models.ForeignKey(Period, on_delete=models.CASCADE, null=True)

    start_date = models.DateField('Began', null=True)
    end_date = models.DateField('Left', blank=True, null=True)
    active = models.BooleanField('Active?', null=True)

    yearly_points = models.FloatField(default=0, null=True)
    yearly_hours_of_play = models.FloatField(default=0)
        ...other non-relational properties...

    @cached_property
    def calculated_yearly_points_per_hour(self):
        try: return self.yearly_points / self.yearly_hours_of_play
        except ZeroDivisionError: return 0

        ...other cached properties...

Для моего просмотра я использовал:

class PlanReport(TemplateView):
    template_name = 'pension/report/index.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        self.team = get_object_or_404(Plan, id=self.kwargs['team_id'])
        self.sponsor = get_object_or_404(Company, id=self.kwargs['sponsor_id'])
        self.report = get_object_or_404(Report, id=self.kwargs['plan_id'])

        pp = Participation.objects.filter(plan_id=self.plan)
        s = pickle.dumps(pp.query)
        s2 = pickle.loads(s)
        participation_list = s2.model.objects.all()

        # Add in other context information
        context['team'] = self.team
        context['sponsor'] = self.sponsor
        context['report'] = self.report
        context['participation_list'] = participation_list

        return context

Структура шаблона отчета:

index.html
 - section1.html
 - section2-table-of-group-participants-with-stats-on-each-participant.html
...

А шаблоны для таблицы показывают:

...other html...
    {% for participation in participation_list %}
        <tr>
            <td>{{ participation.person }}</td>
            <td>{{ participation.team }}</td>
            <td>{{ participation.year }}</td>
            <td>{{ participation.start_date }}</td>
            <td>{{ participation.end_date }}</td>
            <td>{{ participation.active }}</td>
            <td>{{ participation.yearly_points }}</td>
            <td>{{ participation.yearly_hours_of_play }}</td>
            <td>{{ participation.calculated_yearly_points_per_hour }}</td>
                ...other calculated attributes...
    </tr>
    {% empty %}
    </tbody>
    </table>
    <p>No data imported into the list</p>
    {% endfor %}

& # x200B;

Спасибо за ответы до сих пор!

@ Даниэль Роузман:

Спасибо большое.

Я возвратил все @cached_properies в @property, как вы упомянули (у меня было подозрение, что они замедляют работу!)

Я удалил всю информацию о рассолах - это было то, что я слышал от кого-то, что так пытался сделать, но не совсем понимал, как это очень хорошо связано - так что я думаю, что это не имеет большого отношения к кешированию - по крайней мере, в моей ситуации.

Я также изменил следующее:

В команде модель:

def _participation_queryset(self):
    from .participation import Participation
    return self.participation_set.all()

В представлении create_report:

    participation_list = self.team.participation_set.all().select_related('team')

    ...

    context['participation_list'] = participation_list

1 Ответ

0 голосов
/ 10 января 2019

В вашем коде есть несколько серьезных недостатков.

Во-первых, вы определили свой собственный способ привлечения участников в команду с помощью метода _participation_queryset. Это не только не нужно, но и нарушает встроенное кэширование Django; оно будет оцениваться при каждом вызове. Удалите его и обратитесь к self.participation_set.all().

Другое, и, возможно, даже более важное, одно: вы ничего не делаете для предварительной загрузки любых связанных данных для любой из ваших моделей. Таким образом, каждый вызов {{ participation.team }} вызывает попадание в базу данных, даже если у вас уже есть команда. Вы должны использовать select_related, когда выбираете набор запросов Участия в своем представлении (что вы должны сделать с помощью того же вызова participation_set).

(И я не могу понять, что вы делаете с этим дампом / загрузкой с рассолами. Почему, черт возьми, вы это делаете? Также обратите внимание, что нет причин назначать значения в качестве атрибутов экземпляра.)

Итак, ваш взгляд выглядит следующим образом:

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)

    team = get_object_or_404(Plan, id=self.kwargs['team_id'])
    sponsor = get_object_or_404(Company, id=self.kwargs['sponsor_id'])
    report = get_object_or_404(Report, id=self.kwargs['plan_id'])

    participation_list = team.participation_set.all().select_related('team')
    ...

Наконец, я не думаю, что ваши cached_properties делают то, о чем вы думаете, и, вероятно, делают вещи еще более неэффективными. Нечто вроде calculated_yearly_points_per_hour представляет собой простое деление двух целых чисел и вычисление тривиально; нет необходимости его кешировать. Удалите большинство, если не все эти декораторы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...