Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: Jiří Míšek 15. 12. 2022, 13:37:32
-
Zdravím všechny vespolek,
mám problém s rychlostí vykreslování dat v templates. Mám aplikaci typu dodací list s proměnným počtem položek.
V databázi mám zvlášť tabulku pro dodací list a položku dodacího listu. Ve html view mám seznam dodáků i položkami a to mi dělá problémy s rychlostí, stránka s cca 100 dodacími listy se zobrazuje zhruba 7 sekund a to je moc. Mám to řešené tak že se vypíše dodák a pak prohledává celé tabulka s položkami a hledá ty které patří k dodáku. Takže cyklus prohledání všech položek se zopakuje ke každému dodáku. Nemáte někdo nápad jak by to šlo zjednodušit či zrychlit?
Díky moc za jakýkoliv nápad.
-
Co zobrazujete v te tabulce? Proc prochazite polozky pro kazdy list?
-
je tam tabulka všech dodacích listů i s položkami kód viz níže,
ten druhý for mi dělá potíže je to asi 1000 položek
{% for item in model %}
<tr>
<td><a href="{% url 'dodak:DodakEdit' item.id %}"><i class="fa fa-pencil-square-o">[/url]
<a href="{% url 'dodak:DodakPrint' item.id %} " target="_blank"><i class="fa fa-print">[/url]
<a href="pdf/{{item.id }}"><i class="fa fa-file-pdf-o" aria-hidden="true">[/url]
</td>
<td><a href="{% url 'dodak:DodakView' item.id %}">{{ item.cisloDodaku }}[/url]
</td>
<td>{{ item.datumVytvoreni }}</td>
<td>{{ item.projekt }}</td>
<td>{{ item.adresaOdberatel|slice:"20" }}</td>
<td>{{ item.cisloObjednavky }}</td>
<td>
{% for itemsx in items %}
{% if itemsx.dodak == item %}
{{ itemsx.polozka|slice:"20" }}
{% endif %}
{% endfor %}
</td>
<td>{% if item.soubor %}Ano{% else %}Ne{% endif %}</td>
<td>{% if item.vyuctovano %}Ano{% else %}Ne{% endif %}</td>
</tr>
{% endfor %}
-
Já bych to tedy udělal jinak. Do seznamu si načtu jen ty hlavičky dodacích listů a až po kliknutí bych si dočetl položky. Proč to načítáš vše najednou?
-
Nebo když už to chceš mít vše přednačtené a bez AJAXu, tak bych změnil model tak, aby už konkrétní dodák obsahoval ve svém atributu právě jen své položky a ty pak for cyklem vypíšeš bez nutnosti procházení celého listu všech položek všech dodáků.
{% for itemsx in item.polozky %}
{{ itemsx.polozka|slice:"20" }}
{% endfor %}
-
Všechno najednou načítám kvůli jednoduššímu vyhledávání, které mi obsluhuje js v tabulce
-
Nebo když už to chceš mít vše přednačtené a bez AJAXu, tak bych změnil model tak, aby už konkrétní dodák obsahoval ve svém atributu právě jen své položky a ty pak for cyklem vypíšeš bez nutnosti procházení celého listu všech položek všech dodáků.
{% for itemsx in item.polozky %}
{{ itemsx.polozka|slice:"20" }}
{% endfor %}
Úplně přesně nechápu jak to myslíš, když je proměnný počet položek. Můžeš mi prosím poslat nějaký příklad?
-
Na počtu položek vůbec nezáleží. Musíš udělat změnu na backendu (model a načítání dat do něj) ještě před vykreslením template.
Jak jsem psal výše, položky dodáku budou atributem dodáku (bude to list, nezajímá tě jeho délka = splňuje tvůj požadavek na dynamický počet položek). Při načítání dodacích listů z databáze si rovnou načteš i jeho položky. Třeba přes relaci viz můj vymyšlený příklad níže.
Abych ti mohl konkrétněji poradit, potřeboval bych vidět tvoje modely a metody načítání dat.
from django.db import models
class Dodak(models.Model):
id = models.AutoField(primary_key=True)
polozky = models.oneToMany()
class PolozkaDodaku(models.Model):
dodak = models.ForeignKey(Dodak, on_delete=models.RESTRICT)
-
Myslim, ze tvuj problem by mohl redukovat prefetching
https://hakibenita.com/all-you-need-to-know-about-prefetching-in-django
https://docs.djangoproject.com/en/4.1/ref/models/querysets/
Na profiling DOPORUCUJI:
https://github.com/jazzband/django-silk
-
Já tomu nerozumím, ale takhle na první pohled mi přijde jestli to není kvadratické? Tj. v hlavním for cyklu vypisuješ dodáky a pak ve vnořeném procházíš všechny znova abys pokaždé jen vybral tím ifem ten jeden co zrovna zpracováváš?
{% for item in model %} // pro všechny dodáky
// vypiš hlavičku dodáku
{% for itemsx in items %} // co je items? neprocházíš všechny dodáky znova?
{% if itemsx.dodak == item %}
{{ itemsx.polozka|slice:"20" }}
{% endif %}
{% endfor %}
{% endfor %}
-
Přesně jak říkáš, já si první vypíšu obsah dodáku (číslo adresa datum atd) a pak hledám v dalším tabulce ve které jsou jen návzi položek a relace
takhle vypadá model
class DodakModel(models.Model):
uuid = ShortUUIDField(unique=True)
projekt = models.ForeignKey(ProjektModel, on_delete=models.CASCADE, related_name='projekt')
vlastnik = models.ForeignKey(User,on_delete=models.CASCADE)
datumVytvoreni = models.DateTimeField(default=timezone.now, editable=False)
datumVystaveni = models.DateTimeField(default=timezone.now)
cisloDodaku = models.CharField(max_length=20)
adresaOdberatel = models.TextField(max_length=200)
adresaDodavatel = models.TextField(max_length=200)
atd.
def __str__(self):
return self.cisloDodaku
class DodakPolozkaModel(models.Model):
dodak = models.ForeignKey(DodakModel,on_delete=models.CASCADE,related_name='dodak')
polozka = models.CharField(max_length=75)
pocet = models.IntegerField(default=1)
-
No vidíš, ty tam tu relaci Dodák -> Položka už máš.
class DodakPolozkaModel(models.Model):
dodak = models.ForeignKey(DodakModel,on_delete=models.CASCADE,related_name='dodak')
Tak by mělo jít v template zavolat foreach cyklus na položkách dodáku
{% for itemsx in item.dodak_set.objects.all() %}
{{ itemsx.polozka|slice:"20" }}
{% endfor %}
Až to rozchodíš, tak bys měl zvážit nějaké stránkování. Až tam budeš mít tisíce dokladů, tak to zase bude pomalé. Proto doporučuji spíš způsob
Do seznamu si načtu jen ty hlavičky dodacích listů a až po kliknutí bych si dočetl položky.
-
Šak tak to mám už teď, jen v djangu jsem foreach nenašel tak jsem použil for.
stránkování jsem tam měl, ale chtěl jsem použít něco takového
https://www.jqueryscript.net/demo/Paginate-Sort-Filter-Table-Sortable/
-
Přemýšlel jsem jestli by to nešlo plnit ve funkci co volám ve view, kde by se to plnilo do jedné proměnné abych se vyhl druhému cyklu
-
Šak tak to mám už teď, jen v djangu jsem foreach nenašel tak jsem použil for.
Sorry, jsem zvyklý na terminologii C#. V Pythonu se mezi foreach (for pro iterátor) a for (range od-do) nerozlišuje.
Nemáš to tak už teď. Tvůj způsob z prvního příspěvku prochází pro každý item dodáku celý list položek a podmínkou hledá jestli se položka rovná dodáku. Můj navrhovaný způsob tento nesmysl nedělá. V přípravě modelu se už načtou správné položky a template pak už rovnou ve for cyklu vypíše pouze položky konkrétního dodáku, tedy se to zákonitě musí hodně zrychlit.
Přemýšlel jsem jestli by to nešlo plnit ve funkci co volám ve view, kde by se to plnilo do jedné proměnné abych se vyhl druhému cyklu
Ano. Řešení je spousta. Hlavně se vyhni složitým operacím s daty v template, to je největši brzda.
-
Podival bych se na prefetch related v dokumentaci.
-
Přesně jak říkáš, já si první vypíšu obsah dodáku (číslo adresa datum atd) a pak hledám v dalším tabulce ve které jsou jen návzi položek a relace
No ale v té tabulce hledáš tak, že ji pokaždé projdeš celou. Jak je velká? Mohl bys přidat proměnnou, kterou na začátku nastavíš na 0, a v tom vnitřním cyklu ji při každém průchodu o 1 zvětšíš, a pak si vypíšeš kolik je na konci? A vyzkoušet to pro ten pomalý případ co ti trvá 7 sekund.
-
Přemýšlel jsem jestli by to nešlo plnit ve funkci co volám ve view, kde by se to plnilo do jedné proměnné abych se vyhl druhému cyklu
Tak jak, pane, nějaký posun? Jakým způsobem jsi problém vyřešil?