Buttons and Django-templates for-loops are driving me mad

PrimeSteak

Expert Member
Joined
Nov 7, 2020
Messages
1,600
Can someone please help me out? I'm going to link my SO question here cause I don't wanna retype everything.

Link:

Thanks in advance for the help!

Kind Regards
PrimeMinister
 

DA-LION-619

Honorary Master
Joined
Aug 22, 2009
Messages
13,247
Can someone please help me out? I'm going to link my SO question here cause I don't wanna retype everything.

Link:

Thanks in advance for the help!

Kind Regards
PrimeMinister
Python:
{% for like in liked %}
    {% if like.liked_post == page %}
        <span><button class="btn btn-secondary" id="unlike">Unlike</button></span>
    {% endif %}
    {% if like.liked_post != page %}
        <span><button class="btn btn-secondary" id="like">Like</button></span>
    {% endif %}
{% empty %}
    <span><button class="btn btn-secondary" id="like">Like</button></span>
{% endfor %}
 

PrimeSteak

Expert Member
Joined
Nov 7, 2020
Messages
1,600
Python:
{% for like in liked %}
    {% if like.liked_post == page %}
        <span><button class="btn btn-secondary" id="unlike">Unlike</button></span>
    {% endif %}
    {% if like.liked_post != page %}
        <span><button class="btn btn-secondary" id="like">Like</button></span>
    {% endif %}
{% empty %}
    <span><button class="btn btn-secondary" id="like">Like</button></span>
{% endfor %}
Ok, so after a while I noticed that it might be the loop that's producing the unnecessary buttons. But my problem is how can I check the posts without one? Cause it seems like the loop is taking the number of records from like and then generating that number of buttons (which it shouldn't). I'm not sure how to alleviate this, the frustrating thing here is that I just need to get this button-shitstorm done, then I can submit my project cause everything else is done
 
Last edited:

DA-LION-619

Honorary Master
Joined
Aug 22, 2009
Messages
13,247
Ok, so after a while I noticed that it might be the loop that's producing the unnecessary buttons. But my problem is how can I check the posts without one? Cause it seems like the loop is taking the number of records from like and then generating that number of buttons (which it shouldn't). I'm not sure how to alleviate this, the frustrating thing here is that I just need to get this button-shitstorm done, then I can submit my project cause everything else is done
Why are you checking the posts in your View?
It's harder because you're actively going against your framework.
 

DA-LION-619

Honorary Master
Joined
Aug 22, 2009
Messages
13,247
I thought that I could get the needed posts and then just check them in the template and then get the needed buttons. Should I do it in JS or how else?
Makes sense, so why can't you check the posts based on a property?
Code:
post.IsLiked

