jijna2 for loop variable preservation? - python

I'm working on a web app and trying to add functionality where you make an anonymous post and then someone can add an answer to it. I'm using Flask for backend. I have the posts stored in an SQLAlchemy database. By default, a post doesn't have an answer. To dispay the posts, I just print each one with a for loop using jinja2. In that for loop, I also print an "answer" button after each post that brings up a Foundation modal that is supposed to let the user enter their answer. The problem is that the action attribute references the post ID ({{post.id}}), which by the end of the loop refers to the last post. Here's my code:
{% for post in s: %}
{% if post.nh == hood %}
<li>
<td> {{ post.date.strftime('%Y-%m-%d %H:%M:%S') }} </td>
<br>
<td> {{ post.text }} {{post.nh}} </td>
<br>
Answer
<div id="reply_modal" class="reveal-modal" data-reveal aria-labelledby="modalTitle" aria-hidden="true" role="dialog">
<p>{{ post.text }}</p>
<form action='/{{post.id}}/{{hood}}' method="post">
<div class="row">
<div class="large-12 columns">
<textarea placeholder="Write your answer here"name="user_input" required></textarea>
</div>
<div class="right">
<button type="submit">Submit</button>
</div>
</div>
</form>
<a class="close-reveal-modal" aria-label="Close">×</a>
</div>
<br>
{% if post.answer != 'none' %}
<li>
<div align="right">
<td> {{ post.answer }} </td>
</div>
</li>
{% endif %}
</li>
{% endif %}
{% endfor %}
I basically need to preserve {{post.id}} for each "answer" button so that the answer gets added to the correct post. I would appreciate any suggestions.

Related

collapsing accordion content in django for loop

I am trying to implement for loop in my accordion. Everything seems to be fine except the fact that when I click on 3rd or 4th button then other bodies stay expanded. It should work exactly the same as in the first example in Bootstrap documentation so if you click on Accordion Item #2 then Accordion Item #1 and Accordion Item #3 collapse.
I am sure that the issue is with my {% if forloop.first %} but I am not sure how can I dynamically change that so it will collapse all accordion contents except active one.
My code:
{% for category in top_categories %}
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="heading{{category.id}}">
<button class="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target="#collapse{{category.id}}" aria-expanded="false" aria-controls="collapse{{category.id}}">
{{category.title}}
</button>
</h2>
<div id="collapse{{category.id}}" class="accordion-collapse {% if forloop.first %} show {% else %} collapse {% endif %}" aria-labelledby="heading{{category.id}}"
data-bs-parent="#accordionExample">
<div class="accordion-body">
{% for qa in category.qa.all|slice:"0:3" %}
Q: {{qa}}<hr>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
I think that the main problem is that you are including <div class"accordion" id="accordionExample"> inside the loop. A secondary one is that the "collapse" class is also necessary for the first iteration. Try this:
<div class="accordion" id="accordionExample">
{% for category in top_categories %}
<div class="accordion-item">
<h2 class="accordion-header" id="heading{{category.id}}">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{category.id}}" aria-expanded="true" aria-controls="collapse{{category.id}}">
{{category.title}}
</button>
</h2>
<div id="collapse{{category.id}}" class="accordion-collapse collapse {% if forloop.first %}show{% endif %}" aria-labelledby="heading{{category.id}}" data-bs-parent="#accordionExample">
<div class="accordion-body">
{% for qa in category.qa.all|slice:"0:3" %}
Q: {{qa}}<hr>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>

Python Jinja2 loop index to make id unique

