ThrowsException
ThrowsException

Reputation: 2636

Django redirect back to previous page on form post

I'm trying to have a form post redirect back to the page it was posted from. So my app has a detail page that displays information about a place, /arena/123, and on the same page there is a form that a user can enter some comments or ratings about the current place they are looking at. These are my view functions.

class DetailView(LoginRequiredMixin, generic.DetailView):
    model = Arena
    template_name = 'arenas/detail.html'

    def get_context_data(self, **kwargs):
        context = super(DetailView, self).get_context_data(**kwargs)
        if 'form' not in context:
            print 'no form in context'
            context['form'] = RatingForm
        return context

def rate(request):
    if request.method == 'POST':
        form = RatingForm(request.POST)
        if form.is_valid():
            # post was valid. go back to page and display data
            return DetailView.as_view()(request(), form=RatingForm)
        else:
            # post was unsuccessful. go back to the page and display errors
            print form.errors
            return DetailView.as_view()(request(), form=form)

and my template

{% extends "base.html" %}
{% block content %}
    <h1>{{ arena.address }}</h1>
    <h1>{{ arena.address_2 }}</h1>
    <h1>{{ arena.phone }}</h1>

    {% if form.errors %}
        {% for field in form %}
            {% for error in field.errors %}
                <div class="alert alert-error">
                    <strong>{{ error|escape }}</strong>
                </div>
            {% endfor %}
        {% endfor %}
        {% for error in form.non_field_errors %}
            <div class="alert alert-error">
                <strong>{{ error|escape }}</strong>
            </div>
        {% endfor %}
    {% endif %}
    <form action="/arenas/rate/" method="post">
        {% csrf_token %}
        {{ form }}
    <input type="submit" value="Submit" />
    </form>

    <div>
        {{comments}}
    </div>
{% endblock %}

So what I'd like to do is after a user submits the form on the page I'd like to go back to the same detail page I was on and either have the data that user posted there shown in the comments section if it was successful or redirect back to the same page but with the form errors if it was unsuccessful. My problem is right now I don't know how to retain the url and form errors to display the detail page again. I don't think I can use return DetailView.as_view()(context(), form=form) because my DetailView doesn't have a post so I get a 405. Every example I see doesn't cover redirecting back to the same page. They usually just show a HttpResponseRedirect(/thanks) or something similar.

Also note. I don't want to post to /arena/123 because eventually that will be another feature to update information about the place so I didn't want to put the comments post on that url.

Upvotes: 0

Views: 3539

Answers (1)

ThrowsException
ThrowsException

Reputation: 2636

So I dug into some more Django examples and stumbled across SingleObjectMixin. This combined with a FormView seems to be what I want. The Django docs had an example that was 99.9 percent what I wanted here. Just be sure to read down to the better solution section.

I changed my views to this

class DetailView(LoginRequiredMixin, generic.DetailView):
    model = Arena
    template_name = 'arenas/detail.html'

    def get_context_data(self, **kwargs):
        print kwargs
        context = super(DetailView, self).get_context_data(**kwargs)
        context['form'] = RatingForm
        return context


class RatingView(LoginRequiredMixin, detail.SingleObjectMixin, generic.FormView):
    model = Arena
    template_name = 'arenas/detail.html'
    form_class = RatingForm

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super(RatingView, self).post(request, *args, **kwargs)

    def get_success_url(self):
        print "successfully posted"
        return reverse('arenas:detail', kwargs={'pk': self.object.pk})

Added a route for posting the form with /rate

urlpatterns = patterns('',
                       url(r'^$', views.IndexView.as_view(), name='index'),
                       url(r'^(?P<pk>\d+)/$',
                           views.DetailView.as_view(), name='detail'),
                       url(r'^(?P<pk>\d+)/rate/$', views.RatingView.as_view(), name='rate')
                       )

and modified my template a bit to pass the id of the object to my route in the form post action

{% extends "base.html" %}
{% block content %}
    <h1>{{ arena.address }}</h1>
    <h1>{{ arena.address_2 }}</h1>
    <h1>{{ arena.phone }}</h1>

    {% if form.errors %}
        {% for field in form %}
            {% for error in field.errors %}
                <div class="alert alert-error">
                    <strong>{{ error|escape }}</strong>
                </div>
            {% endfor %}
        {% endfor %}
        {% for error in form.non_field_errors %}
            <div class="alert alert-error">
                <strong>{{ error|escape }}</strong>
            </div>
        {% endfor %}
    {% endif %}
    <form action="/arenas/{{ arena.id }}/rate/" method="post">
        {% csrf_token %}
        {{ form }}
    <input type="submit" value="Submit" />
    </form>

    <div>
        {{comments}}
    </div>
{% endblock %}

Now on an error the context is kept and the SingleObjectMixin/FormView combination keeps me on the same page to display the form errors and on a successful post it redirects to the detail page I want using the get_success_url to load the page again with the new information that was posted.

Upvotes: 1

Related Questions