0

I'm developing a web app using Django and Django Rest Framework and struggling with slow API responses which appear to be based on fetching data from related tables.

Test case 1 runs very quickly because the fields are directly tied to the Host model. As soon as I introduce a many-to-many field, it slows down dramatically.


Test case 1 - takes 250ms

serializers.py

class HostSerializer(serializers.Serializer):
    created = serializers.DateTimeField()
    last_updated = serializers.DateTimeField()
    name = serializers.CharField();

Test case 2 - takes 30s

serializers.py

class HostSerializer(serializers.Serializer):
    created = serializers.DateTimeField()
    last_updated = serializers.DateTimeField()
    name = serializers.CharField();
    applications = serializers.SerializerMethodField(read_only=True)

    def get_applications(self, instance):
        return [item.name for item in instance.applications.all()]

models.py

class Host(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)
    name = models.CharField(unique=True, max_length=settings.MAX_CHAR_COUNT)
    ip_addresses = models.ManyToManyField(IPAddress, blank=True)
    open_ports = models.ManyToManyField(Port, blank=True)
    domain = models.ForeignKey('Domain', on_delete=models.CASCADE)

    applications = models.ManyToManyField(Application, blank=True)
    tls = models.ForeignKey(TLS, on_delete=models.CASCADE, blank=True, null=True)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name

    def __eq__(self, other):
        if isinstance(other, Host):
            return self.name == other.name
        return False

    def __hash__(self) -> int:
        return hash(self.name)

    def __lt__(self, other):
        return self.name < other.name

views.py

class HostList(generics.ListAPIView):
    """
    List all hosts for a given domain
    """
    host_service = HostService()
   
    def get(self, request, domain_name, format=None):
        hosts = self.host_service.find_hosts_by_domain_name(domain_name)

        if len(hosts) == 0:
            return Response(status=status.HTTP_404_NOT_FOUND)

        serializer = HostSerializer(hosts, many=True)
        page = self.paginate_queryset(serializer.data)
        return self.get_paginated_response(page)
1
  • Could you post HostService .find_hosts_by_domain_name method code? Commented Jul 29, 2020 at 13:54

1 Answer 1

2

I was able to resolve my performance issues by making a few changes to the view.

  • Refactored the view and added prefetch_related to views.py

    class HostList(generics.ListAPIView):
        """
        List all hosts for a given domain
        """
        host_service = HostService()
        serializer_class = HostSerializer
    
        def get_queryset(self):
            return Host.objects.filter(domain__name__iexact=self.kwargs['domain_name'])\
                .prefetch_related('applications')
    
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.