Hinzufügen eines Kontaktformulars zu einer mehrsprachigen Seite mit Inhalten aus einer Datenbank
Wenn der Seiteninhalt aus einer Datenbank stammt, sollten Sie ein Kontaktformular mit einem Tag hinzufügen.
Update 11. Oktober 2019: Ich habe das Addon-Tag von '{% addon: .... %}' auf '[[ addon: .... ]]' geändert. Der Grund dafür ist, dass ich in der Lage sein wollte, den von der Datenbank kommenden Seitentext mit render_template_string und '{% .... %}' Konflikten mit Jinja2 Tags darzustellen. Und ja, ich möchte kein Jinja2 eigenes Tag implementieren.
Was ist schwierig an der Implementierung einer Kontaktseite mit einem Kontaktformular mit Flask und WTForms? Sie können Lösungen finden, wie man eine Kontaktseite implementiert, Flask aber jedes Mal, wenn die Seite eine einsprachige Seite ist und eine Jinja2 Vorlagendatei verwendet. Warum also einen Beitrag darüber schreiben?
Der Grund dafür ist, dass dies nicht trivial ist, wenn der Seiteninhalt mehrsprachig sein kann und aus einer Datenbank stammt. Ich habe den Seiteninhalt, den ich mit dem Administrator bearbeiten kann, und ich möchte, dass das Kontaktformular mit einem Tag irgendwo auf der Seite platziert wird. Warum ein Tag? Denn wir müssen in der Lage sein, das Kontaktformular an jeder beliebigen Stelle im Inhalt zu platzieren. Sobald das Tag durch das Kontaktformular ersetzt wurde, muss es auch bei der Übermittlung verarbeitet werden. Einfach? Vielleicht für dich, aber nicht für mich.
Einführung von Add-ons
Im Hinblick auf andere Lösungen hielt ich es für sinnvoll, das Kontaktformular als Add-on zu implementieren. Warum? Weil ein Add-on etwas ist, das Sie auf sehr einfache Weise zu Ihren Inhalten hinzufügen können sollten. Es sollte auch möglich sein, das Kontaktformular auf mehreren Seiten einzufügen. Ein Add-on hat noch mehr zu bieten, z.B. fügt das Add-on für das Kontaktformular dem Administrator auch eine Kontaktformularfunktion hinzu, mit der wir uns die gesendeten Kontaktformulare ansehen können.
Implementierung des Add-ons
Das erste, was ich tat, war die Definition eines Tags, der das Kontaktformular-Add-on identifizieren würde:
{% addon:contact_form,id=87 %}
Dies ist der Tag, den wir dem Inhalt unserer mehrsprachigen Seite hinzufügen können, die aus der Datenbank kommt. Weitere Bestandteile des Kontaktformular-Add-ons sind:
- ContactForm, das Modell (Tabelle)
- Admin-Bereich, in dem wir die eingereichten Formulare sehen können.
Und dann brauchen wir einen allgemeinen Mechanismus, der das Add-on verarbeitet, wenn wir eine Seite anzeigen. Wie Sie sich vielleicht von einem früheren Beitrag erinnern, gibt es nur eine Funktion, die eine Seite generiert. Da sich der Inhalt nicht ändert, wird er zwischengespeichert:
@pages_blueprint.route('/<slug>', methods=['GET', 'POST'])
def page_view(slug):
...
# get content_item
...
# render content_item of get from cache
hit, rendered_content_item = current_app.app_cache.load(cache_item_id)
if not hit:
rendered_content_item = render_template(
...
content_item=content_item,
content_item_translation=content_item_translation,
)
# cache it
current_app.app_cache.dump(cache_item_id, rendered_content_item)
...
return render_template(
...
rendered_content_item=rendered_content_item,
)
Diese Funktion muss so modifiziert und erweitert werden, dass sie Add-ons verarbeiten kann.
MVC in eine Klasse umwandeln
So ist z.B. die Implementierung Flask über WTForms das Kontaktformular sehr einfach:
@pages_blueprint.route('/contact-form', methods=['GET', 'POST'])
def contact_form():
form = ContactFormForm()
if form.validate_on_submit():
contact_form = ContactForm()
form.populate_obj(contact_form)
db.add(contact_form)
db.commit()
flash( _('Contact form submitted.'), 'info')
return redirect(url_for('pages.thank_you'))
return render_template(
'pages/contact_form.html',
form=form)
Und das Kontaktformular ist:
class ContactFormForm(FlaskForm):
name = StringField(_l('Name'), validators=[
Length(min=2, max=60),
InputRequired()])
email = StringField(_l('Your email'), validators=[
InputRequired(),
Email()])
message = TextAreaField(_l('Your message'), validators=[
Length(min=6, max=500),
InputRequired()])
submit = SubmitField(_l('Send'))
Wir können das hier nicht verwenden, also schreiben wir es als Klasse um. Ich entschied mich, Erfolg oder Fehler für die GET- und POST-Methoden zurückzugeben und habe eine separate Methode, um das gerenderte Kontaktformular zu erhalten.
class AddonContactForm:
def __init(self)__:
...
self.errors = False
self.rendered_contact_form = ''
def get_contact_form(self):
self.errors = False
form = ContactFormForm()
self.rendered_contact_form = render_template(
'addons/contact_form.html',
form=form)
return self.errors
def get_rendered_contact_form(self):
return self.rendered_contact_form
def post_contact_form(self):
self.errors = False
form = ContactFormForm()
if form.validate_on_submit():
contact_form = ContactForm()
form.populate_obj(contact_form)
db.add(contact_form)
db.commit()
flash( _('Contact form submitted.'), 'info')
return redirect(url_for('pages.thank_you'))
self.errors = True
self.rendered_contact_form = render_template(
'addons/contact_form.html',
form=form)
return self.errors
Das Kontaktformular wird um einen versteckten Parameter erweitert, der das Add-on identifiziert:
addon_id = HiddenField('Addon id')
Damit können wir nun die Funktion page_view ändern:
@pages_blueprint.route('/<slug>', methods=['GET', 'POST'])
def page_view(slug):
...
if request.method == 'POST':
addon_id = None
if 'addon_id' in request.form:
addon_id = request.form['addon_id']
if addon_id is not None:
if addon_id == 'contact_form':
addon_contact_form = AddonContactForm()
if addon_contact_form.process_contact_form():
addon_redirect_url = addon_contact_form.get_redirect_url()
return redirect(addon_redirect_url)
# error(s) found during processing
rendered_contact_form = addon_contact_form.get_rendered_contact_form()
addon_error = True
addon_error_message = addon_contact_form.get_error_message()
....
# addon: processing if '{% addon' found
if '{% addon:' in rendered_content_item:
m = re.findall('\{\%\s*(addon)\s*\:\s*([a-z_]+)\s*.*?\%\}', rendered_content_item)
addon_name = None
if m:
addon_name = m[0][1]
if addon_name == 'contact_form':
if request.method == 'GET':
addon_contact_form = AddonContactForm()
if addon_contact_form.get_contact_form():
rendered_contact_form = addon_contact_form.get_rendered_contact_form()
else:
rendered_contact_form = ''
error = True
error_message = addon_contact_form.get_error_message()
rendered_content_item = re.sub('\{\%\s*(addon)\s*\:\s*([a-z_]+)\s*.*?\%\}', rendered_contact_form, rendered_content_item)
elif request.method == 'POST':
# here we just paste the result from the addon
# typically we only come here when an error was detected in the form
rendered_content_item = re.sub('\{\%\s*(addon)\s*\:\s*([a-z_]+)\s*.*?\%\}', rendered_contact_form, rendered_content_item)
return render_template(
...
)
Zusammenfassung
Das Obige ist nur eine Zusammenfassung, es gibt noch mehr, aber ich wollte dir nur die Grundlagen vermitteln. Ich habe auch ein FAQ-Add-on implementiert, bei dem wir es nur mit einem GET zu tun haben. Sie können die Seiten Kontakt und FAQ auf dieser Website besuchen. Dies war nur ein erster Versuch, Add-ons zu implementieren, und nein, es ist nicht endgültig. Ich sollte nun eine klare Schnittstelle für alle Methoden und Attribute definieren, die ein Add-on verwenden kann oder muss. Ein anderes Mal.....
Links / Impressum
Exact difference between add-ons, plugins and extensions
https://stackoverflow.com/questions/33462500/exact-difference-between-add-ons-plugins-and-extensions
Intro to Flask: Adding a Contact Page
https://code.tutsplus.com/tutorials/intro-to-flask-adding-a-contact-page--net-28982
Mehr erfahren
Flask Jinja2 Multilanguage WTForms
Neueste
- Ausblenden der Primärschlüssel der Datenbank UUID Ihrer Webanwendung
- Don't Repeat Yourself (DRY) mit Jinja2
- SQLAlchemy, PostgreSQL, maximale Anzahl von Zeilen pro user
- Anzeige der Werte in den dynamischen Filtern SQLAlchemy
- Sichere Datenübertragung mit Public Key Verschlüsselung und pyNaCl
- rqlite: eine hochverfügbare und distverteilte SQLite -Alternative
Meistgesehen
- Verwendung von Pythons pyOpenSSL zur Überprüfung von SSL-Zertifikaten, die von einem Host heruntergeladen wurden
- Verwendung von UUIDs anstelle von Integer Autoincrement Primary Keys mit SQLAlchemy und MariaDb
- Verbindung zu einem Dienst auf einem Docker -Host von einem Docker -Container aus
- PyInstaller und Cython verwenden, um eine ausführbare Python-Datei zu erstellen
- SQLAlchemy: Verwendung von Cascade Deletes zum Löschen verwandter Objekte
- Flask RESTful API Validierung von Anfrageparametern mit Marshmallow-Schemas