perf: prefetch for serializers on network view

Reduces request time by more than 50%
This commit is contained in:
James Graham
2021-04-25 15:11:21 +01:00
parent 7d14fed90f
commit 7681e78a50
2 changed files with 29 additions and 34 deletions

View File

@@ -84,8 +84,8 @@
{{ organisation_relationship_set|json_script:'organisation-relationship-set-data' }} {{ organisation_relationship_set|json_script:'organisation-relationship-set-data' }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.14.0/cytoscape.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.18.2/cytoscape.min.js"
integrity="sha256-rI7zH7xDqO306nxvXUw9gqkeBpvvmddDdlXJjJM7rEM=" integrity="sha512-CBGCXtszkG5rYlQSTNUzk54/731Kz28WPk2uT1GCPCqgfVRJ2v514vzzf16HuGX9WVtE7JLqRuAERNAzFZ9Hpw=="
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"

View File

@@ -6,7 +6,7 @@ import logging
import typing import typing
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q from django.db.models import Q, QuerySet
from django.forms import ValidationError from django.forms import ValidationError
from django.utils import timezone from django.utils import timezone
from django.views.generic import TemplateView from django.views.generic import TemplateView
@@ -16,34 +16,47 @@ from people import forms, models, serializers
logger = logging.getLogger(__name__) # pylint: disable=invalid-name logger = logging.getLogger(__name__) # pylint: disable=invalid-name
def filter_by_form_answers(model: typing.Type, answerset_model: typing.Type, relationship_key: str): def filter_by_form_answers(queryset: QuerySet, answerset_queryset: QuerySet, relationship_key: str):
"""Build a filter to select based on form responses.""" """Build a filter to select based on form responses."""
def inner(form, at_date): def inner(form):
answerset_set = answerset_model.objects.filter( # Filter on timestamp__date doesn't seem to work on MySQL
# To compare datetimes we need at_date to be midnight at
# the *end* of the day in question - so add one day
at_date = form.cleaned_data['date']
if not at_date:
at_date = timezone.now().date()
at_date += timezone.timedelta(days=1)
# Filter to answersets valid at required time
answerset_set = answerset_queryset.prefetch_related('question_answers').filter(
Q(replaced_timestamp__gte=at_date) Q(replaced_timestamp__gte=at_date)
| Q(replaced_timestamp__isnull=True), | Q(replaced_timestamp__isnull=True),
timestamp__lte=at_date timestamp__lte=at_date
) )
# Filter answers to relationship questions # Filter to answersets containing required answers
for field, values in form.cleaned_data.items(): for field, values in form.cleaned_data.items():
if field.startswith(f'{form.question_prefix}question_') and values: if field.startswith(f'{form.question_prefix}question_') and values:
answerset_set = answerset_set.filter(question_answers__in=values) answerset_set = answerset_set.filter(question_answers__in=values)
return model.objects.filter(pk__in=answerset_set.values_list(relationship_key, flat=True)) return queryset.filter(pk__in=answerset_set.values_list(relationship_key, flat=True))
return inner return inner
filter_relationships = filter_by_form_answers( filter_relationships = filter_by_form_answers(
models.Relationship, models.RelationshipAnswerSet, 'relationship' models.Relationship.objects.prefetch_related('source', 'target'),
models.RelationshipAnswerSet.objects, 'relationship'
) )
filter_organisations = filter_by_form_answers( filter_organisations = filter_by_form_answers(
models.Organisation, models.OrganisationAnswerSet, 'organisation' models.Organisation.objects, models.OrganisationAnswerSet.objects, 'organisation'
) )
filter_people = filter_by_form_answers(models.Person, models.PersonAnswerSet, 'person') filter_people = filter_by_form_answers(
models.Person.objects, models.PersonAnswerSet.objects, 'person'
)
class NetworkView(LoginRequiredMixin, TemplateView): class NetworkView(LoginRequiredMixin, TemplateView):
@@ -92,39 +105,21 @@ class NetworkView(LoginRequiredMixin, TemplateView):
if not all(map(lambda f: f.is_valid(), all_forms.values())): if not all(map(lambda f: f.is_valid(), all_forms.values())):
return context return context
# Filter on timestamp__date doesn't seem to work on MySQL
# To compare datetimes we need at_date to be midnight at
# the *end* of the day in question - so add one day to each
relationship_at_date = all_forms['relationship'].cleaned_data['date']
if not relationship_at_date:
relationship_at_date = timezone.now().date()
relationship_at_date += timezone.timedelta(days=1)
person_at_date = all_forms['person'].cleaned_data['date']
if not person_at_date:
person_at_date = timezone.now().date()
person_at_date += timezone.timedelta(days=1)
organisation_at_date = all_forms['organisation'].cleaned_data['date']
if not organisation_at_date:
organisation_at_date = timezone.now().date()
organisation_at_date += timezone.timedelta(days=1)
context['person_set'] = serializers.PersonSerializer( context['person_set'] = serializers.PersonSerializer(
filter_people(all_forms['person'], person_at_date), many=True filter_people(all_forms['person']), many=True
).data ).data
context['organisation_set'] = serializers.OrganisationSerializer( context['organisation_set'] = serializers.OrganisationSerializer(
filter_organisations(all_forms['organisation'], organisation_at_date), many=True filter_organisations(all_forms['organisation']), many=True
).data ).data
context['relationship_set'] = serializers.RelationshipSerializer( context['relationship_set'] = serializers.RelationshipSerializer(
filter_relationships(all_forms['relationship'], relationship_at_date), many=True filter_relationships(all_forms['relationship']), many=True
).data ).data
context['organisation_relationship_set'] = serializers.OrganisationRelationshipSerializer( context['organisation_relationship_set'] = serializers.OrganisationRelationshipSerializer(
models.OrganisationRelationship.objects.all(), many=True models.OrganisationRelationship.objects.prefetch_related('source', 'target').all(),
many=True
).data ).data
for person in models.Person.objects.all(): for person in models.Person.objects.all():