Question

I'm trying to post a very simple comment with Ajax, when I'm not using Ajax the comment is posted and saved to the database just fine.

So far I've encountered 501 and 403 errors and all sorts of unwanted behaviour. I've tried to follow these instructions and I've read several answers on a similar topic on stackoverflow, but was unable to apply those solutions to my problem.

I'm really stuck here so any help would be greatly appreciated.

Relevant models.py:

class Photo(models.Model):
    image = models.ImageField(
        'image path', upload_to='images/%Y/%m/%d')
    image_thumb = models.ImageField(
    'thumbnail path', upload_to='images/thumbnails/%Y/%m/%d')
    name = models.CharField(max_length=100)
    description = models.CharField(max_length=150)
    avg_rating = models.FloatField('average rating', default=5.1)

    def __unicode__(self):
        return self.description

class Comment(models.Model):
    post_date = models.DateTimeField('posted')
    comment_text = models.TextField(max_length=500)
    photo = models.ForeignKey(Photo)

    def __unicode__(self):
    return self.comment_text

The comment form from forms.py:

class CommentForm(forms.Form):
    comment_text = forms.CharField(widget=forms.Textarea)

And the view which normally handles comments in views.py:

def details(request, photo_id):
photo = get_object_or_404(Photo, pk=photo_id)
ratings_list = photo.rating_set.all().order_by('-id')

if request.method == 'POST':
    form_c = CommentForm(request.POST)

    if form_c.is_valid():
        new_comment = Comment(
            post_date = datetime.now(),
            comment_text = form_c.cleaned_data['comment_text'],
            photo = photo
        )
        new_comment.save()
        if request.is_ajax():
            return "???"
        else:
            return HttpResponseRedirect(reverse('photos:details', args= (photo_id,)))
else:
    form_c = CommentForm()

return render(request, 'photos/details.html', 
    {'photo': photo, 'ratings_list': ratings_list[:5],
    'form_c': form_c})

I've put question marks in there since by now I have no idea where should I redirect.

Relevant details.html template snippet:

<div id="commentSection">
{% for c in photo.comment_set.all %}
    <p>On {{c.post_date|date:"dS \o\f F, \a\t H:i"}}, someone said: </p>
    <p>{{c.comment_text}}</p>
    <br>
{% endfor %}
</div>
<form id="commentForm" name="commentForm" action="{% url 'photos:details' photo.id %}" method="post">
    {% csrf_token %}
    <p>{{form_c.non_field_errors}}
    {{form_c.comment_text.label_tag}}
    {{form_c.comment_text.errors}}</p>
    <p>{{form_c.comment_text}}</p>
    <input type="submit" value="Post Comment" />
</form>

The urls.py:

from django.conf.urls import patterns, url
from photos import views

urlpatterns = patterns('',
    url(r'^rate/(?P<photo_id>\d+)/$', views.rate, name='rate'),
    url(r'^(?P<photo_id>\d+)/$', views.details, name='details'),
    url(r'^$', views.index, name='index'),
)

I've added this script to avoid having the csrf validation fail when trying to submit comments via Ajax.

<script type="text/javascript">
    $(document).ajaxSend(function(event, xhr, settings) {
        function getCookie(name) {
            var cookieValue = null;
            if (document.cookie && document.cookie != '') {
                var cookies = document.cookie.split(';');
                for (var i = 0; i < cookies.length; i++) {
                    var cookie = jQuery.trim(cookies[i]);
                    // Does this cookie string begin with the name we want?
                    if (cookie.substring(0, name.length + 1) == (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                    }
                }
            }
            return cookieValue;
        }
        function sameOrigin(url) {
            // url could be relative or scheme relative or absolute
            var host = document.location.host; // host + port
            var protocol = document.location.protocol;
            var sr_origin = '//' + host;
            var origin = protocol + sr_origin;
            // Allow absolute or scheme relative URLs to same origin
            return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
                (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
                // or any other URL that isn't scheme relative or absolute i.e relative.
                !(/^(\/\/|http:|https:).*/.test(url));
        }
        function safeMethod(method) {
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }

        if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        }
    });

And this is the Ajax part I've been struggling with today and yesterday:

$(document).ready(function() {
        $('#commentForm').on('submit', function(e){
            e.preventDefault();
            var form = $(this);
            $.ajax(
                ???
            });
        });
    });

Thank you for your time.

Was it helpful?

Solution

You can't redirect from an Ajax post. In fact, the whole point of doing Ajax at all is to prevent the need for redirecting. You need to actually return some data: the usual way to do that is to return some JSON indicating what happens. If the form was valid and the save succeeded, you might just return an empty response: but if the form was not valid, you'll need to serialize the errors and return that as a JSON dict.

Then, in your Ajax script itself, you'll need to process that information and display it to the user. Your Ajax function should look something like:

var form = $(this);
$.post(form.attr('action'), form.serialize())
   .done(function(data) {
       // do something on success: perhaps show a "comment posted!" message.
   })
   .fail(function(xhr, status, error) {
      // do something on failure: perhaps iterate through xhr.responseJSON and
      // and insert the error messages into the form divs.
   });
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top