angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

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.

28 September 2019 Aktualisiert 15 Oktober 2019
post main image
unsplash.com/@nickmorrison

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

Einen Kommentar hinterlassen

Kommentieren Sie anonym oder melden Sie sich zum Kommentieren an.

Kommentare

Eine Antwort hinterlassen

Antworten Sie anonym oder melden Sie sich an, um zu antworten.