0

I really need some help with Ajax and Django! I'm trying to iterate over all Item objects so that each one has an individual form that allows the user to favorite the item with an ajax button. Here is my form:

<div class="row">
{% for item in items%}
  <div class="col-sm-6 col-md-4">
    <div class="thumbnail">
        {% if item.photo %}
      <img src="{{ MEDIA_URL }}{{item.photo.url}}">
        {% endif %}
      <div class="caption">
        <h3>{{item.name}}</h3>
        <p>{{item.description}}</p>
        <h3>Price: {{item.price}}</h3>
        <h3>Rating: {{item.rating}}</h3>
        <p>
            <form id="add_favorite_form">
                {% csrf_token %}
                <button id='fav' class='btn btn-primary' type="submit" data-itemid="{{item.id}}" value="Favorite">
                Favorite
                <span class="glyphicon glyphicon-thumbs-up"</span></a>
            </form>
        </p>
      </div>
    </div>
  </div>
{% endfor %}
</div>

Here is the Ajax:

    <script type="text/javascript">
        $(document).on('submit', '#add_favorite_form',function(e){
            e.preventDefault();
            var $fav = $('#fav');
            $.ajax({
                type: "POST",
                url: "/items/favorites/",
                data: {
                    id: $($fav).attr('data-itemid'),
                    csrfmiddlewaretoken:$("input[name=csrfmiddlewaretoken]").val()
                },
                success: function(){
                    alert("button works but is broken. Only the first Item instance is added to favorites")

                }
            });
        });
    </script>

And finally, my views:

def favorite_item(request):
    favorites, created = Favorite.objects.get_or_create(user=request.user)
    if request.method == "POST":
        id = request.POST['id']
        item = Item.objects.get(id=id)
        print item
        favorites.items.add(item)
        favorites.save()
    return HttpResponse(' ')

As you can see, the button uses {{item.id}} as the data-* attribute, so I assumed that this would pass the value of each individual item.id to the variable var $fav = $('#fav'); in the ajax script. However, only the first item.id ever seems to be passed because that is the only Item object that is added to favorites.

How can I solve this issue? Thanks for any help.

3
  • define fav as class instead of id Commented Apr 4, 2016 at 6:21
  • @AvinashRaj didn't work :( Commented Apr 4, 2016 at 6:28
  • This is wrong: {{ MEDIA_URL }}{{item.photo.url}}. If you configured your MEDIA_ROOT and MEDIA_URL correctly, {{item.photo.url}} should suffice (Django will add the necessary base path). Commented Apr 5, 2016 at 10:01

2 Answers 2

1
  1. You are creating an HTML element with id fav inside a loop. This will result in incorrect HTML with several elements having the same id, if the loop has more than one iteration.

  2. You lookup $('#fav') instead of the element that triggered the submit event.

=> Remove id='fav' from your markup. (You can add an ID like id='fav-{{item.id}} if you want but it is not necessary.

=> Change your JS:

<script type="text/javascript">
    var csrftoken = $("input[name=csrfmiddlewaretoken]").val();
    $(document).on('submit', '#add_favorite_form',function(e){
        e.preventDefault();
        var itemId= $(e.target).data('itemid');  // jquery +1.4.3 or attr()
        $.ajax({
            type: "POST",
            url: "/items/favorites/",
            data: {
                id: itemId,
                csrfmiddlewaretoken: csrftoken
            },
            success: function(){
                alert("Added favorite " + itemId);
            }
        });
    });
</script>

EDIT:

A simpler way to do it, is to use the form as such and just submit it via AJAX.

<form id="add_favorite_{{ item.id }}" class="add_favorite_form" method="POST" action="{% url 'add-favorite' %}">
    {% csrf_token %}
    <input type="hidden" name="id" value="{{ item.id }}" />
    <button id='fav' class='btn btn-primary' type="submit" value="Favorite">
        Favorite
    <span class="glyphicon glyphicon-thumbs-up"</span></a>
</form>

JS:

<script type="text/javascript">
    var csrftoken = $("input[name=csrfmiddlewaretoken]").val();
    $(document).on('submit', '.add_favorite_form',function(e) {
        e.preventDefault();
        e.stopPropagation();
        var $form = $(this);
        var url = $form.attr( "action" );
        var thisId = $form.attr( "id" );
        $.post(url, $form.serialize())
            .done(function(data) {
                alert("Added favorite " + thisId);
            });
    });
</script>
Sign up to request clarification or add additional context in comments.

1 Comment

Okay, so it's almost working but not quite. The button does not get to the success part, but the csrftoken is being rendered into the http link, like this: /items/?csrfmiddlewaretoken=nFShf590EQFMWizgPk5B4Kv8n0wY8fzL . I tried doing some of the fancy things the django documentation shows but it still won't work... do you have any experience with this problem?
0

You have a couple things wrong, but the one that is causing you the issue is that $('#fav') will only ever find the first element on the page with that id.

You'll find it hard to adjust your current javascript since it relies on the page submission. You could change it around to just have the button use an onclick event.

You will just need to include the CSRF token still but there are lots of resources about how to do that.

<button onclick="favourite({{item.id}})" class='btn btn-primary' value="Favorite">

function favourite(id){
    // Use the id passed to the method
}

2 Comments

Thanks for the insight. Sorry, but I'm not all that familiar with JS; can you elaborate a little more about the function favourite? Would I just put my ajax code in there or do I now need to approach it in a different way?
@tear728 - Pretty much yeah, its just a function that takes in an id as a parameter. You can then use that id however you like (i.e one of your ajax calls inputs). You'll just need to include a csrftoken too

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.