save dictionary to database from html template in flask - python

I will try my best to be as concise as possible.
In my back end, I have a list of dictionaries saved to a variable. Each dictionary represents a post from reddit and includes the score, url, and title.
Respectively, the template will loop through this list and then return the values of each of these keys to the user like so:
<table>
<tr>
{% for x in data[0:5] %}
<td>
{{ x['score'] }}
{{ x['title'] }}
<br>
<a href='/add_to_favorites'> Add To Favorites </a>
</td>
{% endfor %}
</tr>
</table>
As you can see, there's an tag which is linked to a function on my utils.py that is attempting to save the respective dictionary to the database (I have a model that represents the url, title, and score).
I feel as though my template is not representing the dictionary in the correct way, for my link to include the html as when it is pressed I receive a 404 error (though I have this route already defined in views.py - '/add_to_favorites' which calls my 'save_post' function).
def save_post():
data = get_info()
for post in data:
fav= Favorite(title=post.get('title'), url=post.get('url'), score=post.get('score'), user_id=current_user.id)
db.session.add(fav)
db.session.commit()
return redirect(url_for('favorites'))
and:
#app.route('/add_to_favorites')
#login_required
def add_to_favorites():
return save_post()
Am i going about this the wrong way? How can i make sure that the link/button is associated with only the html of the that it is included in?
Just need some guidance into the right direction here, not necessarily the code to fix it. Thank you

Related

Python & Flask HTML - Making n number of textboxes based on variable

I have a flask webpage I am trying to create. When users register for an account, they declare the number of agricultural fields they have. This is saved in a MySQL database. From this, I need users to add information on another input page. This input page should have "n" number of textboxes based on the number of fields they have.
Is there anyway to create "n" number of textboxes in HTML? I can read in the number of fields from their account but cannot find a way to create a variable number of textboxes within HTML.
I do not need this to be dynamic (the user add or remove) this needs to be a FIXED amount of textboxes.
I'm going to assume that because of flask you are using jina2 as your rendering engine, let's call the number of fields that need to be created n, you can just use the loop structure
{% for i in range(n) %}
<input name="field-{{i}}" />
{% endfor %}
you can get more info at https://jinja.palletsprojects.com/en/3.0.x/templates/#for
if you need more complex forms, I would recommend using WTForms.
Found a solution, it is a little ugly but should work.
in your html:
<table>
{% for x in range(fields | int) %}
<tr>
<td>
<input type="text" value="{{ x }}">
</td>
</tr>
{% endfor %}
</table>
in python:
def irrigation_input():
...... non important stuff here.....
fields=account_info_irr[9]
int(float(fields))
return render_template('irrigationinput.html',fields=fields)

Display documents in a table from MongoDB with Jinja2 HTML

I currently try to figure out how to display multiple documents from my MongoDB collection "one by one". My recent code to fetch the data looks like this:
#app.route('/shops', methods=['GET', 'POST'])
#login_required
def certified_shops():
shopdata = col.find({ "go": True})
return render_template("tables.html", shopdata=shopdata)
My HTML Jinja injection like this:
<tr>
<td>
{% for data in shopdata: %}
{{ data.url_name }}
{% endfor %}
</td>
</tr>
The code itself works fine - but literally all URLs are displayed in one row of course.
Right now I can't find a good way to handle this.
I just tried to displaying the data with a different loop or some "in range", but this doesn't worked so far. Everything i found here or in the documentation for mongodb and jinja didn't went well for me.
Might there be an easy trick with my html handling or the db query to display one url for each td/tr easy and flexible ?
Since the collection will grow, the list should automatically expand in my web application.
Would love to here some ideas since I've been racking my brain for days now without any good solutions and I've been thinking that there must be an easy way and I just can't see it because it's that simple.
Did I understand your question well? This will create a new row for every url_name:
{% for data in shopdata %}
<tr>
<td>
{{ data.url_name }}
</td>
</tr>
{% endfor %}
Notice. No colon (:) used in the jinja for loop after 'shopdata'.

