I am screwing around with an image uploader build with python and flask. I have it up and running on my VPS here
http://107.170.119.38/
The application displays the first 25 images just fine, but anything after the 25th image does not get displayed anywhere. I am trying to add a button that would allow me to display the next 25 images from. Here is a sample of my code
#app.route('/', methods=['GET', 'POST'])
def get_more_pics():
g.db = connect_db()
cur = g.db.execute('select filename, label from pics order by id desc limit 25')
more_pics = [dict("filename": row[0], "label": row[1]) for row in cur.fetchall()]
g.db.close()
return render_template('upload.html', more_pics=more_pics)
def upload_pic():
if request.method == 'POST':
file = request.files['file']
label = request.form['label']
try:
extension = file.filename.rsplit('.', 1)[1].lower()
except IndexError, e:
abort(404)
if file and check_extension(extension):
# Salt and hash the file contents
filename = md5(file.read() + str(round(time.time() * 1000))).hexdigest() + '.' + extension
file.seek(0) # Move cursor back to beginning so we can write to disk
file.save(os.path.join(app.config['UPLOAD_DIR'], filename))
add_pic(filename, label)
gen_thumbnail(filename)
return redirect(url_for('show_pic', filename=filename))
else:
# Bad file extension
abort(404)
else:
return render_template('upload.html', pics=get_last_pics())
#app.route('/show')
def show_pic():
filename = request.args.get('filename', '')
t = (filename,)
cur = g.db.execute('select label from pics where filename=?', t)
label = cur.fetchone()[0]
return render_template('upload.html', filename=filename, label=label)
# Return a list of the last 25 uploaded images
def get_last_pics():
try:
cur = g.db.execute('select filename, label from pics order by id desc limit 25')
filenames = []
for row in cur.fetchall():
filenames.append({"filename": row[0], "label": row[1] or ''})
return filenames
except:
return []
and in the jinja2 template file:
<input type="button" value="Get More Pics" onclick="{{ more_pics }}">
<ul>
{% for pic in more_pics %}
<li class="thumb">
<img class="thumb" src="{{ pic_path('thumb2_'+pic.filename) }}"></li>
{% endfor %}
</ul>
all in all I can't figure out how to get the get_more_pics() function to display the next 25 images. Any help would be appreciated
You need to add an offset parameter to your SQL:
select filename, label from pics order by id desc limit 25 offset 0
You'll have to pass it into your SQL, as it will be 0 for the first page, then 25, then 50, etc. Probably better to break that 25 out into a parameter rather than hard-coding it, and calculate offset as the product of page and limit.
Also, bear in mind that LIMIT and OFFSET don't necessarily work with all databases, although they do work with SQLite and MySQL, the syntax can vary. E.g. for Oracle: http://www.oracle-base.com/articles/12c/row-limiting-clause-for-top-n-queries-12cr1.php.
An alternative is to use a slightly higher-level approach, such as an ORM.
Related
Im doing the cs50 web course and working in Django.
I need to select md files based on the keyword that the user searches. If the query does not match the name of an the files, the user should instead be taken to a search results page that displays a list of all files that have the query as a substring. For example, if the search query were Py, then Python should appear in the search results assuming such file exists.
Problem is im not sure how. Also we arent using sql.
Here is util.py, code given by course that we are told we dont need to touch:
import re
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
def list_entries():
"""
Returns a list of all names of encyclopedia entries.
"""
_, filenames = default_storage.listdir("entries")
return list(sorted(re.sub(r"\.md$", "", filename)
for filename in filenames if filename.endswith(".md")))
def save_entry(title, content):
"""
Saves an encyclopedia entry, given its title and Markdown
content. If an existing entry with the same title already exists,
it is replaced.
"""
filename = f"entries/{title}.md"
if default_storage.exists(filename):
default_storage.delete(filename)
default_storage.save(filename, ContentFile(content))
def get_entry(title):
"""
Retrieves an encyclopedia entry by its title. If no such
entry exists, the function returns None.
"""
try:
f = default_storage.open(f"entries/{title}.md")
return f.read().decode("utf-8")
except FileNotFoundError:
return None
Here are my views function:
def entry(request, title): #based on the url it shows a md file
try:
content = markdown2.markdown(util.get_entry(title))
except:
return HttpResponseNotFound("Page not found")
return render(request, "encyclopedia/entry.html", {
"entry": content, "title": title.capitalize()
})
def search(request):
keyword = request.GET.get('q')
result = util.get_entry(keyword)
if result: #if the keyword matches a title, return that result
content = markdown2.markdown(util.get_entry(result))
return redirect('entry', result)
else: #here i should return results that match the substring query
#stuck
results = util.get_entry('%keyword%')
return render(request, "encyclopedia/results.html", {
"results": results
})
Search bar:
<form method="get" action="{% url 'search' %}">
{% csrf_token %}
<input type="text" name="q" placeholder="Search Encyclopedia">
</form>
You can achieve this by using indexing in Python.
Here is how I did it.
list = ["Python", "CSS"]
q = "Py"
for i in list:
for j in range(3):
result = str(i[0:j])
if result == q:
print("One entry found: ", i)
Output:
One entry found: Python
I've got a simple flask web-app set up where the user can add and delete tasks from a database. All the entries in the database are displayed in a template, sorted by the type they're assigned. I can't format the output to be at least somewhat readable, though. How do i do that?
The actual database uses different values and whatnot, so not all of them may make sense right now.
This is the function that gets all the entries from my sqlite database:
def get_tsk_by_type(type):
c.execute("SELECT * FROM tasks WHERE type=:type", {'type': type})
result = c.fetchall()
return result
The database:
c.execute("""CREATE TABLE IF NOT EXISTS tasks (
type text,
description text,
amount integer,
id integer
)""")
And here's how i'm returning all the entries that are then displayed in in a template. There is also a function to delete tasks if you input their id.
#app.route('/', methods = ['GET', 'POST'])
def index():
form = DeleteForm()
curr_homework = str(get_tsk_by_type("homework"))
curr_cleaning = str(get_tsk_by_type("cleaning"))
curr_cooking = str(get_tsk_by_type("cooking"))
if form.validate_on_submit():
try:
conn = sqlite3.connect('tasks.db', check_same_thread=False)
c = conn.cursor()
delete = request.form['delete']
if (delete):
remove_tsk(delete)
return redirect('/')
conn.close()
except:
return "Something went wrong while submitting the form"
return render_template('index.html', curr_homework = curr_homwork, curr_cleaning = curr_cleaning, curr_cooking = curr_cooking, form = form)
The relevant parts of my index.html look like this:
{% block content %}
<div>
<p>
<span>Currently registered homework: {{ curr_homework }}</span><br />
<span>Currently registered cleaning tasks: {{ curr_cleaning }}</span><br />
<span>Currently registered cooking tasks {{ curr_cooking }}</span>
</p>
</div>
{% endblock content %}
However, the output i'm getting looks like this:
Currently registered homework: [('homework', 'math', 1, 'df19c0b1-a2128-431274-2e32-3a2f901b1b26')]
Currently registered cleaning tasks: [('cleaning', 'kitchen', 1, 'df19c0b1-aa18-4874-9e32-3a2f901b1b26')]
Currently registered cooking tasks: [('cooking', 'lunch', 1, '0697c139-0299-4c93-88ac-c07d77377796')]
I've tried for-loops and that kinda thing but it only ever returns the first tuple inside the list that get_tsk_by_type() returns. I also tried panda but i couldn't get that to output the way i want it to, either. How do i prettify it to the point that it's easily readable? Without brackets etc.? I later want to display each individual task separately, preferably in divs.
I recommend using a dict cursor so you can access the result elements by name.
You can do that like this (from: https://stackoverflow.com/a/3300514/42346):
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
conn.row_factory = dict_factory
Then you will get this kind of result:
result = c.fetchall()
result
# [{'type':'homework','description':'math',
# 'amount':1,'id':'df19c0b1-a2128-431274-2e32-3a2f901b1b26'}]
Then in your template you can do something like:
{% for homework in curr_homework %}
<div>
<h6>{{ homework['type'] }}</h6>
<div>{{ homework['description'] }}</div>
</div>
{% if not loop.last %}
<hr>
{% endif %}
{% endfor %}
You may benefit from a little bit of re-organization of the database-specific code.
You can do this:
from flask import g
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
def get_db():
if 'db' not in g:
conn = sqlite3.connect('tasks.db', check_same_thread=False)
conn.row_factory = dict_factory
g.db = conn.cursor()
return g.db
And then in your view do this:
db = get_db()
db.execute('your query here')
I have simple html template with link, that starts script, in views that retrieves data to the page,in views file I have two functions: render function def output(request):(it retrieves data to the page) and another function def summoner(): that makes postgres quires in cycle and appends results to the list. Separately each of them work fine, but I have to call second function from render function and retrieve the data to the page, but now when I do that all I am getting is empty list.
enter image description here
template:
<html>
<head>
<title>
Python script
</title>
</head>
<body>
Execute Script <hr>
{% if data %}
{{ data }}
{% endif %}
</body>
</html>
views:
from django.shortcuts import render
import pandas as pd
import psycopg2
import os, glob
conn = psycopg2.connect(host='127.0.0.1', database='db',
user='user', password='pass')
cur = conn.cursor()
def insert_data_as_is(file_name):
cur.execute('truncate table test_inv.start_tbl')
with open(file_name, 'r') as file:
cur.execute("insert into test_inv.start_tbl values {0}".format(file.read()))
conn.commit()
def insert_data_to_be(file_name):
cur.execute('truncate table test_inv.res_calc_ratios_t')
with open(file_name, 'r') as file:
cur.execute('insert into test_inv.res_calc_ratios_t (test_no, test_name, hcode_id, '
'hcode_name, hcode_'
'unit_name,'
' org_id, dor_kod, duch_id, nod_id, date_type_id, metric_type_id, cargo_type_id, val_type_id,'
' unit_id, dt, value, ss, dir_id, kato_id, vids_id) values {0}'.format(file.read()))
conn.commit()
path_start = 'files/csv_s/as_is/'
start_list = []
path_finish = 'files/csv_s/to_be'
finish_list = []
for infile in glob.glob(os.path.join(path_start, '*.*')):
start_list.append(infile)
for infile in glob.glob(os.path.join(path_finish, '*.*')):
finish_list.append(infile)
def summoner():
fun_sql_set = []
fun_query = """select * from test_inv.test_ratios('1','15')"""
for i in range(len(finish_list)):
insert_data_as_is(start_list[i])
insert_data_to_be(finish_list[i])
results = pd.read_sql_query(fun_query, conn)
fun_sql_set.append(results)
return fun_sql_set
def index(request):
return render(request,'calculus/index.html')
def output(request):
data = summoner()
print(data)
return render(request,'calculus/index.html',{'data':data})
The answer is creating absolute file path:
cur_dir = os.path.dirname(os.path.abspath(__file__))
path_start = '{}/static/csv_s/as_is/'.format(cur_dir)
path_finish = '{}/static/csv_s/to_be/'.format(cur_dir)
I have such functions
def main():
''' Demonstrate UDF '''
with sqlite3.connect("User.db") as conn:
conn.row_factory = dict_factory
conn.create_function("date_parse", 1, date_parse)
cur = conn.cursor()
rows = cur.execute("SELECT Status,date_parse(DateEntered) as 'DateEntered', EnteredBy, DateModified, AccountName,SalesRepName FROM data WHERE Status!=''and EnteredBy!='' order by DateEntered asc ").fetchall()
def date_parse(s):
''' Converts a string to a date '''
try:
t = parser.parse(s, parser.parserinfo(dayfirst=True))
return t.strftime('%Y/%m/%d %H:%M')
except:
return None
def dict_factory(cursor, row):
''' Helper for dict row results '''
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
When i use
for row in rows:
print (row)
i get all data i need, ordered in a proper way,buy when i want to display it on my web app , i get exception
#app.route('/Accont/')
def SetUp():
# ...code
return render_template('navbar.html',rows = rows)
i tried to render template without request, directly from jinga2 , but that does not help me. Anybody can help me to display that data in proper order on my web app?
Navbar.html
<thead>
<tr>
<th>Status</th>
<th>DateEntered</th>
<th>EnteredBy</th>
<th>DateModified</th>
<th>AccountName</th>
<th>SalesRepName</th>
</thead>
<tbody>
{% for row in rows %}
<td>{{row[0]}}</td>
<td>{{row[1]}}</td>
<td>{{row[2]}}</td>
<td>{{row[3]}}</td>
<td>{{row[4]}}</td>
<td>{{row[5]}}</td>
{%endfor%}
got this error : sqlite3.OperationalError: user-defined function raised exception
In my GET portion of the Flask function I'm working on I have some very simple code written in Python 3. The data I'm trying to pass in is never displayed on my HTML render.
#app.route("/sellselected", methods=["GET", "POST"])
#login_required
def sellselected(order_num):
if request.method == "POST":
#not done, just have a redirect to index
else:
stock_to_sell = db.execute("SELECT * FROM purchases WHERE order_num = :order_num", order_num=order_num)
#stock_to_sell = ['fish']
return render_template("sellselected.html", stock_to_sell=stock_to_sell, order_num=order_num)
The SQL statement seems to passing in nothing, it's just blank on the HTML render. But as a test I also used 'fish' and it's None/empty too.
Jinja looks like:
{% block main %}
<list>
<ul>{{ stock_to_sell }}</ul>
<ul>{{ order_num }}</ul>
</list>
{% endblock %}
So the body of the page has the order number, but stock_to_sell is always empty.
Your problem is not related to jinja anymore. Your route is wrong, it should be: #app.route("/sellselected/<order_num>", methods=["GET", "POST"]).
Because you pass order_num to sellselected function so you need to declare it on route.
You say you like to pass the param order_num as GET right? Because the code say POST is redirect to index.
So, you pass the param as GET. You need to get it first
current_order = request.args.get('order_num')
Your code should be:
#app.route("/sellselected", methods=["GET", "POST"])
#login_required
def sellselected(order_num):
if request.method == "POST":
# not done, just have a redirect to index
else:
order_num = request.args.get('order_num')
stock_to_sell = db.execute("SELECT * FROM purchases WHERE order_num = :order_num", order_num=order_num)
return render_template("sellselected.html", stock_to_sell=stock_to_sell, order_num=order_num)
Beware if you iterate over an object then it's contents will comeback empty. This is because the result is a generator, and it's values can be accessed only once.
Won't work as gcp_certs has been iterated over:
gcp_certs = connection.execute(query)
for cert in gcp_certs:
print(cert.name)
return render_template('certifications.html',
gcpcerts=gcp_certs,
now=datetime.utcnow(),
cookie=token)
Works:
gcp_certs = connection.execute(query)
return render_template('certifications.html',
gcpcerts=gcp_certs,
now=datetime.utcnow(),
cookie=token)
One solution:
imports copy
gcp_certs = list(connection.execute(query))
sql_select = ''
for cert in copy.copy(gcp_certs):
print(cert.name)
return render_template('certifications.html',
gcpcerts=gcp_certs,
now=datetime.utcnow(),
cookie=token)