Search code examples
pythonjsondjangorecaptcha

Adding Google's reCAPTCHA a to a class-based view in Django


I want to add recaptcha for signup view in my Django app. This below uses decorators.py to achieve that. I have tried other tutorials for adding reCAPTCHA also but does not seem working. Any idea why?

views.py

class signup_view(generic.CreateView):
    form_class = RegisterForm
    template_name = 'users/signup.html'
    success_url = reverse_lazy('users:login')

    def form_valid(self, form):
        if self.request.recaptcha_is_valid:
            form.save()
            return render(self.request, 'users/login.html', self.get_context_data())
        return render(self.request, 'users/signup.html', self.get_context_data())

urls.py

path("signup", check_recaptcha(signup_view.as_view()), name="signup"),

decorators.py

from django.conf import settings
from django.contrib import messages
 
import requests
 
def check_recaptcha(function):
    def wrap(request, *args, **kwargs):
        request.recaptcha_is_valid = None
        if request.method == 'POST':
            recaptcha_response = request.POST.get('g-recaptcha-response')
            data = {
                'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
                'response': recaptcha_response
            }
            r = requests.post('https://www.google.com/recaptcha/api/siteverify', data=data)
            result = r.json()
            if result['success']:
                request.recaptcha_is_valid = True
            else:
                request.recaptcha_is_valid = False
                messages.error(request, 'Invalid reCAPTCHA. Please try again.')
        return function(request, *args, **kwargs)
 
    wrap.__doc__ = function.__doc__
    wrap.__name__ = function.__name__
    return wrap

signup.html

    <div class="form">
    <form method="POST">
        {% csrf_token %}
        {{ form|crispy }}
        <br>
        <script src='https://www.google.com/recaptcha/api.js'></script>
        <div class="g-recaptcha" data-sitekey="6LfzEg8gAAAAABcVpBvOjuLjs787K8_4Fu0N2wgu"></div>
        <input type="submit" value="Sign Up">
    </form>
</div>

Solution

  • Change your decorator to:

    def wrap(request, *args, **kwargs):
       request.recaptcha_is_valid = None
    
    def wrap(obj, *args, **kwargs):
      request = obj.request
      request.recaptcha_is_valid = None
      ....
      return function(obj, *args, **kwargs)
    

    so it can works with django views.

    In view put it before form_valid:

    @check_recaptcha
    def form_valid(self, form):