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

Een contactformulier toevoegen aan een meertalige pagina met inhoud uit een database

Wanneer de pagina-inhoud uit een database komt, wilt u een contactformulier toevoegen met behulp van een tag.

28 september 2019 Bijgewerkt 15 oktober 2019
post main image
unsplash.com/@nickmorrison

Update 11 oktober 2019: Ik heb de add-on-tag gewijzigd van "{% addon: .... %}" in "[[ addon: ....]]". De reden is dat ik de paginatekst uit de database wilde kunnen weergeven met render_template_string en '{% .... %}' in strijd met Jinja2 tags. En ja, ik wil geen Jinja2 aangepaste tag implementeren.

Wat is er moeilijk aan het implementeren van een contactpagina met een contactformulier met Flask en WTForms? U kunt oplossingen vinden voor het implementeren van een contactpagina in Flask maar elke keer dat de pagina een enkele taalpagina is en een Jinja2 sjabloonbestand gebruikt. Dus waarom schrijf je hier dan een bericht over?

De reden is dat dit niet onbeduidend is wanneer de pagina-inhoud meertalig kan zijn en uit een database komt. Ik heb de pagina-inhoud die ik kan bewerken met behulp van de beheerder, en ik wil dat het contactformulier ergens op de pagina wordt geplaatst met behulp van een tag. Waarom een tag? Omdat we het contactformulier op elke locatie in de inhoud moeten kunnen plaatsen. Zodra de tag is vervangen door het contactformulier moet deze ook worden verwerkt bij het verzenden. Rustig aan? Misschien voor jou, maar niet voor mij.

Introductie van add-ons

Als ik naar andere oplossingen kijk, dacht ik dat het nuttig zou zijn om het contactformulier als add-on te implementeren. Waarom? Want een add-on is iets dat je op een zeer eenvoudige manier zou moeten kunnen toevoegen aan je inhoud. Het zou ook mogelijk moeten zijn om het contactformulier op meerdere pagina's toe te voegen. Er is meer aan een add-on, bijvoorbeeld het contactformulier add-on voegt ook een contactformulier functie toe aan de admin waar we de ingediende contactformulieren kunnen bekijken.

De add-on implementeren

Het eerste wat ik deed was het definiëren van een tag die de add-on van het contactformulier zou identificeren:

{% addon:contact_form,id=87 %}

Dit is de tag die we kunnen toevoegen aan de inhoud van onze meertalige pagina die uit de database komt. Andere onderdelen van het contactformulier add-on zijn:

  • ContactForm, het model (tabel)
  • Admin-gedeelte, waar we de ingediende formulieren kunnen bekijken

En dan hebben we een algemeen mechanisme nodig dat de add-on verwerkt wanneer we een pagina tonen. Zoals u zich wellicht herinnert van een vorig bericht is er maar één functie die een pagina genereert. Omdat de inhoud niet verandert, wordt deze in het cachegeheugen bewaard:

@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,
        )

Deze functie moet worden aangepast en uitgebreid zodat hij in staat is om add-ons te verwerken.

MVC omzetten naar een klasse

Door Flask het gebruik WTForms van het contactformulier is de implementatie bijvoorbeeld heel eenvoudig:

@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)

En het ContactFormFormulier is dat ook:

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'))

We kunnen dit hier niet gebruiken, dus herschrijven we dit als een klasse. Ik besloot om terug te keren succes of fout voor de GET en POST methoden en hebben een aparte methode voor het verkrijgen van de gerenderde contact formulier.

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

Het ContactFormFormForm wordt uitgebreid met een verborgen parameter die de add-on identificeert:

    addon_id = HiddenField('Addon id')

Hiermee kunnen we nu de page_view functie wijzigen:

@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(
    ...
    )

Samenvatting

Het bovenstaande is slechts een samenvatting, er is meer, maar ik wilde u alleen de basisprincipes geven. Ik heb ook een FAQ add-on geïmplementeerd, waarbij we alleen te maken hebben met een GET. U kunt de Contact- en FAQ-pagina's op deze website bekijken. Dit was slechts een eerste poging om add-ons te implementeren, en nee, het is niet definitief. Ik zou nu een duidelijke interface moeten definiëren van alle methoden en attributen die een add-on kan of moet gebruiken. Een andere keer....

Links / credits

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

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.