Can't get initial value into form correctly.

31 views
Skip to first unread message

Chris Kavanagh

unread,
May 15, 2016, 1:42:03 AM5/15/16
to Django users

I'm working on an online shop, and I'm having a problem in the cart,
 here's a screen shot (http://pasteboard.co/Vwnv2T0.png).
I'd like the form for each item (product) to show it's current quantity (that's already been added to the cart)
 as an initial value as most ecommerce sites do.

I've tried iterating through the items in the cart (in views.show_cart) then adding the form,
however the form for all items in the cart shows the exact same value (of the last item).
In other words, there's 2 items in the cart, one has a quantity of 7 and the other a quantity of 3,
 but both forms show the quantity as 3.

 I can't figure out how to correct this, although I'm sure it's a simple fix.
Thanks in advance for the help, and I hope this question is formatted correctly?
Here's the code on github if it helps. . .https://github.com/chriskavanagh/ecommerce/tree/master/src

# form
PRODUCT_QUANTITY_CHOICES = [(i, str(i)) for i in range(1, 21)]
class ProductAddToCartForm(forms.Form):
    quantity = forms.TypedChoiceField(choices=PRODUCT_QUANTITY_CHOICES, coerce=int)
    update = forms.BooleanField(required=False,initial=False,widget=forms.HiddenInput(attrs={'class': 'form-control'}))


# views (cart_id.views.show_cart)
def show_cart(request):
    cart_id = _cart_id(request)
    cart_items = CartItem.objects.filter(cart_id=cart_id)
    for item in cart_items:
        q = item.quantity
        print q    #shows correct value (quantity) of each item in cart
        add_product_form = ProductAddToCartForm(initial={'quantity': q})
        context = {'cart_items': cart_items, 'add_product_form': add_product_form}
    return render(request, 'cart_id/cart.html', context)


# template (cart.html)
{% extends 'base.html' %}
{% load staticfiles %}

{% block title %}Shopping Cart{% endblock %}

{% block content %} {% endblock %}

{% block extra_content %}
    <div class="container">
        <div class="col-md-12">

        <h2>Cart</h2>
        <caption><b>Your Shopping Cart:</b> {{ cart_items_count }} Items In Your Cart.</caption>   
        <hr>

        {% for item in cart_items %}

            <table class="table table-striped">
                <thead>
                    <tr>
                        <th>Product</th>
                        <th>Unit Price</th>
                        <th>Quantity</th>
                        <th>Total Price</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td><a href="{{ item.product.get_absolute_url }}">{{ item.product }}</a></td>
                        <td>${{ item.product.price }}</td>
                        <td>{{ item.quantity }}</td>
                        <td>{{ item.total }}</td>
                        <td>
                        <div class="form-group">
                            <form action="{% url 'cart:add' item.product.slug %}" method="POST">                               
                                    {% csrf_token %}
                                <div class="col-xs-3">
                                    {{ add_product_form.quantity }}
                                </div>                               
                                    {{ add_product_form.update }}                                                               
                                <input class='btn btn-success btn-sm' type='submit' value='Update'/>                               
                            </form>
                        </div>
                        </td>
                    </tr>
                </tbody>
            </table>

        {% empty %}
            <h3>You Have Nothing In Your Cart.</h3>
        {% endfor %}
    </div>
</div>   

{% endblock %}

Stephen J. Butler

unread,
May 15, 2016, 5:00:52 AM5/15/16
to django...@googlegroups.com
You assign to context every loop iteration. So at the end, only the last add_product_form constructed is ever used. Try this:

def show_cart(request):
    cart_id = _cart_id(request)
    cart_items = CartItem.objects.filter(cart_id=cart_id)
    add_product_forms = {}

    for item in cart_items:
        q = item.quantity
        print q    #shows correct value (quantity) of each item in cart
        add_product_form = ProductAddToCartForm(initial={'quantity': q})
        add_product_forms[item.pk] = add_product_form
    context = {'cart_items': cart_items, 'add_product_forms': add_product_forms}

    return render(request, 'cart_id/cart.html', context)

Note how I moved the context assignment OUTSIDE the for loop. Then adjust your template to index into add_product_forms based on item.pk.

There are lots of other ways to do this too, I just thought this was the quickest to show over email.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/adb6c8ab-77bb-46ba-bec6-21df4e5499b8%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Chris Kavanagh

unread,
May 15, 2016, 5:32:20 AM5/15/16
to Digest Recipients
Thank for the answer, Stephen. However, I'm still a little confused, and it apparently doesn't' work unless I'm doing something wrong in the template.
In the template, I'm supposed to change the "add_product_form" to "add_product_forms" in the template?
In other words, "add_product_forms.quantity" and "add_product_forms.update"? Im missing something. . .

--
You received this message because you are subscribed to a topic in the Google Groups "Django users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/django-users/K7C47FE-Q3g/unsubscribe.
To unsubscribe from this group and all its topics, send an email to django-users...@googlegroups.com.

To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Chris Kavanagh

unread,
May 15, 2016, 5:48:17 AM5/15/16
to Digest Recipients
Yeah, I'm obviously doing something wrong in the template, Stephen. I'm not quite sure what I'm supposed to do (in the template). You kinda lost me on the last sentence, "Then adjust your template to index into add_product_forms based on item.pk."

Could you give me a hint, please? And thank you so much for your help!

On Sun, May 15, 2016 at 1:00 AM, Stephen J. Butler <stephen...@gmail.com> wrote:

--
You received this message because you are subscribed to a topic in the Google Groups "Django users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/django-users/K7C47FE-Q3g/unsubscribe.
To unsubscribe from this group and all its topics, send an email to django-users...@googlegroups.com.

To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Stephen J. Butler

unread,
May 15, 2016, 5:56:05 AM5/15/16
to django...@googlegroups.com
It's now a dict, so you have to index into it. The for loop won't magically know which element of add_product_forms you want. You'd want add_product_forms[item.pk].

But ugg... apparently Django templates won't (out of the box) let you index a dict using another variable. Thanks for reminding me why I switched to Jinja.

Change it to this:

def show_cart(request):
    cart_id = _cart_id(request)
    cart_items = CartItem.objects.filter(cart_id=cart_id)
    objects = []
    for item in cart_items:
        q = item.quantity
        print q    #shows correct value (quantity) of each item in cart
        add_product_form = ProductAddToCartForm(initial={'quantity': q})
        objects.append({item: item, form: add_product_form})
    context = {'objects': objects}
    return render(request, 'cart_id/cart.html', context)

Then in your template, loop over "objects" instead of "cart_items":

{% for obj in objects %}
{{ obj.item.total }}
{{ obj.form.quantity }}

etc. See what's going on? Each item needs its own form.

Chris Kavanagh

unread,
May 15, 2016, 6:31:12 AM5/15/16
to Digest Recipients
Nope, can't get this to work either. This way, nothing works. Nothing will display on the page this way, not the products nor the form.
I see now what you were trying to do in the 1st example, however as you said, django templates won't allow that.

I was able to do the form in the template manually (not using a django form), just html <input>, and
assigned the value of the quantity to the value of the form element and this works ok.

It seems there would be an easier way to do something so simple. Anyways, thanks for all the help,
it is appreciated.

Chris Kavanagh

unread,
May 15, 2016, 9:04:57 PM5/15/16
to Digest Recipients
Stephen, my apologies, it most certainly did work! You did make one small mistake by forgetting the quotes around the item and the form in the dict. . .
like this [{'item': item, 'form': form}]. This is why it didn't work the 1st time I tried it, you were missing the quotes and I didn't catch it till this morning.

 I was so tired last night I couldn't think straight, lol. Anyways, I'd like to thank you again for all the help. And if you have the time. . .
I'd like to hear the other ways this could be accomplished (as you mentioned earlier)? I know you're probably busy, but if you have
the time and don't mind, I love to learn. . .

Thanks, Chris.

Reply all
Reply to author
Forward
0 new messages