Jinja2 if-for loop-else

Django, Flask, Bottle, WSGI, CGI…
Antworten
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Hallo Leute,

ich arbeite zur Zeit an einer Webapp mit Flask und baue da eine Tabelle (HTML) die auf zwei Routings zutreffen kann, da die übergebenen Werte vom Aufbau gleich sind.
Bei den Daten übergebe ich einmal paginierte Daten, die ich im Template mit Daten.items und einer for loop darstelle. Die zweite Route übergibt an sich den gleichen Datenaufbau aber weniger Menge, weshalb ich hier die Pagination weglassen kann und hier ohne das Daten.items sonder rein mit Daten arbeiten kann.
Leider ist im Template da dann der Unterschied, welchen man normalerweise in Python über ein if..forloop else..forloop lösen könnte. Im Jinja2 steht mir hier der "Endblock" im Weg zumindest meckert so der Debugger.

Code: Alles auswählen

{% if request.method != 'POST' %}
	{% for date in Daten.items %}
{%else%}
	{% for date in Daten.items %}
        	 <tr><td>{{ date.id }}</td></tr>
        {%endfor%}
{%endif%}
Gibt es da vllt doch eine Möglichkeit?
*ja ich könnte die Blöcke nacheinander zweimal setzen also

Code: Alles auswählen

{% if request.method != 'POST' %}
	{% for date in Daten.items %}
		<tr><td>{{ date.id }}</td></tr>
	{%endfor%}
{%else%}
	{% for date in Daten.items %}
		<tr><td>{{ date.id }}</td></tr>
	 {%endfor%}
{%endif%}
Dann würde ich aber aufgrund der Tabellengröße und anderen Templates etwa 40 Zeilen HTML Code "doppeln" und das gleich für 3 leicht unterschiedliche Tabellen. Daher ist die obere Variante eigentlich eleganter meiner Meinung nach.
Per Google/Docs finde ich leider nur if-Statements innerhalb der For-Loop...
Zuletzt geändert von Karlirex am Mittwoch 17. August 2022, 12:53, insgesamt 3-mal geändert.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Karlirex: Rück das mal ordentlich ein, dann sollte klar werden warum das so nicht gehen kann. Würde es in Python auch nicht. Oder allen anderen Programmiersprachen mit diesen Strukturierten Blöcken für ``if``/``else`` und ``for``-Schleifen. Mal von der kaputten „Verzahnung“ der Syntaxkonstrukte abgesehen fehlt auch ein ``endfor``.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

@__blackjack__: habe es gerade gerückt. Im ersten Fall kann ja auch noch kein "endfor" stehen, da die Schleife ja erst nachdem <tr> geschlossen wird. Und in Pyhton funktioniert so etwas auch siehe:

Code: Alles auswählen

list = [1, 2, 3, 4, 5, 6]
x = 4

if x == 3:
    for y in list:
        print(y)
else:
    for y in list:
        print(x)
Mein Problem liegt darin, dass der <tr> -Block eben etwa 30 Zeilen enthält und es aufgrund von verschiedenen Tabellen in verschiedenen Routen ein solches Kontrukt leider öfter gibt und ich deshalb nicht 30 Zeilen Code wegen einem "if-else-for" doppeln möchte. Deshalb wollte ich mir so den <tr> - Block ein zweites Mal "sparen", wenn man dem Dont repeat yourself - Prinzip folgen würde, zumindest in meinen Augen
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Karlirex: In Python geht das nicht was Du im ersten Template-Beispiel machst, da kann man das nicht mal eingeben, weil die Endschlüsselworte implizit durch die Einrückung da sind. Das ist nicht eleganter sondern einfach nur kaputt, weil man Code so nicht strukturieren kann. Die Konstrukte müssen jeweils komplett sein.

Also musst Du entweder komplette Schleifen in ``if`` und ``else`` setzen, oder nur eine Schleife schreiben *in* der dann, an gegebenenfalls mehreren Stellen, die Unterschiede zwischen POST oder nicht-POST per ``if`` und gegebenenfalls ``else`` geregelt werden. Was alternativ oder zusätzlich vielleicht auch geht sind Makros. Das Äquivalent zu Funktionen in Code um Gemeinsamkeiten, eventuell parametrisiert, heraus zu ziehen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Das mit dem if und else selbst machen geht leider nicht, da es sich eben auf den zu iterierenden Term der Daten handelt, ich wüsste zumindest nicht, wie ich das innerhalb gestalte.
Wie gesagt, oben das ist nur Beispielhaft und die <tr> unter der for-loop sind deutlich länger und daher dann eben sehr viel "doppelt" in einem Template bzw dann anschließend in allen viern, wo ich diese Struktur bräuchte.
Aber gut schade.
Und ja das mit dem if for funktioniert so in Python auch nicht. Da das "print" in dem Sinn ja mein <tr> war, genau gleich aufgebaut klappt es nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Karlirex: Den ersten Satz verstehe ich inhaltlich nicht. Und auf Makros gehst Du in der Antwort auch nicht ein.

