Pass value to new page in Flask when button is clicked - python

Basically I have a for loop to generate N buttons. Each button has a name and an id to recognize the activity. When one of these buttons is clicked, a new HTML page is opened in order to display the info of that activity. I could open the page but I need to pass the value of the button or I won't know which activity was clicked. There must be a pattern to do this.
You can check the code here:
<div class="activities">
<h2>Activities</h2>
{% set i = [0] %}
{% for d in data %}
<button class="btn btn-info" style="margin: 10px;" value="{{ indexs[i[0]] }}">{{ d }}</button>
{% if i.append(i.pop() + 1) %}{% endif %}
{% endfor %}
</div>
#views.route('/activity')
def activity():
return render_template("activity.html")

need to pass the value of the button
Simplest way I can think of is using URL query parameters, simplified example:
HTML snippet:
<div>
<button>first</button>
<button>second</button>
<button>third</button>
</div>
Flask:
from flask import request
...
#views.route('/activity')
def activity():
return "You clicked " + request.args["buttonid"] + "button"
must be a pattern to do this
FORMs (HTML tag <form>) are currently in use for this purpose.

Related

How do I capture data with a html button without using a form?

I'm using Flask and am sending reports and displaying them in html. I want each report entry to have a corresponding delete button that when clicked, will trigger a python function to delete that report from a database I have. I am not struggling with the logic of how to delete the report from within that python function, I am trying to figure out how to send the information to python. I know this can be done with a form where the user enters the name of what they want deleted but I would prefer that the button just correspond to the report and not have to have the user type out which report they want to delete.
{% if report_data %} {% for report in report_data %}
<div class="report">
<dl>
<h1><b>{{ report['name'] }}</b></h1>
<dt><b>Date of Report:</b> {{ report['time'] }}</dt>
<dt><b>Overall Score:</b> {{ report['overall'] }}</dt>
<dt><b>Cleanliness Score:</b> {{ report['cleanliness'] }}</dt>
<dt><b>Availability Score:</b> {{ report['avail'] }}</dt>
<dt><b>Location Score:</b> {{ report['location'] }}</dt>
</dl>
<button method="post"> <a href='/delete_report'>Delete Report</a></button>
</div>
{% endfor %} {% elif error_message %}
My main.py has the following function:
#app.route('/delete_report', methods = ["GET", "POST"])
def delete_report():
#remove report from database
which is being reached but how do I send say report['name'] to it? Is that something that can be done without having to create a form?

Django Pagination Where User Selects/Types Desired Page

I am looking to create a pagination like either of the following examples:
https://www.martindale.com/search/attorneys/?term=Bankruptcy%20near%20New%20York%2C%20USA (top right of search results)
https://www.adidas.com/us/superstar?start=0 (scroll to the bottom)
Where a user could either type in the page they want to go to and press enter or have a dropdown selection showing all the possible pages to choose from and then once selecting, automatically go to that page (the dropdown probably seems better to prevent users from typing a random set of numbers of characters into the input field). Otherwise the input field where a user types will need forms.py validation.
When attempting to do this, I ran into an issue not being able to use a template tag within forms.py. I was able to make a template for loop dropdown menu that loops through all the possible page numbers. But this for loop is not using forms.py which I don't think is desirable (because no validation). My pagination code is currently separate from forms.py or the form tag HTML.
I would still like to keep the current next, previous, first, and last links to appease those types of users. But if there are lots of pages of results, I want to give users freedom to quickly go to a desired page they want without clicking next or previous a bunch of times to get there.
How would I get the desired result similar to both of the links above?
Current HTML Pagination:
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
next
last »
{% endif %}
</span>
</div>
App_tags.py:
from urllib.parse import urlencode
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def url_replace(context, next_page):
query = context['request'].GET.copy().urlencode()
if '&page=' in query:
url = query.rpartition('&page=')[0] # equivalent to .split('page='), except more efficient
elif 'page=' in query:
url = query.rpartition('page=')[0]
else:
url = query
return f'{url}&page={next_page}'
Attempt Template For Loop Showing All Possible Pages:
<select class="form-control col-md-1" autocomplete="off">
{% for i in "x"|rjust:page_obj.paginator.num_pages %}
<option> {{ forloop.counter }} </option>
{% endfor %}
</select>
Attempt Forms.py:
class CourseForm(forms.Form):
page_number = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control col-md-1', 'autocomplete':'off', 'id':'title_contains', 'type':'search', 'placeholder': {{ page_obj.number }} }), required=False)
def __init__(self, *args, **kwargs):
super(CourseForm, self).__init__(*args, **kwargs)
self.fields['page_number'].widget.attrs['placeholder'] = {{ page_obj.number }}

How do I use modal to render data dynamically with Python Flask and HTML