Cause it seems like the loop is taking the number of records from like and then generating that number of buttons (which it shouldn't).
Python:
{% for something in anything %}
    {% if conditionA  %}       
        This can happen       
    {% endif %}   
    
    {% if conditionB %}
        This can also happen
    {% endif %}
{% empty %}
 

PrimeSteak

Expert Member
Joined
Nov 7, 2020
Messages
1,600
Makes sense, so why can't you check the posts based on a property?
Code:
post.IsLiked
I have a model called Like which has two foreign keys (one from the Post model and another from the User model). My Post model only has fields for the user (an FK), the content (CharField), date posted on (also CharField for the sake of practicality) and an IntegerField for the number of likes the post has. I don't have a spare property I can use to compare. That's why I thought getting the posts the user liked from Like and comparing those posts to the posts generated on the page, would be the easiest way of rendering the right buttons on every post. But at this rate, I think I'm just going to make it a single button and let an alert display that says the user liked or disliked the post (a bit brute force, but meh).
Python:
{% for something in anything %}
    {% if conditionA  %}      
        This can happen      
    {% endif %}  
   
    {% if conditionB %}
        This can also happen
    {% endif %}
{% empty %}
Yeah, I guess. I initially tried using elif and else to try and alleviate this but also to no avail.
 

SaucePlz

Senior Member
Joined
Aug 11, 2020
Messages
550
A user should only be able to like or dislike a post once, so I would say you need to adjust your query for getting the likes. Get only the like where it's post_id and user_id matches the current user and post. I'm guessing that the extra buttons are being generated from other user's likes on that post being included during that loop.

Edit: nevermind, pretty sure the unneeded buttons are from the user's likes on other posts. I'm assuming that like.liked_post is an int referencing the id from Post? In which case you should be checking page.id in your if. And then you will still need that elif.
At the moment while looping through the posts, you loop through the liked_posts each time, but you don't check if the liked_post matches the page_id, seems like you are comparing an int with an object.
 
Last edited:

PrimeSteak

Expert Member
Joined
Nov 7, 2020
Messages
1,600
Edit: nevermind, pretty sure the unneeded buttons are from the user's likes on other posts. I'm assuming that like.liked_post is an int referencing the id from Post? In which case you should be checking page.id in your if. And then you will still need that elif.
At the moment while looping through the posts, you loop through the liked_posts each time, but you don't check if the liked_post matches the page_id, seems like you are comparing an int with an object.
The like.liked_post is a Foreign Key object of the Post model(in essence a copy or reference of a record in Post) and page is an object representing a Post model record (more specifically page is based on the posts from Post that are entered into the Paginator function Django offers). But I could try to match the IDs maybe, even tho I doubt it would change anything? I've considered maybe putting the like loop on the outside of the page loop, but I doubt that would work either
 

SaucePlz

Senior Member
Joined
Aug 11, 2020
Messages
550
The like.liked_post is a Foreign Key object of the Post model(in essence a copy or reference of a record in Post) and page is an object representing a Post model record (more specifically page is based on the posts from Post that are entered into the Paginator function Django offers)
ok, my bad, didn't think it would reference the whole object
 

DA-LION-619

Honorary Master
Joined
Aug 22, 2009
Messages
13,247
The like.liked_post is a Foreign Key object of the Post model(in essence a copy or reference of a record in Post) and page is an object representing a Post model record (more specifically page is based on the posts from Post that are entered into the Paginator function Django offers). But I could try to match the IDs maybe, even tho I doubt it would change anything? I've considered maybe putting the like loop on the outside of the page loop, but I doubt that would work either
What would happen if a user has liked posts that are not on the page?
 

PrimeSteak

Expert Member
Joined
Nov 7, 2020
Messages
1,600
When do you need to submit?
Technically by Jan 1 2022, lmao. I tend to keep it week by week for example if I get Project 4 to do for this week then I'd preferably finish it in that week. Cause this is the last homework per se of the course, then next week if I finished this project, then it's only one more lecture and the Final Project and then I'm finished. Cause I want to be finished with the course by the end of the month/start of October.
I can take a look tomorrow
Thx bruv, appreciate it
 

PrimeSteak

Expert Member
Joined
Nov 7, 2020
Messages
1,600
What would happen if a user has liked posts that are not on the page?
Do you mean if the user has liked posts but not a specific one? Then only a Like button should be generated (theoretically anyway)for that post, the same if the user has no likes. But I'm going to check my view that adds and deletes the likes from the model. Maybe I should add a constraint with the adding part
 

MagNorthDigital

Well-Known Member
Joined
Aug 5, 2015
Messages
147
@PrimeSteak firstly cudos for trying to solve this on your own first.

Now, how to solve this.

Ask yourself, what are you trying to accomplish, in the simplest terms.

That would be to toggle a button on the frontend, but more importantly to track and manage the state relative to the user and the specific post.

So, here is what you do.

Firstly, make 2 buttons on the page, forget conditionals for now and get the functional part out of the way.

Once you can reliably like and unlike a post, across refreshes... (for that specific user of course)

Write a single button into the page, and give it the current state, and then when the user clicks the button, send the inverse state back to the server (in the same way you did it with the "two button setup") and then all you need to do is invert the id and data on the button, ready for the next state change.

I don't know how you're storing the data, but you would want to either track user associated to post or post associated to user. (It depends on how you access the data) And then create a boolean on one of those models to track
liked: bool

There are many ways to do this, find the way that you can reason about, and explain to yourself.
 
Last edited:

DA-LION-619

Honorary Master
Joined
Aug 22, 2009
Messages
13,247
Do you mean if the user has liked posts but not a specific one? Then only a Like button should be generated (theoretically anyway)for that post, the same if the user has no likes. But I'm going to check my view that adds and deletes the likes from the model. Maybe I should add a constraint with the adding part
You’re taking a set of posts, you pass it into your pagination function and you get your subset.
Page 1, with 10 posts.
That’s your first loop, which you have.

You then have your other set, which is all the likes filtered by the user.

The likes are not related to those 10 posts on the page.
If I liked 100 posts, within your post loop, your second loop is going through my 100 likes.

Also you’re dealing with two different aspects here that may appear to be the same but are not.
Data Model(ORM) => You don’t need to add a constraint, that’s the job of your database. If you expect but don’t have referential integrity, throw your database away.
View Model(MVC) => You should be able to render data from anywhere, if you got a JSON response from an API as an example. If your View is concerned about your data store, throw your framework away.

When in doubt look at something similar, like Stack Overflow which follows an MVC pattern.
 
Last edited:

PrimeSteak

Expert Member
Joined
Nov 7, 2020
Messages
1,600
@PrimeSteak firstly cudos for trying to solve this on your own first.

Now, how to solve this.

Ask yourself, what are you trying to accomplish, in the simplest terms.

That would be to toggle a button on the frontend, but more importantly to track and manage the state relative to the user and the specific post.

So, here is what you do.

Firstly, make 2 buttons on the page, forget conditionals for now and get the functional part out of the way.

Once you can reliably like and unlike a post, across refreshes... (for that specific user of course)

Write a single button into the page, and give it the current state, and then when the user clicks the button, send the inverse state back to the server (in the same way you did it with the "two button setup") and then all you need to do is invert the id and data on the button, ready for the next state change.

I don't know how you're storing the data, but you would want to either track user associated to post or post associated to user. (It depends on how you access the data) And then create a boolean on one of those models to track
liked: bool

There are many ways to do this, find the way that you can reason about, and explain to yourself.
Ok, so I've made 2 buttons. That said, they work as intended and they switch (meaning the one's displayed, the other isn't, I toggled it with JS.) Now there's a single hurdle left, determining what button is displayed first and which one should be hidden and that's what gets me.

1630859823937.png

It works correctly when the liked queryset is empty, the same if there are liked posts. The problem is just with the other option if the post doesn't exist in liked, cause when I also add that if statement, then that's when the buttons rise up against me. I've considered using break as you normally would in Python, but Django's templating language doesn't support it and Jinja doesn't support it out of the box.

I've tried this to get a break function but it doesn't work at all. And I don't understand how slicing the loop would work either (would be great if someone would explain this to me).

So yeah, current state of affairs is the project is 95% complete and the other 5% is stubborn af
 

Attachments

  • 1630859669907.png
    1630859669907.png
    24 KB · Views: 1

MagNorthDigital

Well-Known Member
Joined
Aug 5, 2015
Messages
147
Ok, so I've made 2 buttons. That said, they work as intended and they switch (meaning the one's displayed, the other isn't, I toggled it with JS.) Now there's a single hurdle left, determining what button is displayed first and which one should be hidden and that's what gets me.

View attachment 1141532

It works correctly when the liked queryset is empty, the same if there are liked posts. The problem is just with the other option if the post doesn't exist in liked, cause when I also add that if statement, then that's when the buttons rise up against me. I've considered using break as you normally would in Python, but Django's templating language doesn't support it and Jinja doesn't support it out of the box.

I've tried this to get a break function but it doesn't work at all. And I don't understand how slicing the loop would work either (would be great if someone would explain this to me).

So yeah, current state of affairs is the project is 95% complete and the other 5% is stubborn af
Alright!

You don't need 4 buttons :)
You can just use one.

Given your conditional above.

You have 2 possible values... like / unlike
So you can use that to your advantage, if not one, then it must be the other value.

And so, your initial value will always be the result of that conditional.

Remember that js on the page is "live" (unlike on the server side) so let's say, you write the id (on the client side) as unlike, the next time the user clicks on that button id will be unlike.

So write the initial state (button) into the page, and then you have live control of the state on the client side. Your js function should then post the new data, aka like or unlike back to the server. (eg. Ajax) After the server returns a confirmation, change the values of the button (the id and text)
 

PrimeSteak

Expert Member
Joined
Nov 7, 2020
Messages
1,600
UPDATE: My suffering is over, it works as it should now thx to @SaucePlz and his help

Here's the answer to the question:

In your index.html

Code:
Python:
{% if user.is_authenticated %}
{% for like in liked %}
{% if like.liked_post == page %}
<span class="is-liked-unlike"><button class="btn btn-secondary" id="unlike">Unlike</button></span>
<span class="is-liked-like"><button class="btn btn-secondary" style="display:none"
id="like">Like</button></span>
{% endif %}
{% endfor %}
<span class="is-not-liked-likeBtn"><button class="btn btn-secondary" id="like">Like</button></span>
<span class="is-not-liked-unlikedBtn"><button class="btn btn-secondary" id="unlike"
style="display:none">Unlike</button></span>
{% endif %}

In your network.js, in the divs.forEach:

Code:
JavaScript:
div.querySelectorAll(".is-liked-unlike").forEach((unliked) => {
unliked.nextElementSibling.nextElementSibling.remove();
unliked.nextElementSibling.nextElementSibling.remove();
});
Thx to everyone who participated in this thread!
 
Top