Zeig doch mal ein Beispiel das nicht so weit gekürzt ist, dass die Unterscheidung komplett sinnlos ist.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Eine der Tabellen in der HTML-View:

Code: Alles auswählen

            <tbody>
                {% for adresse in Adresse %}
                    <tr>
                        <td>{{ adresse.id }}</td>
                        <td>{{ adresse.lbv_number }}</td>
                        <td>{{ adresse.th_number }}</td>
                        <td>{{ adresse.birthday | date_formate_html }}</td>
                        <td>{{ adresse.last_name }}</td>
                        <td>{{ adresse.first_name }}</td>
                        <td>{{ adresse.street }}</td>
                        <td>{{ adresse.city_plz }}</td>
                        <td>{{ adresse.city }}</td>
                        <td>{{ adresse.private_phone }}</td>
                                        {% if adresse.active == 0 %}
                                        <td>Nein</td>
                                        {% elif adresse.active == 1 %}
                                        <td>Ja</td>
                                        {% else %}
                                        <td>###</td>
                                        {% endif %}
                        <td>{{ adresse.family }}</td>
                        <td>{{ adresse.enter_date }}</td>
                        <td>{{ adresse.comment }}</td>
                        <td>{{ adresse.group.group_name}}</td>
                        <td>{{ adresse.status }}</td>
                        <td><a style="color: black" href = "{{ url_for('adressen_details', adressen_id=adresse.id) }}">Details</a></td>
                        <td><a style="color: black" href = "{{ url_for('adressen_update', adressen_id=adresse.id) }}">Bearbeiten</a></td>
                        {% if check_role('Admin') %}
                        <td><a style="color: black" href = "{{ url_for('adressen_delete', adressen_id=adresse.id) }}" onclick="return confirm('{{ adresse.last_name }} wirklich löschen?')" class = "btn">Löschen</a></td>
                        {% endif %}
                    </tr>
                {% endfor %}
            </tbody>
Mit dem möglichen Vorschlag müsste ich diesen Block ja unter das else nocheinmal schreiben. Was das Ganze wie gesagt sehr aufbläht. Und ich habe 5 solcher Tabellen, die eben unterschiedlich viele Spalten haben/aus unterschiedlichen Datenbanken kommen.

Das mit den Makros müsste ich mir erst anschauen, um darauf näher eingehen zu können.
Karlirex
User
Beiträge: 126
Registriert: Dienstag 18. August 2020, 18:27

Ich habs jetzt übers backend und einem if vor der pagination gelöst:
HTML

Code: Alles auswählen

            <tbody>
                {% for adresse in Adresse %}
                    <tr>
                        <td>{{ adresse.id }}</td>
                        <td>{{ adresse.lbv_number }}</td>
                        <td>{{ adresse.th_number }}</td>
                        <td>{{ adresse.birthday | date_formate_html }}</td>
                        <td>{{ adresse.last_name }}</td>
                        <td>{{ adresse.first_name }}</td>
                        <td>{{ adresse.street }}</td>
                        <td>{{ adresse.city_plz }}</td>
                        <td>{{ adresse.city }}</td>
                        <td>{{ adresse.private_phone }}</td>
                                        {% if adresse.active == 0 %}
                                        <td>Nein</td>
                                        {% elif adresse.active == 1 %}
                                        <td>Ja</td>
                                        {% else %}
                                        <td>###</td>
                                        {% endif %}
                        <td>{{ adresse.family }}</td>
                        <td>{{ adresse.enter_date }}</td>
                        <td>{{ adresse.comment }}</td>
                        <td>{{ adresse.group.group_name}}</td>
                        <td>{{ adresse.status }}</td>
                        <td><a style="color: black" href = "{{ url_for('adressen_details', adressen_id=adresse.id) }}">Details</a></td>
                        <td><a style="color: black" href = "{{ url_for('adressen_update', adressen_id=adresse.id) }}">Bearbeiten</a></td>
                        {% if check_role('Admin') %}
                        <td><a style="color: black" href = "{{ url_for('adressen_delete', adressen_id=adresse.id) }}" onclick="return confirm('{{ adresse.last_name }} wirklich löschen?')" class = "btn">Löschen</a></td>
                        {% endif %}
                    </tr>
                {% endfor %}
            </tbody>
        </table>
{% if request.method != 'POST' %}
    <div class="pagination">
        {% if pagination.has_prev %}
            <span>
                <a class='page-number' href="{{ url_for('adressen_all', page=pagination.prev_num) }}">
                    {{ '<<<' }}
                </a>
            </span>
        {% endif %}

        {% for number in pagination.iter_pages() %}
            {% if pagination.page != number %}
                <span>
                        <a class='page-number'
                            href="{{ url_for('adressen_all', page=number) }}">
                        {{ number }}
                        </a>
                </span>
            {% else %}
                <span class='current-page-number'>{{ number }}</span>
            {% endif %}
        {% endfor %}

        {% if pagination.has_next %}
            <span>
                <a class='page-number'
                    href="{{ url_for('adressen_all', page=pagination.next_num) }}">
                    {{ '>>>' }}
                </a>
            </span>
        {% endif %}
    </div>
{% endif %}
Backend

