django-invitations icon indicating copy to clipboard operation
django-invitations copied to clipboard

Double form validation in SendInvite

Open mcheshkov opened this issue 7 years ago • 4 comments

In SendInvite view, in form_valid method self.get_context_data is called without form kwarg. Django (since 1.9.1, I think) creates new form instance in FormMixin in get_context_data method.

It's new instance and it does not have any errors initialized, and validation will be executed again on email.errors access in template. Template is rendered after invitation is created, so it fails with "already_invited" case, and this error is rendered. But invite is still sent.

My proposal is to add form=form kwarg to get_context_data, since this view is rendering on POST and not redirecting.

mcheshkov avatar Apr 21 '17 15:04 mcheshkov

I have the same issue, using the view creates the invite but also displays the error "AlreadyExists". Any workaround?

oesah avatar Apr 08 '18 19:04 oesah

I have a quick workaround:

You can change the _invite.html, which contains the form, to check for success_message and then do if then clauses where appropriate:

{% extends 'base.html' %}
{% load i18n %}
{% load crispy_forms_tags %}

{% block content %}
<h1 class="title">{% trans "Freunde einladen und belohnt werden" %}</h1>
<p>Sie können Freund, Bekannt oder auch andere Nutzer einladen, die gut in unser Netzwerk passen. Dazu brauchen Sie einfach 
eine E-Mail Adresse angeben und die Person erhält dann eine Einladung per Mail.</p>

<div class="alert alert-info">
  Für jedes 5. Mitglied, das durch Ihre Einladung in unser Netzwerk eingetreten ist, erhalten Sie 1 Jahr Premium Mitgliedschaft umsonst.
</div>

<form id="invite-form" method="POST" action="{% url 'invitations:send-invite' %}">
  {% csrf_token %}
  <div class="form-group">
      <input type="text" name="email" id="id_email" value="{% if not success_message %}{{ form.email.value }}{% endif %}" class="form-control validate-email" placeholder="{% trans 'Email' %}">
  </div>
  <div class="form-group">
    {% if not success_message %}
    {% for error in form.email.errors %}
      <div class="alert alert-danger">{{ error }}</div>
    {% endfor %}
    {% else %}
    <div class="alert alert-success">{% blocktrans %}{{ success_message }}{% endblocktrans %}</div>
    {% endif %}
    
  </div>
  <button type="submit" class="btn btn-inverse btn-loading">Einladen</button>
</form>  
{% endblock content %}

Not a nice solution, but it works for now. I hope @bee-keeper can fix it in the view soon.

oesah avatar Apr 09 '18 15:04 oesah

Hit this bug as well.

How about a fix?

I can confirm that passing the form to get_context_data solves the issue.

Natureshadow avatar Jan 25 '21 21:01 Natureshadow

Thanks for the workaround @oesah. It works but would be nice to have the form cleared after a successful invite.

@bee-keeper, any chance you could amend 'class SendInvite(FormView)' to clear the form after a successful invite? Below is a screenshot of the form after a successful invite without the workaround suggested by @oesah.

With the workaround in place, the errors are ignored but the email remains in the form field; it would be much cleaner if it was cleared.

image

Cheers,

Simon

LlewopNomis avatar May 16 '22 05:05 LlewopNomis