Accessing model data in Django template

I am using the below lines to pass data to my template index.html, model1 being the class in my models.py.
data = model1.objects.all()
return TemplateResponse(request, 'index.html', {'data': data})
I am able to access data on the front end by using a for loop as shown below
{% for x in data %}
<h3>{{x.name}}</h3>
<h4>{{x.department}}</h4>
{% endfor %}
Since there are mutliple objects in this data, my question is if I want to access only the department of particular object with certain name, how can I do that?
For example here I am using a for loop, consider there are two objects in the data. Then the output would be
name1
department1
name2
department2
So now if I need to access only name2 without any loop, how can i do that?
Updating the question: I am updating this question with html, so that the question looks clear.
table id="example" class="table table-striped" cellspacing="1" width="100%">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Department</th>
<th>View/Edit</th>
</tr>
</thead>
<tbody>
{% for x in data %}
<tr>
<td>{{x.id}}</td>
<td>{{x.name}}</td>
<td>{{x.department}}</td>
<td>View</td>
<button type="button" class="btn-sm btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
view</button></td>
</tr>
{% endfor %}
</tbody>
This is how my html looks, I am using data-table here. So all the data is captured in a table automatically. As you can see every row has a view button that I would implement. Once the user clicks the view button, I should pop up a modal dialog to show all the other details like {{x.dateJoined}} etc which I don't show in the table. But if I use a separate view to pop this dialog, I should send a request to the view from my template saying which row(with some ID) the user has clicked. How can i achieve that? How can I bind the view button with respective rows here?
You need to write custom template tag which will take the queryset and filtering parameters and returns you appropriate object, you can use simple_tag:
myapp/templatetags/myapp_tags.py
from django import template
register = template.Library()
#register.simple_tag
def get_model1_object(queryset, **filters):
if not filters:
raise template.TemplateSyntaxError('`get_model1_object` tag requires filters.')
retrun queryset.filter(**filters).first()
Then in template:
{% load get_model1_object from myapp_tags %}
{% get_model1_object data name='blah' as obj %}
Note: Your filtering criteria might yield multiple results but in get_model1_object i am only returning the first object assuming your criteria will be strict, change it according to your needs.
The first thing to understand is that template rendering happens on the server, before the user sees anything in their browser. The template is not sent to the user, only the HTML that the template generated. When the user is interacting with the page, the original template is not involved. So, you have two choices here:
You can render the data for all the objects in hidden div's on your page, then use javascript with something like a jquery dialog to display them on demand. This is only realistic if you have very few records.
You can create a second view with its own template, which renders just the HTML for the modal dialog contents. You could then, again using javascript/jquery, make an AJAX request to load the contents of the dialog that you need when you need it. In your first view template, the list of departments, include the url of the object you want to fetch, eg:
{% for x in data %}
<tr>
<td>{{x.name}}</td>
<td><a class="deptlink" href="{% url 'dept_detail' x.pk %}">
{{ x.department }}</a></td>
</tr>
{% endfor %}
Where dept_detail is the name of the urls.py entry for your view that supplies the contents of the dialog.
Then in your javascript, hook the a tag so it opens your dialog instead of leaving the page:
$('.deptlink').click(function (event) {
event.preventDefault();
# then the code after this is up to you, but something that'll
# load the url into your dialog and open it.
$('yourdialog').load($(event.target).attr('href'));
Since you say you are an intermediate in javascript I won't get into the details of implementation, but basically, the moral of the story is that the output of django is going to be an HTML page. Anything that you need to have on the client side either has to be in that page, or you will have to make another request to the server to get it... Either way, you'll need some javascript to do this, since you want a modal dialog with client side interaction.

Pass data from textbox to wtform & display in browser

EDITED. My original question wasn't clear enough. I want to allow a user to pass values into a TextField in wtforms, then the value they entered appears after they add it. This would allow the user to pass multiple values before then hitting a final "Sumbit" button on all the values that were originally entered.
I found this question for doing something with the entered text, which is what I tried.
My Python code:
from flask import Flask, request, render_template, redirect
from wtforms import TextField, Form, SubmitField
def redirect_url(default='index'):
return request.args.get('next') or \
request.referrer or \
url_for(default)
class RegionForm(Form):
field = TextField('Region')
Submit = SubmitField('AddRegion')
fieldList = []
def main():
app = Flask(__name__)
#app.route('/region/', methods=['GET'])
def region():
form = RegionForm(request.form)
return render_template("region.html",
form=form)
#app.route('/add/', methods=['POST'])
def add():
request.form['fieldList'].append(request.form['field'])
return redirect(redirect_url())
app.run(debug=True)
My html code:
<form action="/add/" method="Post">
{% for field in form %}
<tr>
<th>{{ field.label }}</th>
<td>{{ field }}</td>
</tr>
{% endfor %}
</form>
{% for item in form.fieldList %}
{{ item }}
{% endfor %}
But after I enter the text and click the "AddRegion" button, I get the following error: The browser (or proxy) sent a request that this server could not understand. However, if I comment out the line request.form['fieldList'].append(request.form['field']), then the redirect happens, but the text hasn't been added to the hidden list on the form. How do I both add the text to the list and redirect back to the original page, so the user can add more text? It seems like there must be an error with this line only, because the rest works fine.
How can I allow a user to dynamically add text to a field, then have that field display in the browser?
Then once the complete region fields have been added, I want to be able to retrieve that list to process in a separate function later.
Part One
So after looking at your code, I think I have found your problem. Flask is very particular about its app routes.
The app route that you have in your flask is:
#app.route('/add', methods=['POST'])
However, your current action on your form which is:
<form action="/add/" method="Post">
In flask /add and /add/ are actually two different web-routes. Therefore, the #app.route is not being triggered. If you change your code to:
`<form action="/add" method="post">`
You should be good to go from here.
Part Two
I think I may have an additional issue. So within your HTML right now, you actually close your </form> tag before looping through your items in the fieldList.
</form>
{% for item in form.fieldList %}
{{ item }}
{% endfor %}
Try:
{% for item in form.fieldList %}
{{ item }}
{% endfor %}
</form>
What I believe to be happening is that your form inputs are not actually being placed inside of the form so when you try to access them you are getting a KeyError.
I second what Cody Myers said. However there's a simple way to guarantee correct routes even if you later change them: in your form use action="{{ url_for('region') }}" and Flask will automatically substitute the correct route for the given function name.

Deleting elements from database in Flask

I want to make sure I'm following best practices here. I have a table of data that pulls from the database and in the last column I have links to edit or delete that row. I feel like I've always been told to never modify data on the server with a GET request. How would I handle deleting this data row with anything other than a GET request?
Here's the code for the data table:
<table class="table table-hover notifications">
<thead><tr><th>Search Parameter</th><th>Subreddits</th><th>Actions</th></thead>
{% for notification in notifications %}
<tr>
<td>{{ notification.q }}</td>
<td>{% for s in notification.subreddits %} {{ s.r }}<br> {% endfor %}</td>
<td>Edit | Delete</td>
</tr>
{% endfor %}
</table>
I guess I'm not sure how to approach building the url for Delete. I can build a delete method and pass the id of the element I want to delete (ex: /delete/1), but is that not modifying data with a GET request then?
You can create a form that makes a POST request on submit, connected to a view that deletes the object when request.method is POST (passing the object ID in the URL, as you said).
I am not a Flask expert, but taking this code as example, your view should look something like:
#app.route('/delete/<int:id>', methods=['POST'])
def remove(id):
object = Object.query.get_or_404(id)
delete(object)
return redirect(url_for('index'))
And your form like:
<form method="post" action="{{ url_for('remove', id=object.id) }}">
<button type="submit">Delete</button>
</form>
The form action attribute forces the form to submit its information to the given URL/view.

Categories

Resources