Code: Alles auswählen

@app.route('/adressen/')
@login_required
def adressen_all():
    if request.method != 'POST':
        pagination = Adressen.query.paginate(request.args.get('page', 1, type=int), per_page=20)
        adressen_view = pagination.items
    else: adressen_view = Adressen.query.all()
    return render_template("HTML/Adressen/adressen_all.html", Adresse=adressen_view, pagination=pagination, title='Adressen')
Dafür musste ich die Routen logischerweise überall an passen, aber ich habe nur "4" Zeilen bei den Routen ähnlich und keine 30 im HTML Block doppelt. *zufrieden*
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

`pagination` wird im else-Zweig nicht definiert. Nach einem : fängt immer eine neue Zeile an.
Und man schreibt keine komplizierte Logik ins Template. Welche Request-Methode benutzt wird, sollte dem Template egal sein.

Code: Alles auswählen

def adressen_all():
    if request.method != 'POST':
        pagination = Adressen.query.paginate(request.args.get('page', 1, type=int), per_page=20)
        adressen_view = pagination.items
    else:
        pagination = None
        adressen_view = Adressen.query.all()
    return render_template("HTML/Adressen/adressen_all.html", Adresse=adressen_view, pagination=pagination, title='Adressen')
Wenn in jedem if-Teil die selben Tags stehen, dann gehören die drumrum. Warum packst Du einen literalen String in {{}}?

Code: Alles auswählen

  <table>
    <tbody>
      {% for adresse in Adresse %}
      <tr>
        <td>{{ adresse.id }}</td>
        <td>{{ adresse.lbv_number }}</td>
        <td>{{ adresse.th_number }}</td>
        <td>{{ adresse.birthday | date_formate_html }}</td>
        <td>{{ adresse.last_name }}</td>
        <td>{{ adresse.first_name }}</td>
        <td>{{ adresse.street }}</td>
        <td>{{ adresse.city_plz }}</td>
        <td>{{ adresse.city }}</td>
        <td>{{ adresse.private_phone }}</td>
        <td>{% if adresse.active == 0 %}Nein{% elif adresse.active == 1 %}Ja{% else %}###{% endif %}</td>
        <td>{{ adresse.family }}</td>
        <td>{{ adresse.enter_date }}</td>
        <td>{{ adresse.comment }}</td>
        <td>{{ adresse.group.group_name}}</td>
        <td>{{ adresse.status }}</td>
        <td><a style="color: black" href = "{{ url_for('adressen_details', adressen_id=adresse.id) }}">Details</a></td>
        <td><a style="color: black" href = "{{ url_for('adressen_update', adressen_id=adresse.id) }}">Bearbeiten</a></td>
        {% if check_role('Admin') %}
        <td><a style="color: black" href = "{{ url_for('adressen_delete', adressen_id=adresse.id) }}" onclick="return confirm('{{ adresse.last_name }} wirklich löschen?')" class = "btn">Löschen</a></td>
        {% endif %}
      </tr>
      {% endfor %}
    </tbody>
  </table>
{% if pagination %}
  <div class="pagination">
    {% if pagination.has_prev %}
    <span><a class='page-number' href="{{ url_for('adressen_all', page=pagination.prev_num) }}"> &lt;&lt;&lt;</a></span>
    {% endif %}
    {% for number in pagination.iter_pages() %}
      {% if pagination.page != number %}
      <span><a class='page-number' href="{{ url_for('adressen_all', page=number) }}">{{ number }}</a></span>
      {% else %}
      <span class='current-page-number'>{{ number }}</span>
      {% endif %}
    {% endfor %}
    {% if pagination.has_next %}
    <span><a class='page-number' href="{{ url_for('adressen_all', page=pagination.next_num) }}"> &gt;&gt;&gt;</a></span>
    {% endif %}
  </div>
{% endif %}
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sirius3: Also zumindest ``{{ "<<<" }}`` finde ich schöner/lesbarer als die Alternative ``&lt;&lt;&lt;``. Während man bei ``>>>`` das einfach schreiben könnte, sieht man da nicht selten aus ”Symmetriegründen” ``&gt;&gt;&gt;``, und auch da finde ich dann ``{{ ">>>" }}`` irgendwie schöner zu lesen. Ist wahrscheinlich Geschmackssache.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten