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

Ajout d'un formulaire de contact à une page multilingue avec le contenu d'une base de données

Lorsque le contenu de la page provient d'une base de données, vous voudrez ajouter un formulaire de contact en utilisant une balise.

28 septembre 2019 Mise à jour 15 octobre 2019
post main image
unsplash.com/@nickmorrison

Mise à jour 11 octobre 2019 : J'ai changé le tag addon de'{% addon : .... %}' à '[[ addon : .... ]]'. La raison est que je voulais être capable de rendre le texte de la page provenant de la base de données, en utilisant render_template_string, et'{% ... %}' conflits avec les Jinja2 balises. Et oui, je ne veux pas implémenter un tag Jinja2 personnalisé.

Qu'est-ce qui est difficile dans la mise en œuvre d'une page de contact avec un formulaire de contact avec Flask et WTForms? Vous pouvez trouver des solutions sur la façon d'implémenter une page de contact dans Flask mais chaque fois que la page est une page en une seule langue et utilise un fichier Jinja2 modèle. Alors pourquoi écrire un post à ce sujet ?

La raison en est que ce n'est pas trivial lorsque le contenu de la page peut être multilangue et provient d'une base de données. J'ai le contenu de la page que je peux éditer en utilisant l'administrateur, et je veux que le formulaire de contact soit placé quelque part sur la page en utilisant un tag. Pourquoi un tag ? Parce que nous devons être en mesure de mettre le formulaire de contact à n'importe quel endroit dans le contenu. Une fois que l'étiquette est remplacée par le formulaire de contact, elle doit également être traitée au moment de l'envoi. Facile ? Peut-être pour toi, mais pas pour moi.

Présentation des add-ons

En regardant d'autres solutions, j'ai pensé qu'il serait utile d'implémenter le formulaire de contact en tant qu'add-on. Pourquoi ? Parce qu'un add-on est quelque chose que vous devriez pouvoir ajouter très facilement à votre contenu. Il devrait également être possible d'ajouter le formulaire de contact à plusieurs pages. Il y a plus qu'un add-on, par exemple l'add-on formulaire de contact ajoute également une fonction formulaire de contact à l'admin où nous pouvons regarder les formulaires de contact qui ont été soumis.

Mise en œuvre de l'add-on

La première chose que j'ai faite a été de définir une balise qui identifierait le module complémentaire du formulaire de contact :

{% addon:contact_form,id=87 %}

C'est la balise que nous pouvons ajouter au contenu de notre page multilingue qui provient de la base de données. Les autres composants de l'add-on du formulaire de contact sont :

  • ContactForm, le modèle (tableau)
  • Partie administrative, où l'on peut voir les formulaires soumis

Et puis nous avons besoin d'un mécanisme général qui traite l'add-on lorsque nous affichons une page. Comme vous vous en souvenez peut-être dans un message précédent, il n'y a qu'une seule fonction qui génère une page. Comme le contenu ne change pas, il est mis en cache :

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

Cette fonction doit être modifiée et étendue pour qu'elle soit capable de gérer les add-ons.

Conversion de MVC en classe

Dans l' Flask utilisation WTForms du formulaire de contact, par exemple, la mise en œuvre est très simple :

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

Et le ContactFormFormFormForm est :

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

On ne peut pas l'utiliser ici, alors on réécrit ça en classe. J'ai décidé de retourner le succès ou l'erreur pour les méthodes GET et POST et j'ai une méthode séparée pour obtenir le formulaire de contact rendu.

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

Le ContactFormFormFormForm est étendu avec un paramètre caché identifiant l'add-on :

    addon_id = HiddenField('Addon id')

En utilisant ceci nous pouvons maintenant changer la fonction page_view :

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

Résumé

Ce qui précède n'est qu'un résumé, il y a plus, mais je voulais juste vous donner les bases. J'ai aussi implémenté un add-on FAQ, où nous n'avons à traiter qu'avec un GET. Vous pouvez consulter les pages Contact et FAQ sur ce site. Ce n'était qu'une première tentative d'implémentation d'add-ons, et non, ce n'est pas définitif. Je dois maintenant définir une interface claire de toutes les méthodes et attributs qu'un add-on peut ou doit utiliser. Une autre fois, ...

Liens / crédits

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

Laissez un commentaire

Commentez anonymement ou connectez-vous pour commenter.

Commentaires

Laissez une réponse

Répondez de manière anonyme ou connectez-vous pour répondre.