Need Help to optimize the query that returns the list of products

Hi everyone! my project has been running normally for two years but recently, the server’s response on some pages that contain a lot of products has become very slow.

database: MySQL
here is the number of entries in the db:
Product: 1 458 624
Catalog: 2309

::::::#My Model

class TypeMenu(models.Model):
 name = models.CharField(max_length=256)
 template = models.CharField(max_length=1024)
 def __str__(self):
    return self.name

 class Meta:
    ordering = ["id"]
    verbose_name_plural = “Type menu”




class MenuCatalog(models.Model):
 name = models.CharField(max_length=255, unique=True)
 name_title = models.CharField(max_length=255, blank=True)
 slug = models.SlugField(max_length=255, blank=True, unique=True)
 order_number = models.FloatField(blank=True, null=True)
 parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
 parents = models.ManyToManyField('self', db_table='MenuCatalog_and_parent_MenuCatalog', related_name='_id', blank=True, symmetrical=False)
 type_menu = models.ForeignKey(TypeMenu, on_delete=models.CASCADE)
 is_hidden_child = models.BooleanField(default=False)
 is_hidden = models.BooleanField(verbose_name=“hide)

 def __str__(self):
     return self.name

 class Meta:
     ordering = ["order_number"]
     verbose_name_plural = “Menu/Catalog”

 def get_child(self):
     tmp = MenuCatalog.objects.filter(parent=self, is_hidden=False).only('name', 'slug', 'has_child')
    return tmp

 def get_childs(self):
     tmp = self.parents.all()
     return tmp

 def get_all_children(self):
     children = MenuCatalog.objects.filter(parent=self, is_hidden=False).only('id')
     return children

 def get_child_menu(self):
     tmp = self.parents.filter(is_hidden=False).only('name', 'slug', 'has_child') | MenuCatalog.objects.filter(parent=self, is_hidden=False).only('name', 'slug', 'has_child')
    return tmp


 def get_absolute_url(self):
    return '/{}/'.format(self.slug)





 class Product(models.Model):
  order_number = models.FloatField( blank=True, null=True)
  name = models.CharField(max_length=1024)
  name_full = models.CharField(max_length=1024, blank=True, null=True)
  slug = models.SlugField(max_length=1024, blank=True, null=True)
  catalog = models.ForeignKey(MenuCatalog, related_name='product_catalog_set', on_delete=models.CASCADE)
  marka = models.ForeignKey(Marka, blank=True, null=True, on_delete=models.CASCADE)
  standart = models.ForeignKey(Standart, blank=True, null=True, on_delete=models.CASCADE)
  available = models.CharField(max_length=128, blank=True, null=True)
  price = models.CharField(max_length=128, blank=True, null=True)
  image = models.CharField(max_length=256, blank=True, null=True)
  description = tinymce_models.HTMLField(blank=True, null=True)
  is_hidden = models.BooleanField(verbose_name="Hide", default=False)
  def __str__(self):
      return self.name

  class Meta:
      ordering = ["catalog"]
      verbose_name_plural = "Product"

  def get_absolute_url(self):
      return '/product/{}/'.format(self.slug)

::::::::::#my view.py

MAX_COUNT_PRODUCTS = 20
ID_TYPE_CATEGORY_LIST = 8
ID_TYPE_CATEGORY_LIST_2 = 11
 
class MenuView(View):
   def get(self, request, menu_slug):    
     current_menu = get_object_or_404(MenuCatalog, slug=menu_slug)

     if current_menu.type_menu_id == ID_TYPE_CATEGORY_LIST or current_menu.type_menu_id == ID_TYPE_CATEGORY_LIST_2:
        if current_menu.is_hidden:
            raise Http404()
        
        category_s_list = current_menu.get_all_children()
    
        if current_menu.type_menu_id == ID_TYPE_CATEGORY_LIST_2:
            product_list = Product.objects.filter(catalog__in=category_s_list)
        else:
            product_list = Product.objects.filter(catalog=current_menu)

        page = 1

        product_list_count = product_list.count()


        paginator_min = 1
        paginator_max = 10
        product_list = product_list.select_related('marka', 'standart')



        paginator = Paginator(product_list, MAX_COUNT_PRODUCTS)
        try:
            product_list = paginator.page(page)
            if int(page) < 5:
                paginator_min = 1
                paginator_max = 10
            else:
                paginator_min = max(int(page) - 5, 1)
                paginator_max = min(int(page) + 5, paginator.num_pages)

        except PageNotAnInteger:
            product_list = paginator.page(1)

        except EmptyPage:
            product_list = paginator.page(paginator.num_pages)

	  #I’m using pug js for the template
     response = render(request, current_menu.type_menu.template.replace('.html', '.pug'), locals())
     return response

Maybe I’m doing something wrong. So any help from you will be really useful :pray:

Perhaps you can try something like this:

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.http import Http404
from django.shortcuts import get_object_or_404, render
from django.views import View

MAX_COUNT_PRODUCTS = 20
ID_TYPE_CATEGORY_LIST = 8
ID_TYPE_CATEGORY_LIST_2 = 11

class MenuView(View):
    def get(self, request, menu_slug):
        # Get the current menu object or raise a 404 error
        current_menu = get_object_or_404(MenuCatalog, slug=menu_slug)

        # Check if the current menu type is a category list
        if current_menu.type_menu_id in [ID_TYPE_CATEGORY_LIST, ID_TYPE_CATEGORY_LIST_2]:
            if current_menu.is_hidden:
                raise Http404()

            # Get all the child categories of the current menu
            category_s_list = current_menu.get_all_children()

            # Filter products based on the current menu type
            if current_menu.type_menu_id == ID_TYPE_CATEGORY_LIST_2:
                product_list = Product.objects.filter(catalog__in=category_s_list)
            else:
                product_list = Product.objects.filter(catalog=current_menu)

            # Get the current page from the request GET parameters
            page = request.GET.get('page', 1)

            # Count the total number of products in the list
            product_list_count = product_list.count()

            # Select related fields to avoid additional queries
            product_list = product_list.select_related('marka', 'standart')

            # Create a paginator object with the product list and maximum number of products per page
            paginator = Paginator(product_list, MAX_COUNT_PRODUCTS)

            try:
                # Get the requested page from the paginator
                product_list = paginator.page(page)

                # Calculate the minimum and maximum page numbers for pagination
                if int(page) < 5:
                    paginator_min = 1
                    paginator_max = 10
                else:
                    paginator_min = max(int(page) - 5, 1)
                    paginator_max = min(int(page) + 5, paginator.num_pages)

            except PageNotAnInteger:
                # If the requested page is not an integer, return the first page
                product_list = paginator.page(1)

            except EmptyPage:
                # If the requested page is out of range, return the last page
                product_list = paginator.page(paginator.num_pages)

            # Render the response using the template associated with the current menu type
            response = render(request, current_menu.type_menu.template.replace('.html', '.pug'), locals())
            return response

Hi anefta! thanks, I just tried but no change, the server response is still slow:(

Try to install django debug toolbar and show to us your querysets: Django Debug Toolbar — Django Debug Toolbar 4.4.6 documentation