Najaaz
Najaaz

Reputation: 390

django forloop counter as a variable

So I am trying to make an e-commerce pizza site where when a user adds an item to the basket it is stored as a cookie in the form cart=[{"id":"2","quantity":"1"},{"id":"3","quantity":"5"},{"id":"5","quantity":"3"},{"id":"5","quantity":"3"}]

And my views.py is...

def cart(request):
    cart = request.COOKIES.get('cart')
    cart = json.loads(cart)

    items =[]
    basket=[]
    for i in cart:
        pizza = Product.objects.filter(pk=i['id']).first()
        basket.append(int(i['quantity']))
        items.append(pizza)

    cart = len(cart)

    context = {
        "cart":cart,
        "pizzas":items,
        "basket":basket
    }
    return render (request, "store/cart.html", context)

And my models.py is ...

class Product(models.Model):
    name = models.CharField(max_length=70)
    price = models.IntegerField()
    description = models.CharField(max_length=300)
    image = models.ImageField(upload_to="pizza_images")

    def save(self, *args, **kwargs):
        super().save()
        img = Image.open(self.image.path)

        img.save(self.image.path)

    def __str__(self):
        return f"{self.name}"

In which my template code...

 <table>
            <thead>
                <tr>
                    <th scope="col" class="small_col">Item</th>
                    <th id="product_col" scope="col">Product</th>
                    <th scope="col" class="small_col">Quantity</th>
                    <th scope="col" class="small_col">Price</th>
                </tr>
            </thead>

            <tbody>
                {% for pizza in pizzas %}

                    <tr>
                        <td class="center">{{forloop.counter}}</td>
                        <td>{{pizza.name}}</td>
                        <td class="quantity_row justify-content-center">
                            <input class="quantity_input" type="text" value="{{basket.forloop.counter0}}" id="pizza{{forloop.counter}}">
                            <div class="form_buttons">
                                <img onclick="add(this.getAttribute('data-attribute'))" data-attribute="pizza{{forloop.counter}}" src="https://s.svgbox.net/hero-outline.svg?ic=plus-sm&fill=000" >
                                <img onclick="minus(this.getAttribute('data-attribute'))" data-attribute="pizza{{forloop.counter}}" src="https://s.svgbox.net/hero-outline.svg?ic=minus-sm&fill=000">
                            </div>
                        </td>
                        <td class="center">
                            <h6>$25</h6>
                        </td>
                        <td class="small_col">
                            <a><img class="mb-1" src="https://s.svgbox.net/hero-outline.svg?ic=trash&fill=d90429" width="20" height="20"></a>
                        </td>
                    </tr>
                        
                {% endfor %}

            </tbody>

        </table>

And in the input field I want the quantity of the product to be displayed. The line which says... <input class="quantity_input" type="text" value="{{basket.forloop.counter0}}" id="pizza{{forloop.counter}}">and for somereason the forloop.counter doesn't get the the value of the list and makes it null.

Any ideas on how i could make it work? Thanks!

Upvotes: 2

Views: 1894

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476614

{{ basket.forloop.counter0 }} will not work, since this simply will look if basket has a forloop attribute or item, but that is not the case, since both basket.forloop and basket['forloop'] will raise an error.

It is also not a good idea, what you do here is writing business logic in the template. A template should only contain rendering logic: logic that explains how to render data in a pleaseant way, not what data to render.

You can make use of zip(…) [python-doc] to generate 2-tuples of items, and then enumerate over these 2-tuples in the template:

def cart(request):
    cart = request.COOKIES.get('cart')
    cart = json.loads(cart)

    items = []
    basket = []
    for i in cart:
        pizza = Product.objects.filter(pk=i['id']).first()
        basket.append(int(i['quantity']))
        items.append(pizza)
    cart = len(cart)

    context = {
        "cart":cart,
        'pizzabaskets': zip(items, basket),
    }
    return render (request, 'store/cart.html', context)

and then render this with:

{% for pizza, basket in pizzabaskets %}
  <tr>
    <td class="center">{{forloop.counter}}</td>
    <td>{{pizza.name}}</td>
    <td class="quantity_row justify-content-center">
      <input class="quantity_input" type="text" value="{{basket}}" id="pizza{{forloop.counter}}">
      <div class="form_buttons">
        <img onclick="add(this.getAttribute('data-attribute'))" data-attribute="pizza{{forloop.counter}}" src="https://s.svgbox.net/hero-outline.svg?ic=plus-sm&fill=000" >
        <img onclick="minus(this.getAttribute('data-attribute'))" data-attribute="pizza{{forloop.counter}}" src="https://s.svgbox.net/hero-outline.svg?ic=minus-sm&fill=000">
      </div>
    </td>
    …
{% endfor %}

the basket it is stored as a cookie.

I would advise not to work with cookies. Cookies can easily be manipulated by the user through request forgery. This thus means that a user can alter the cookies and for example make an order with

[{"id":"2","quantity":"-10"},{"id":"3","quantity":"5"},{"id":"5","quantity":"3"}]

so a person can order a negative amount of pizzas to reduce the price. The user has the freedom to submit a different structure and try to look for vulnerabilities. If you want to have data that is attached to the session, it is better to make use of session variables [Django-doc], since these are stored at the server side.

Upvotes: 2

Related Questions