How To: For...Else in a Django template (if/else inside a for loop)

Viewed 406

I apologize in advance if this has already been asked, but I couldn't find any answers that answered the problem I'm having:

I need to do something similar to a For...Else loop in a Django template.

I need to show a button on a template base on an if condition:

  • If the user has already bought this product, then show button 1
  • If the user has not bought this product, then show button 2

For each product, I have to go through the user's purchases, and then show one button or the other depending on whether they have already bought the product or not.

A simplified (and wrong) version of the code would be like:

{% for product in products %}
  //other code here

  {% for purchase in purchases %}
     {% if purchase.service_id.id == product.id %}
       // show button 1
     {% else %}
       // show button 2
     {% endif %}
  {% endfor %}

{% endfor %}

However, this code doesn't work, as it shows both buttons as it goes through the for loop.

I can't do a For...Empty because the user may have other purchases (so the for loop wouldn't be empty), but none of them coincide with this product.

Thanks in advance.

EDIT:

Thanks @JashOFHop for the reply! In the end, I found a way around. I'll share it in case anyone else find themselves in this situation:

For clarity, the models concerned in this case are:

class User(AbstractUser):
    pass

class Service(models.Model):
    user_id = models.ForeignKey("User", on_delete=models.CASCADE, related_name="user_id_services")
    name = models.CharField(max_length=64)
    status = models.BooleanField(default=True)
    description = models.TextField(max_length=300)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="category_name")
    price = models.IntegerField()
    slots = models.IntegerField(default=1)
    amount = models.IntegerField(default=1)
    watchedby = models.ManyToManyField(User, blank=True, related_name="watchedby")

class Purchase(models.Model):
    user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_id_purchases")
    service_id = models.ForeignKey(Service, on_delete=models.CASCADE, related_name="service_id_purchases")
    amountpaid = models.IntegerField()

The view for this template was:

def explore(request):

    # get all the active services from the db
    services = Service.objects.filter(status=True).order_by('name')

    # get the catogories for the filter option
    categories = Category.objects.all().order_by('category')

    # get the info of the user
    userinfo = User.objects.get(username=request.user)

    # get the info of the user's purchases
    purchases = Purchase.objects.filter(user_id=userinfo)

    # render the template
    return render(request, "barter/explore.html", {
        "services": services,
        "categories": categories,
        "userinfo": userinfo,
        "purchases": purchases
    })

And the template, as explained above, rendered all the services and in each is supposed to check whether this user has already bought said service or not.

Solution:

In the view I've added this and passed it also to the template:

# create a list of the IDs of the services purchased by the user to be able to render the buy/bought button correctly
    purchases_list = []
    for purchase in purchases:
        purchases_list.append(purchase.service_id.id)

Then, the template is:

{% for service in services %}
   // other code with infomation of the service here

   // Important part:
   {% if service.id in purchases_list %}
     <button>You already bought this service</button>
   {% else %}
     <button>Buy now</button>
   {% endif %}

{% endfor %}
1 Answers

You can create another models, for example Profile

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    cart = models.MenyToManyField(Product, null=True)

so in views for byu button you can write like this

def buy(request, pk): #byu button
    product = Product.objects.get(id=pk)
    profile = Profile.objects.filter(user.username=request.user)
    profile.cart.add(product.id)
    return redirect('some html')

another button has the same

def index(request, pk): # for template
    product = Product.objects.get(id=pk)
    profile = Profile.objects.filter(user.username=request.user)
    return response(request, 'some html', context)

so template

{% for i in product%}
 {% for products in profile%}
     {% for p in products.cart.all %}
       {% if i.id in p.id%}
       <p>you bought</p>
       {% else %}
        <a href='byu'>Buy</a>
       {% endif %}
      {% endfor%}
    {% endfor%}
 {% endfor%}

but i think there is another the easiest way to solve your problem

Related