I have looked online and found that I can use macros to render different content to my modal. Yet there is something missing in my code that prevents it from getting updated and I'm not sure of what exactly.
#app.route("/candidates")
def candidate_fun():
mapping={
"001":"Bangalore",
"002":"Delhi",
"003":"Chennai",
"004": "Mumbai",
"005":"Kolkata",
"006":"Hyderabad"
}
myclient=pymongo.MongoClient(uri)
mydb = myclient["codefundo"]
mycol=mydb['cand_reg']
result=[]
for x in mycol.find():
obj=NewsSearch()
# import pdb; pdb.set_trace()
t=dict(x)
t['a'],t['b'],t['c']=obj.news_candidate(search_term=str(x["First Name"].lower()+" "+x["Last Name"].lower()+ " election"))
# t['News']=str(a+". "+b+". "+c)
result.append(t)
# result.append({'News':obj.news_candidate(search_term=str(result["First Name"]+" "+result["Last Name"]+" election"))})
return flask.render_template("candidate.html",result=result,mapping=mapping)
While the python code isn't of significance, I have provided it to show that I am passing a list result of type dict.
HTML Jinja
<!--MODAL PART-->
{% macro render_modal(a,b,c) -%}
div class="modal-body">
<p>{{a}}</p>
<p>{{b}}</p>
<p>{{c}}</p>
</div>
{%- endmacro%}
<!-- Jinja to make a call -->
{% for key in result %}
<div class="col-6">
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#exampleModalLong">Info</button>
{{render_modal(key['a'],key['b'],key['c'])}}
<!-- Just to test if the value sent is received {{key['a']}} -->
</div>
{% endfor %}
It returns the same data over the modal box for all entries being passed. I want for it to show me the specific values - key[a], key[b], key[c] for every new key in the iteration.
Similar question here: Passing a row ID in a table to a modal in flask (python)
You need to set an id on your modal and refer to that specific id in your button to identify a specific modal. In order for each modal to render different inputs, you need to set different ids for each button-modal group.
In your case, add a unique identifier for each key object in addition to your data entries, e.g.
# in the routes
t['id'] = # unique id identifying the modal
t['a'] = ...
t['b'] = ...
# etc.
Then in the for-loop that calls the modal macro, include the unique id in some way on the data-target attribute of your button:
{% for key in result %}
<div class="col-6">
<button type="button" data-target="#exampleModalLong{{ key.id }}" class="btn btn-info" data-toggle="modal">
Info
</button>
{{ render_modal(key) }}
</div>
{% endfor %}
Use the exact same name you used for data-target for the id of your modal div:
{% macro render_modal(key) -%}
<div class="modal-body" id="exampleModalLong{{ key.id }}">
<p>{{ key.a }}</p>
<p>{{ key.b }}</p>
<p>{{ key.c }}</p>
</div>
{%- endmacro%}
Note that this will inject a lot of HTML into your code if you have a lot of entries in your for-loop. As mentioned in the Stack Overflow post linked at the top, you can consider defining just one modal, and use AJAX to change the modal content.

How can I generate buttons or fields (forms) in Flask according to a list of strings?

Well, suppose we have a list of strings (objects with a toString() method respectively) and a jinja2 template that shall have selection forms (buttons or something alike) that agree in number and label to the list. This list may alter during the session. So far, I tried to work with submit buttons and radio buttons. Problems are: submit buttons vary in size because of different string length and I dislike that radio buttons force the user to first make a choice and then submit it.
The jinja2 markup looks like this:
<form method = 'post' action= "{{ url_for('add_dialogue_turn') }}">
{% if questions %}
{% for q in questions %}
<input type="radio" name="question" value={{q}}> {{q}} <br>
{% endfor %}
{% endif %}
<input type="submit" /><br /><br />
</form>
The flask function looks like this:
#app.route("/turn", methods=['POST'])
def add_dialogue_turn():
label = request.form["question"]
print(label)
return render_template("sometemplate.html", questions=aListOfQuestions, answers = aListOfAnswers)
Can I make the radio buttons submit the value directly after ticking off the circle? Or can I define some field that returns the string when clicking on it?
Thank you for your help in advance!
This is a Front end problem. You would need either JavaScript to submit your form when a button/radio is ticked. And it also depends on how you submit your form but if you want just the data to be passed into the server without page reloading, I'd suggest Ajax. And if you just want to pass the input value into the server, you do not have to use post.
A simple example would be,
-HTML
<input type="radio" name="question" value={{q}} id="{{something_unique_for_each_iterable}}" onclick="submitFunction(this)">
-JavaScript
function submitFunction(event){
id_of_radio_ticked = '#' + event.id;
$.ajax({
url: "{{url_for('to_your_flask_view_function')}}",
type: 'GET',
data: {'radio_value':$(id_of_radio_ticked).val()},
success: function(resp){
alert('do something with returned data')
}
});
}
I found another solution within the jinja template:
<nav>
<ul>
<div class="sideMenuL">
<form method = 'post' action= "{{ url_for('add_dialogue_turn') }}">
{% if questions %}
{% for q in questions %}
{% autoescape false %}
<input type="submit" name="question" value="{{q}}"><br>
{% endautoescape %}
{% endfor %}
{% endif %}
</form>
</div>
</ul>
</nav>

Flask & Bootstrap Multiple collapsibles, but they each only open the first

I have an HTML page displaying a database populated by emails. I have them displayed in a collapsible, and for each post the timestamp of it is what toggles it and the innards are the email itself. The HTML page is structured like this:
{% extends "base.html" %}
{% block content %}
{% for email in emails %}
<div><button class="btn" data-toggle="collapse" data-target="#demo">{{ email.timestamp }}</button>
<div id="demo" class="collapse">
{{ email.body }}
</div>
{% endfor %}
{% endblock %}
relevant portion of views.py
#app.route('/cruz')
def cruz():
u = Politician.query.get(1)
emails = u.emails.all()
return render_template('cruz.html',title='Ted Cruz',emails=emails)
which produces a webpage that looks like this: http://imgur.com/noqC40E
The problem is that no matter which of those timestamps I click, only the first collapsible opens and closes. I've tried a number of things to fix it, mostly messing around with the HTML page and the for blocks and where I place the {{ email.body }}, but nothing I do seems to work. Can anyone see where this is going wrong?
You are generating the same id attribute for your div each time:
<div id="demo" class="collapse">
You almost certainly need to generate unique ids. You could generate unique ids by adding the loop index perhaps:
<div id="demo-{{loop.index}}" class="collapse">

Categories