inserting data into multiple mySQL tables using flask with one query - python

I have two distinct tables in mySQL :
'Books' ('id', 'title', 'author_id')
and
'Authors' ('id', 'author')
Both id's are auto-increment and the tables have a one to many relationship with the foreign key ('author_id') on Books. My task is to enter a new author and book in one single swoop since data are coming via a POST method to these tables from the same form in HTML.
I have manually tried this on mySQL and it works great:
INSERT INTO authors (author, created_at, updated_at)
VALUES ('Gerald Durrell',NOW(),NOW());
INSERT INTO books (title, rating, created_at, updated_at, author_id)
VALUES ('Birds',5, NOW(),NOW(), LAST_INSERT_ID())
In my Flask app, I have the exact same query but the system gets mad and doesn't allow me to do it.
My models.py
mysql = connectToMySQL('books')
query = 'INSERT INTO authors (author, created_at, updated_at)
VALUES (%(author)s,NOW(),NOW()); INSERT INTO books
(title, created_at, updated_at, author_id) VALUES (% .
(title)s, NOW(),NOW(), LAST_INSERT_ID());'
data = {
'author': request.form['author'],
'title': request.form['title'],
}
mysql.query_db(query,data)
my html:
<form action='/addbook' method='POST'>
<label for='title'>Book Title:</label>
<input type="text" name='title'>
<p>Enter new author:</p>
<input name='author' type="text">
<input type='submit' value='Click to add book'>
</form>
The command works manually when I enter it in mySQL. But it crashes in Flask. I do know that the connection between mySQL and Flask app is good because I successfully entered users who have navigated to this 'Add book page'.
This is the exact error message that pops up in my terminal:
Something went wrong (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO books (title, rating, created_at, updated_at, author_id) VAL' at line 1")
The workaround is to incorporate author into the book table, but then I won't be able to iterate through authors in a different section of the app.

After a bit of struggle, it turns out that Flask does not do multiple queries. So I changed the program to have two different routes.
Route 1 : populate the author table with author fields.
and then return a the id-value which flask does automatically anyway and put it in session.
Then route 2: populate the book table with the same form fields and also the author_id field from session.

Related

How to get the value of html attributes to use it in flask with mysql_db

I am trying to make a ecommerce site using flask and raw mysql i.e. using flask_mysqldb to write my own mysql query. I am trying to use dynamic routing to display data in a single template based on their id to avoid creating multiple templates for different products. But i am stuck as i cannot get the id of the products to pass it as a parameter in the mysql query. How to get the html tags attribute values to pass to the sql query as a parameter??
My route:
#app.route('/products/')
def product_details():
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
productID = request.args.get('id')
print(productID)
cursor.execute('SELECT productCode, productName FROM Products WHERE productCode = %s', (productID, )
)
products = cursor.fetchone()
return render_template('product-details.html', products=products)
My html files
products.html:
<div>
{% for product in products %}
<h1><a href={{url_for('product_details', pk=product.productCode) }} id={{product.productCode}}>{{product.productName}}</a></h1>
<p>Rs. {{product.price}}</p>
{{product.productCode}}
{% endfor %}
product-details.html:
<h1 id={{products.productCode}}>{{products.productName}}</h1>
The problem is caused by a mismatch between the query parameters you are sending to, and expecting in product_details.
You are sending pk in your url_for call but are looking for id in productID = request.args.get('id').
The solution is to look for pk in product_details: productID = request.args.get('pk').
Note that anything information outside the url_for call is not visible to Flask i.e. Flask does not have access to the <a> tag's id attribute.

How to create working like button in flask and sqlite3?

I am trying to create a social network using flask and sqlite3. I had created almost all the main things like post, edit post, delete post. But I also want to add the like button for every post. I had created like number(How many like this post got) and like button and it is working fine but If you like my post for the first time and again login your account then you can again like that post which you had already liked by your account.
I had an idea to save who liked my post but i don't know how to implement that exact code in my webapp.I am using sqlite3 so I am not finding any solution for this issue. I had found exactly same question for mysql or any other database but I had not for sqlite3.
Ok here is my idea,
I had already mentioned that I am using sqlite3 so I had created table like this:
conn=sqlite3.connect('data/detailpost.db')
c=conn.cursor()
c.execute("""CREATE TABLE postdetail(
name text,
address text,
post text,
post_date text,
secretcode text,
mainname text,
likes integer,
likers text)""")
conn.commit()
conn.close()
and If someone click like button(In template) then that will come in this part of back-end where I am getting liker mainname and oid number of that post.And this is the same back-end code which increase the like of the post. Like this
#app.route('/social/post/like',methods=['POST'])
def likedofsocial():
if request.method=='POST':
oid=request.form['oid']
mainusernameofliker=request.form['username']
conn=sqlite3.connect('data/detailpost.db')
c=conn.cursor()
c.execute("SELECT *,oid FROM postdetail WHERE oid="+oid)
alldata=c.fetchall()
for rec in alldata:
c.execute("""UPDATE postdetail SET
name=:name,
address=:address,
post=:post,
post_date=:datee,
secretcode=:secret,
mainname=:mainname,
likes=:likes
likers=:likers
WHERE oid=:num""",
{
'name':rec[0],
'address':rec[1],
'post':rec[2],
'datee':rec[3],
'secret':rec[4],
'mainname':rec[5],
'likes':(int(rec[6]+1)),
'likers':mainusernameofliker,
'num':oid,
})
conn.commit()
conn.close()
.....
Here in this above part I don't know how to save multiple mainusernameofliker inside likers beacause there will not be only one person who likes my posts.
Now I have only idea for front-end I had not tried any of the code which is mentioned below!! I just think following code could works fine
"For front-end check that likers data and
(% if (your mainusername is in that likers data ) %)
then show already liked(Liked) or disable like button
(% else (your username is not in that likers data) %)
then that show like or make clickable like button".
(% endif %)
Probably, I could get help as soon as possible and any help will be appreciated.
I think your main question is that how to store the multiple users who would like the post. It is very simple, you just need to create one more table maybe called postlikers. This table will reference primary keys of user and postdetail. Basically a many-to-many relationship between user and postdetail tables.
I will show you an example that how you can define the tables below:
import sqlite3
conn=sqlite3.connect('detailpost.db')
c=conn.cursor()
c.execute("""CREATE TABLE IF NOT EXISTS user(
id INTEGER PRIMARY KEY,
name text)
""")
c.execute("""CREATE TABLE IF NOT EXISTS postdetail(
id INTEGER PRIMARY KEY,
name text,
address text,
post text,
post_date text,
secretcode text,
mainname text,
likes integer DEFAULT 0
)""")
c.execute("""CREATE TABLE IF NOT EXISTS postlikers(
id INTEGER PRIMARY KEY,
post_id INTEGER,
user_id INTEGER,
FOREIGN KEY(post_id) REFERENCES postdetail(id),
FOREIGN KEY(user_id) REFERENCES user(id)
)
""")
conn.commit()
conn.close()
# Add data
user_data = [('Carl'), ('Mel'), ('Bobby')]
post_data = [('first_post', 'whajjne', 'I love everything', 'dfweffwwffefe', 'secret', 'Carl', 0),
('second_post', 'whajjne', 'I love everything', 'dfweffwwffefe', 'secret', 'Mel', 0)]
conn=sqlite3.connect('detailpost.db')
c=conn.cursor()
for user in user_data:
c.execute('INSERT INTO user (name) VALUES (?);', (user,))
for post in post_data:
c.execute('INSERT INTO postdetail (name, address, post, post_date, secretcode, mainname,likes ) VALUES (?,?,?,?,?,?, ?);', post)
conn.commit()
conn.close()

Using Foreign Keys in Django Template w/django-simple-history

I'm trying to display values associated with a foreign key while rendering a Django Template. I've emulated other answers on this site to no avail.
I'm using the package django-simple-history to track changes to all the records in my database's primary table. This table has a foreign key named history_user_id which corresponds to the id in the django table auth_user.
According to this example (Display foreign key value in django template) I should be able to display the usernames of users who have amended the database by using the following code:
<ul>{% for item in history %}
<li>{{item.history_user_id.username}}</li>
</ul>{% endfor %}
where history is defined in my views.py as
def view_history(request,pk):
project = Project.objects.get(pk=pk)
history = project.history.all()
return render(
request,
"projects/view_history.html",
{
"project": project,
"history": history,
}
)
The template I create can interpret item.history_user_id, and I can manually look into the table auth_user to the corresponding username, but when I try to use the template to render the username I get a blank instead. Am I missing a step?
If I see it correctly item.history_user_id is the id of the user who did the change. This is an integer, which of course does not have any property called user. If a django template hits a non-existant variable it will just leave it blank (and not raise an error). Therefore you a seeing nothing in your template.
So change your code to get the user rather than the user_id
<ul>{% for item in history %}
<li>{{item.history_user}}</li>
</ul>{% endfor %}
This is described in the docs.

How to implement a search function using a raw SQL query

I am creating an app guided by CS50's web series, which requires me to ONLY use raw SQL queries not ORM.
I am trying to make a search function where a user can look up the list of books that's stored in the database. I want to able them to query ISBN, title, author column in the table called 'books'
Currently, it does shoot a 'GET' request with no problem but it is not returning any data and I think the problem is at the SQL line I've scripted.
Here's the route:
#app.route("/", methods=['GET','POST'])
def index():
# search function for books
if request.method == "GET":
searchQuery = request.form.get("searchQuery")
# return value from the search
searchResult = db.execute("SELECT isbn, author, title FROM books WHERE isbn LIKE '%"+searchQuery+"%' OR author LIKE '%"+searchQuery+"%' OR title LIKE '%"+searchQuery+"%'").fetchall()
# add search result to the list
session["books"] = []
# add the each result to the list
for i in searchResult:
session["books"].append(i)
return render_template("index.html", books=session["books"])
return render_template("index.html")
and here's my template.
<form method="GET">
<input type="text" name="searchQuery" class="searchTerm" placeholder="What are you looking for?">
<button type="submit" class="searchButton">submit</button>
</form>
<div>
<h3>
{% for book in books %}
{{ book }}
{% endfor %}
</h3>
</div>
Can anyone spot the problem, please? Please note that I am supposed to utilize the raw SQL queries and session.
I created a github with full solution for you :)
https://github.com/researcher2/stackoverflow_57120430
A couple of things:
Avoiding SQL Injection
I recommend using bindings when doing raw sql statements, my code reflects that. I spent ages trying to get this working with your statement before stumbling upon this:
Python SQLite parameter substitution with wildcards in LIKE
Basically you can't put bindings inside the LIKE '%?%' because the quotes cause the replacement token to be ignored.
Instead you just have to do LIKE ? and build the replacement manually.
Using Session
All session information is JSON serialized and then sent to the client. In this case the row records weren't JSON serializable. This showed up as an error for me:
TypeError: Object of type 'RowProxy' is not JSON serializable
I probably wouldn't use the session here as there is no need for the client to be aware of this, as you're going to build them a nice html page with the information anyway. Just use a python dictionary and pass it to the template engine. My code did use the session because this is what you started with.
In case github ever goes down:
from flask import request, render_template, session
from app import app, db
#app.route("/", methods=['GET','POST'])
def index():
if request.method == "POST":
searchQuery = request.form.get("searchQuery")
print(searchQuery)
# Avoid SQL Injection Using Bindings
sql = "SELECT isbn, author, title \
FROM book \
WHERE isbn LIKE :x \
OR author LIKE :y \
OR title LIKE :z"
# I spent an hour wondering why I couldnt put the bindings inside the wildcard string...
# https://stackoverflow.com/questions/3105249/python-sqlite-parameter-substitution-with-wildcards-in-like
matchString = "%{}%".format(searchQuery)
stmt = db.text(sql).bindparams(x=matchString, y=matchString, z=matchString)
results = db.session.execute(stmt).fetchall()
print(results)
session["books"] = []
for row in results:
# A row is not JSON serializable so we pull out the pieces
book = dict()
book["isbn"] = row[0]
book["author"] = row[1]
book["title"] = row[2]
session["books"].append(book)
return render_template("index.html", searchedFor=searchQuery, books=session["books"])
return render_template("index.html")
Thanks for the chosen answer.
The problem was solved with one simple change.
I needed to swap 'LIKE' to 'ILIKE' in the SQL query line.
searchResult = db.execute(
"""
SELECT isbn, author, title
FROM books
WHERE isbn ILIKE :search OR author ILIKE :search OR title ILIKE :search
""",
{"search": "%"+searchQuery+"%"}
).fetchall()
This may not be an elegant solution but it did solve a temporary problem. Again, kudos for the guy who answered and create a github repo for everyone else who would encounter the problem while doing CS50's web series!

