Search code examples
djangodjango-viewsdjango-pagination

Django: How to return user to correct pagination page after CreateView form_invalid?


I have a paginated comments page. Pagination is provided by django-endless-pagination. When clicking to new paginated pages a get parameter is append to the url. Such as ?page=4.

Each comment on each paginated page displays a 'reply to' comment form containing a captcha field.

My view uses CreateView and I implement form_invalid myself in order to add some data to the context variable. At the end of my form_invalid method I return self.render_to_response(context)

The Problem

If a user attempts to reply to a comment when on page 4, and that user supplies and invalid captcha, then the pagination get parameter (?page=4) is lost during the response.

How can I redirect to the full path (keeping get params) and pass context data along with it?

Thanks


Solution

  • This problem is similar to this SO question, but my scenario is a little different given that I want to maintain my form state (the captcha error mentioned in question) in the redirect.

    Thank you @petkostas for pointing out HTTP_REFERER

    My resolution to this involved storing the context in cache with a cache key derived from the current timestamp. I redirect to the (same) comments url, but in doing this I append the current comment page as a get parameter and also the timestamp as another get parameter.

    Then during get requests the view checks for the existence of the timestamp parameter. If it is provided, then a cache.get call is made to retrieve the needed context data. And finally, the cached item is deleted.

    from datetime import datetime
    from django.core.cache import cache
    from django.shortcuts import redirect
    from django.utils.dateformat import format
    
    class MyView(CreateView):
        def form_invalid(self, form, **kwargs):
            context = {
                'form': form,
                ... other context stuff ...
            }
    
            timestamp = format(datetime.now(), u'U')
            cache.set('invalid_context_{0}'.format(timestamp), context)
    
            page = 1
            full_path = self.request.META.get('HTTP_REFERER')
            if '?' in full_path:
                query_string = full_path.split('?')[1].split('&')
                for q in query_string:
                    if q.startswith('page='):
                        page = q.split('=')[1]
    
            response = redirect('my_comments_page')
            response['Location'] += '?page={0}&x={1}'.format(page, timestamp)
            return response
    
        def get_context_data(self, **kwargs):
            context = super(MyView, self).get_context_data(**kwargs)
            context.update({
               ... add context stuff ...
            })
    
            if 'x' in self.request.GET:
                x = self.request.GET.get('x')
                cache_key = 'invalid_context_{0}'.format(x)
                invalid_context = cache.get(cache_key)
                if invalid_context:
                    context.update(invalid_context)
                    cache.delete(cache_key)
            return context