Jinja2 und nl2br

Django, Flask, Bottle, WSGI, CGI…
Antworten
Lonestar
User
Beiträge: 147
Registriert: Samstag 9. August 2008, 08:31

Montag 16. August 2010, 19:25

Nabend liebes Forum,
ich bin hier leicht am verzweifeln. Ich vermute mal ich habe einfach mal wieder nen Denkfehler...
Ich benutzte momentan Jinja2 um einige Templates für ein kleines Bottle-Projekt zu erstellen. Die Daten werden von Benutzern eingegeben und in der Datenbank abgelegt. Damit vom Benutzer eingegebene HTML-Steuerzeichen nicht angezeicht werden habe ich beim Jinja2-Environment autoescape auf true gesetzt. Nun möchte ich aber die Zeilenumbrüche die in der Datenbank stehen gerne als <br /> in meine Vorlage einfügen. Wenn ich nun im Template mit ``replace`` die Zeilenumbrücke umwandle werden sie zu``<br /&gt`` umgewandelt. Das macht soweit ja auch Sinn.

Ich habs dann vorhin schon mit der Doku versucht die ein Beispiel enthält. Da hat sich zwar ein kleiner Fehler eingeschlichen - ``environmentfilter```importiert und ``evalcontextfilter`` aber mit keinem dieser beiden Filter komme ich zu meinem gewünschten Ergebnis...


meine momentane Konfiguration sieht so aus:

Template Environment:

Code: Alles auswählen

class MyLoader(BaseLoader):

    def __init__(self, path):
        self.path = path

    def get_source(self, environment, template):
        path = os.path.join(self.path, template)
        if not os.path.exists(path):
            raise TemplateNotFound(template)
        mtime = os.path.getmtime(path)
        with file(path) as f:
            source = f.read().decode('utf-8')
        return source, path, lambda: mtime == os.path.getmtime(path)

_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')

@contextfilter
def nl2br(eval_ctx, value):
    print 'filter benutzt'
    result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', '<br>\n')
                          for p in _paragraph_re.split(escape(value)))
    if eval_ctx.autoescape:
        result = Markup(result)
    return result



# environment für Jinja anlegen
env = Environment(line_statement_prefix="#",
                  variable_start_string="${",
                  variable_end_string="}",
                  autoescape = True,
                  loader=MyLoader(TEMPLATES_DIR))

ein html-Teil schaut mit dann so aus:

Code: Alles auswählen

<div id="grey-box-3">
    <p>${data.2}</p>
    <p>${data.2| replace("\n", "<br />")}</p>
</div>
wobei bei mir so keine der beiden varianten funtioniert - sicher übersehe ich hier mal wieder das offensichtliche...

Sebastian
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Dienstag 17. August 2010, 00:18

Den Doku-Bug würde ich melden. Ansonsten: Was genau funktioniert denn jetzt nicht? Fehlermeldungen? Falsches Ergebnis?
Lonestar
User
Beiträge: 147
Registriert: Samstag 9. August 2008, 08:31

Dienstag 17. August 2010, 15:50

hi,
also wenn ich den nl2br filter an ``environmentfilter`` oder ``evalcontextfilter``hinzufüge ändert sich die Ausgabe überhaupt nicht im Vergleich zum normalem Setup. Will heissen ein ``\n`` bleibt auch ein ``\n``.

Verwende ich im Template ``${data.2| replace("\n", "<br />")}`` wird immer ein ``<br /&gt`` eingefügt. also ein escaptes ``<br \>``

ich bin mir nicht sicher - war das verständlich?
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Dienstag 17. August 2010, 16:49

Jupp, es gibt da so nen ``safe``-Filter (ich glaube der hieß so in Werkzeug), mit dem kannst du angeben, dass dein Text nicht mehr escaped werden soll.
Lonestar
User
Beiträge: 147
Registriert: Samstag 9. August 2008, 08:31

Dienstag 17. August 2010, 18:19

der Filter heißt bei Jinja2 autoescape. Den habe ich auch eingeschaltet. Ich möchte ja alle HTML-Tags deaktivieren. Nur möchte ich selber ein nl2br einbauen. Damit ich die Zeilenumbrüche die in den Datensätzen der Datenbank enthalten sind auch im HTML-Dokument sehen kann.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Dienstag 17. August 2010, 20:22

Ne, er heißt ``safe`` und ist hier dokumentiert.
Lonestar
User
Beiträge: 147
Registriert: Samstag 9. August 2008, 08:31

Dienstag 17. August 2010, 21:39

Dauerbaustelle hat geschrieben:Ne, er heißt ``safe`` und ist hier dokumentiert.
Ja, da hattest du vollkommen recht - ich hatte da was falsch verstanden. Gut - wenn ich nun den Text aus der db folgendermaßen in mein Template einbaue:

Code: Alles auswählen

${data.2| replace("\n", "<br />")| safe}
``data.2`` enthält hier die Daten aus der Datenbank. Das problem ist jetzt aber doch das der gesamte Text nicht mehr excaped wird. HTML Steuerzeichen sind wieder möglich und ich hätte genauso gut autoescape ausschalten können. Das ist irgendwie nicht genau das was ich erreichen will - ich werde mich noch mal ausgiebig mit der Doku auseinandersetzen
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Dienstag 17. August 2010, 21:43

Schau dir doch einfach mal ein anständiges Markup wie Textile, RestructuredText, Markdown, ... an.

Ansonsten: HTML-Escaping abschalten, manuell escapen, DANACH \n mit <br>s ersetzen.
Lonestar
User
Beiträge: 147
Registriert: Samstag 9. August 2008, 08:31

Dienstag 17. August 2010, 22:06

Dauerbaustelle hat geschrieben:Ansonsten: HTML-Escaping abschalten, manuell escapen, DANACH \n mit <br>s ersetzen.
genau das habe ich gerade mal zu Fuß gemacht um zu sehen ob ich die Doku richtig kapiert habe. Da ich das Beispiel aus der Doku nicht komplett kapiert habe und mich mit Jinja2.Markup ein wenig schwer tue ist das vermutlich von hinten durch die Brust heraus ins Auge geschossen...
aber das funktioniert wie gewollt und vor allem fast wie von mir erwartet. Ich mache nu erst mal Feierabend, und schaue mir das morgen Nachmittag noch einmal an. Meine Momentane Lösung sieht folgendermaßen aus:

der Python-Teil:

Code: Alles auswählen

@evalcontextfilter
def nl2br(eval_ctx, value):
    temp = list()
    for elem in escape(value).split('\n'):
        temp.append(unicode(elem))
    return Markup('<br/>'.join(temp))

# environment für Jinja anlegen
env = Environment(line_statement_prefix="#",
                  variable_start_string="${",
                  variable_end_string="}",
                  autoescape = True,
                  loader=MyLoader(TEMPLATES_DIR))

env.filters['nl2br'] = nl2br
eingebunden wird der Datensatz dann im Template durch ``${data.2|nl2br}`` wobei das autoescaping an blieiben darf da mir ``Markup()`` den zurückgegeben String als sicher marktiert.

noch einmal Danke für deine Hilfe Dauerbaustelle und gute Nacht
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Donnerstag 19. August 2010, 22:43

Ihr denkt alle viel zu kompliziert. Markup kann den Grossteil davon ja eh schon.

Wenn du weisst, dass du autoescape machst, kannst du dir den evalcontextfilter auch sparen:

Code: Alles auswählen

>>> import re
>>> from jinja2 import Environment, Markup, escape
>>> 
>>> _paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
>>> 
>>> def nl2br(value):
...     return Markup(u'\n\n').join(Markup('<p>%s</p>') %
...         escape(p).replace('\n', Markup('<br>\n'))
...         for p in _paragraph_re.split(value))
... 
>>> 
>>> env = Environment()
>>> env.filters['nl2br'] = nl2br
>>> 
>>> 
>>> t = env.from_string('{{ "Hello <World>!\n\nPretty\ncool"|nl2br }}')
>>> print t.render()
<p>Hello <World>!</p>

<p>Pretty<br>
cool</p>
Der filter ist auch gut genug, dass der bereits mit Zeug umgehen kann wo HTML drin ist (vorausgesetzt die Line-breaks sind nicht in den tags, da muesste man den Filter dann noch schlauer machen):

Code: Alles auswählen

>>> t = env.from_string('{{ "<strong>Awesome!</strong>\n\nMore"|safe|nl2br }}')
>>> print t.render()
<p><strong>Awesome!</strong></p>

<p>More</p>
TUFKAB – the user formerly known as blackbird
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Donnerstag 19. August 2010, 22:48

Noch ein paar Beispiele zu Markup:

Markup's Operationen verstehen Markup:

Code: Alles auswählen

>>> Markup('<p>%s</p>') % '<value>'
Markup(u'<p><value></p>')
>>> Markup('<p>%s</p>') % Markup('<value>')
Markup(u'<p><value></p>')
Das gilt natuerlich auch fuer join und replace:

Code: Alles auswählen

>>> Markup('<br>\n').join(['foo > bar', Markup('<strong>Testing</strong>')])
Markup(u'foo > bar<br>\n<strong>Testing</strong>')
>>> escape('<test>\n').replace('\n', '<br>')
Markup(u'<test><br>')
>>> escape('<test>\n').replace('\n', Markup('<br>'))
Markup(u'<test><br>')
Und damit ist das Beispiel von oben ja schon fast komplett :)

Das Markup Objekt gibts uebrigens auch ohne Jinja2: http://pypi.python.org/pypi/MarkupSafe
TUFKAB – the user formerly known as blackbird
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Donnerstag 19. August 2010, 22:49

Die Beispiel oben erscheinen kaputt weil der Highlighter Probleme mit HTML hat. Denkt euch die & waeren nur &.
TUFKAB – the user formerly known as blackbird
Antworten