Get Data from PostgreSQL to Django and display it in html

I have a table in PostgreSQL database that contains two columns ID and Name. I am using django framework to get the data from the database and I want to display the data into html page. The problem is that the retrieved data is without columns name. It looks like this, and in order to use it in the html page it has to have a key for each tuple.
[(2, 'abc'), (3, 'subhi')]
I've tried to get the columns name but they are only table columns without data. Below is my Code:
models.py
import psycopg2
import pprint
def main():
conn_string = "host='localhost' dbname='music' user='postgres' password='subhi123'"
column_names = []
data_rows = []
with psycopg2.connect(conn_string) as connection:
with connection.cursor() as cursor:
cursor.execute("select id, name from music")
column_names = [desc[0] for desc in cursor.description]
for row in cursor:
data_rows.append(row)
records = cursor.fetchall()
# print out the records using pretty print
# note that the NAMES of the columns are not shown, instead just indexes.
# for most people this isn't very useful so we'll show you how to return
# columns as a dictionary (hash) in the next example.
pprint.pprint(records)
print (type(records))
print("Column names: {}\n".format(column_names))
if __name__ == "__main__":
main()
views.py
from django.http import Http404
from django.shortcuts import render
from .models import main
def index (request):
all_albums = main()
return render(request,'music/index.html',{ 'all_albums' :all_albums})
index.html
{% if all_albums %}
<ul>
{% for album in all_albums %}
<li> {{ album }}</li>
{% endfor %}
</ul>
{% else %}
<h3> You don't have any data</h3>
{% endif %}
and the settings.py for which shows the PostgreSQL connection:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'music',
'USER': 'postgres',
'PASSWORD': 'subhi123',
'HOST': 'localhost',
'PORT': '5432',
}
}
django use ORM(Object-related maping) for db. It's called Model in django.
It means, you should make Model class for your db table and scheme, and django automatically make sql for the model - so you don't do sql job untill you need to.
Below is example of models. (example in docs)
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
Django create database table like...
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
It's hard to explain all in the answer, so you must look django official docs. After following tutorials (it's well written!), you can understand django - so at least, follow tutorials.
Here's some sites for help.
django tutorials - Highly recommend
django Models docs
django girls tutorial - pretty good tutorials, support many languages.
Introducing django ORM
You are supposed to use Django module i.e models.py (inherites by django.db.models.Model) this module creates data fields based on your choice and migrate them to the database (in your case Postgresql) when you hit enter by typing in "python manage.py migrate" without string. Possibly due to the fact of this you ain't attaining anything from Database.

Categories

Resources