I'm using jinja2 template with Flask and MongoDB and I need to display users' cars in a template.
I was able to render cars in the profile template with car information and buttons to edit or delete a car. The problem is that if a user has multiple cars, only the first DELETE CAR button actually works (where I have an include modals to confirm) but nothing happens when click on the second or third delete button. Apparently I duplicated the id that needs to be unique. But I cannot achieve the results.
Here is my template: profile.html
{% extends 'layout/base.html' %}
{% block content %}
{% include 'components/navigation/navigation.html' %}
<div class="container">
<div class="row g-3 justify-content-center">
<div class="col-6 mt-5">
<div class="card">
<div class="card-body">
<h3 class="text-center profileHeader">
{{ username }}'s profile
</h3>
</div>
</div>
</div>
</div>
<div class="row row justify-content-center mt-4">
{% for car in cars %}
{% if session.user|lower == car.created_by|lower %}
<div class="card mb-3" style="max-width: 840px;">
<div class="row g-0">
<div class="col-md-4">
<img src="{{ car.car_image }}" class="imgCard" alt="ferrari image">
</div>
<div class="col-md-8">
<div class="card-body">
<ul class="list-group list-group-flush">
<li class="list-group-item"><strong>Year</strong>: {{ car.car_year }} </li>
<li class="list-group-item"><strong>Name</strong>: {{ car.car_name }}</li>
<li class="list-group-item"><strong>Designer</strong>: {{ car.car_design }}</li>
</ul>
<ul class="list-group list-group-flush">
<li class="list-group-item"><strong>Engine</strong>: {{ car.spec_engine }}</li>
<li class="list-group-item"><strong>Power</strong>: {{ car.car_power }}</li>
<li class="list-group-item"><strong>Trasmission</strong>: {{ car.trasmission }}</li>
<li class="list-group-item"><strong>Races</strong>: {{ car.races }}</li>
<li class="list-group-item"><strong>Wins</strong>: {{ car.wins }}</li>
<li class="list-group-item"><strong>Podiums</strong>: {{ car.podiums }}</li>
<li class="list-group-item"><strong>Poles</strong>: {{ car.poles }}</li>
<li class="list-group-item"><strong>Fast Laps</strong>: {{ car.fast_laps }}</li>
<li class="list-group-item"><strong>Constructor Championship</strong>: {{
car.constructor_champ
}}</li>
<li class="list-group-item"><strong>Driver Championship</strong>: {{ car.drivers_champ }}
</li>
<li class="list-group-item"><strong>Description</strong>: {{ car.description }}</li>
<p><em>by: {{ car.created_by }}</em></p>
</ul>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<a id="myBtn" class="btn btn-danger"><i class=" fas fa-trash-alt"></i> Delete</a>
<a href="{{ url_for('editcar', car_id=car._id) }}" class="btn btn-danger" id="edit-btn"><i
class="far fa-edit"></i> Edit</a>
{% with car_id=car._id %}
{% include 'components/modals/confirm.html' %}
{% endwith %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
enter code here
</div>
{% endblock %}
Just make unique the tag id of delete button, so the trigger will be unique in every button. How to make unique ? just add the car._id on tag id
<a id="myBtn-{{car._id}}" class="btn btn-danger"><i class=" fas fa-trash-alt"></i> Delete</a>
As #Darn pointed out, your ids need to be unique. But that is not the cause of your issue here.
I assume you have some Javascript code that gets triggered when user clicks on the delete link. The issue is probably from how that code is being triggered. The code needs to be written in such a way that it is bound to dynamically generated elements (you're generating the delete links on the fly i.e. afte the code for the delete was already bound; that delete code needs to also apply to each element when it is created).
You should bind the delete action to a class e.g.
$(".fa-trash-alt").on("click", function(){
$(this).parent() ...... // this is the link to the instance of the link that was clicked
});

Problem iterating through MongoDB data using Flask & Jinja2

brand new to Flask/Python and i'm trying to iterate through a list of data from MongoDB using Flask. The data is grabbed with no issues but the result i get is all of the database entries stuffed into one div. Below i've provided some screenshots to further explain the problem.
The 2 MongoDB Entries:
The idea is to have 2 separate cards displaying the 2 entries. The card will display the name_moving, once clicked, the rest of the info will appear in a modal.
HTML Template Code:
<div id="modal1" class="modal request_info_wrap">
{% for user_details in user_details: %}
<div class="modal-content request_info_card">
<h4 class="request_name_header">{{ user_details.user_name }} {{ user_details.user_lname }}</h4>
<p class="request_info"><span class="request_title">Request
Address:</span><br>{{ user_details.user_street }}<br>{{ user_details.user_postcode }}</p><br>
<p class="request_info"><span class="request_title">Contact
Number:</span><br>{{ user_details.user_contact }}</p><br>
<p class="request_info"><span class="request_title">Requested
Date:</span><br>{{ user_details.user_date }}</p><br>
<div class="deep_clean_section">
<p class="request_info" style="color: #000;"><span class="request_title">Deep Clean
for:</span><br>{{ user_details.carpet_clean }}<br>{{ user_details.floor_steam }}<br>
{{ user_details.white_goods }}<br>{{ user_details.window_clean }}
</p>
</div><br>
<hr style="margin: 0;">
<p class="request_info"><span class="request_title">Customer
Message:</span><br>{{ user_details.user_message }}</p>
</div>
<div class="modal-footer job_card_btn_wrap">
<a href="#" class="modal-close replyto_btn">Reply to
{{ user_details.user_name }}</a>
</div>
{% endfor %}
</div>
</div>
The issue i'm having is that both entries show within the same div (shown below) whereas im trying to get each MondoDB entry to show individually when clicked.
Python/Flask Code:
#app.route("/deep_clean_info", methods=["GET", "POST"])
def deep_clean_info():
if request.method == "POST":
user_details = {
"user_name": request.form.get("user_name"),
"user_lname": request.form.get("user_lname"),
"user_contact": request.form.get("user_contact"),
"user_street": request.form.get("user_street"),
"user_postcode": request.form.get("user_postcode"),
"user_message": request.form.get("user_message"),
"user_date": request.form.get("user_date"),
"carpet_clean": request.form.get("carpet_clean"),
"floor_steam": request.form.get("floor_steam"),
"white_goods": request.form.get("white_goods"),
"window_clean": request.form.get("window_clean"),
}
mongo.db.user_details.insert_one(user_details)
flash("Request sent to cleaner")
return redirect(url_for("deep_clean_info"))
details = mongo.db.user_details.find().sort("user_details", 1)
return render_template("deep_clean_details.html", user_details=details)
I do think the issue is with my jinja2 for loop in my template but any help would be greatly appreciated, also if any additional information is needed i'll be more than happy to provide. Thanks!
EDIT
HTML Template that seemed to iterate fine, although i dont want to use this style to display the data.
<ul class="collapsible popout pending_jobs">
{% for user_details in user_details %}
<li>
<div class="collapsible-header request_item_header">
<i class="fas fa-comment-dots"></i>
{{ user_details.user_name }} {{ user_details.user_lname }}
</div>
<div class="collapsible-body request_info">
<div class="row">
<span class="row info_headers">Address:<br>
<span class="light_text info_text" id="customer_address">
{{ user_details.user_street }}</span>
</span><br>
<span class="info_headers">Contact Number:<br>
<span class="light_text info_text" id="customer_contact">{{ user_details.user_contact }}</span>
</span><br>
</div>
<span class="info_headers row customer_request_style">Customer Request:<br>
<span class="light_text italic_text" id="customer_request">{{ user_details.user_message }}</span>
</span><br>
<span class="info_headers">Cleaning Date:<br>
<span class="light_text italic_text" id="customer_request">{{ user_details.user_date }}</span>
</span><br>
<a href="{{ url_for('cleaner_chat') }}"><button class="respond_to_user">Respond to
{{ user_details.user_name }}?</button></a>
</div>
</li>
{% endfor %}
</ul>

Django forloop and all selectable radio buttons

Here is a seemingly simple task, creating a form using a set of records so the user can choose which record to go for, all using a radio button.
<form action="" method="GET">{% csrf_token %}
{% for record in select_records %}
<div class="form-check indent-3">
<label class="form-check-label" for="radio{{forloop.counter}}">
<input type="radio" class="form-check-input" id="radio{{forloop.counter}}" name="{{record.get_model_name}}{{record.id}}" value="{{record.record_name}}">
{% if request.user.userprofile.head_shot_thumb %}
<img src="{{ request.user.userprofile.head_shot_thumb }}" alt="Proforma creator">
{% else %}
<div class="h2-li ">
<i class="fas fa-user"></i>
</div>
{% endif %}
{{ record.record_name }} - {{ record.date_created }}
</label>
</div>
{% endfor %}
The problem is that the form creates a list of radio buttons which are all selectable, just like how all the checkboxes are selectable.
I have searched and compared my code to simple radio forms such as the one at W3schools, but I cannot figure it out. Any help is appreciated.
I did small changes in your code. Check it below.
<form action="" method="GET">
{% csrf_token %}
{% for record in select_records %}
<div class="form-check indent-3">
<label class="form-check-label" for="radio{{forloop.counter}}">
<input type="radio" class="form-check-input" id="radio{{forloop.counter}}" name="record-option" value="{{record.record_name}}">
{% if request.user.userprofile.head_shot_thumb %}
<img src="{{ request.user.userprofile.head_shot_thumb }}" alt="Proforma creator">
{% else %}
<div class="h2-li ">
<i class="fas fa-user"></i>
</div>
{% endif %}
{{ record.record_name }} - {{ record.date_created }}
</label>
</div>
{% endfor %}
</form>
I hope this will help you. :)

Bootstrap accordion with Django: How to only load the data for the open accordion section?

I'm trying to make a webpage that will display recipes in the format of a bootstrap accordion like so (see here).
This is how I'm doing it as of now:
<div class="panel-group" id="accordion">
{% for recipe in recipes %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapse{{ forloop.counter }}">
{{ recipe }}
</a>
</h4>
</div>
<div id="collapse{{ forloop.counter }}" class="panel-collapse collapse">
<div class="panel-body">
<table class="table table-hover">
{% for ingredient in foodtype|ingredients_in_recipe:recipe %}
<tr>
<td>
{{ ingredient.ingredient_name }}
</td>
<td>
{{ ingredient.ingredient_quantity }}
</td>
</tr>
{% endfor %}
<p>{{ recipe.details }}</p>
</table>
</div>
</div>
</div>
{% endfor %}
</div>
I have made a custom template tag for this like so:
#register.filter
def ingredients_in_recipe(foodtype, recipe):
return foodtype.ingredient_set.filter(recipe=recipe).order_by("ingredient_name")
The problem is that I have 200+ recipes and loading all this data is way too slow. Ideally the template tag function ingredients_in_recipe should only be called when the user clicks on the recipe. However from my understanding this isn't possible because Django runs it all then sends the rendered HTML to the user.
Is there anyway I could circumvent this issue whilst still keeping the accordion style like in the picture?
Thanks in advance,
Max
EDIT: Here's my view as well
def detail(request, foodtype_id):
foodtype = get_object_or_404(foodtype, id=foodtype_id)
recipe = foodtype.recipe_set.values_list('recipe').order_by('recipe').distinct()
context = {
'foodtype': foodtype,
'recipe': recipe,
}
return render(request, 'main/detail.html', context)
Always better to do that logic before it gets to the template. What if you set the ordering on ingredients so then you won't have to order them in the template? Does that work and improve the performance?
class Ingredient(models.Model):
...
class Meta:
ordering = ['ingredient_name']
<div class="panel-group" id="accordion">
{% for recipe in recipes %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapse{{ forloop.counter }}">
{{ recipe }}
</a>
</h4>
</div>
<div id="collapse{{ forloop.counter }}" class="panel-collapse collapse">
<div class="panel-body">
<table class="table table-hover">
{% for ingredient in recipe.ingredient_set.all %}
<tr>
<td>
{{ ingredient.ingredient_name }}
</td>
<td>
{{ ingredient.ingredient_quantity }}
</td>
</tr>
{% endfor %}
<p>{{ recipe.details }}</p>
</table>
</div>
</div>
</div>
{% endfor %}
</div>

Categories