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.
Related
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.
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.
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!
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.
I'm using SQLAlchemy with Flask and the Flask-SQLAlchemy extension. What I'm confused about is how to optimize the updating of a record. As a proof of concept I wrote the following endpoints:
#bp.route('/books', methods=['GET', 'PUT'], endpoint="books_index")
#bp.route('/books/<book_id>', methods=['GET', 'POST'], endpoint="books_show")
def books_index(book_id=None):
book = Book.query.get_or_404(book_id) if book_id else Book()
form = Book.model_form(request.form, book if book_id else None)
verb = "updated" if book_id else "created"
if form.validate_on_submit():
form.populate_obj(book)
db.session.add(book)
db.session.commit()
flash('Book %s successfully' % verb, 'info')
return dict(
books=[] if book_id else Book.query.all(),
book=book,
form=form
)
Everything works as I want it to, but when updating an existing book record, SQLAlchemy runs three queries, they are:
SELECT book.id AS book_id, book.title AS book_title FROM book WHERE book.id = ?
UPDATE book SET title=? WHERE book.id = ?
SELECT book.id AS book_id, book.title AS book_title FROM book WHERE book.id = ?
I understand why SQLAlchemy makes all three queries, but it seems a bit excessive for simply updating one record. Is there a way to optimize the amount of queries happening here?
(as a note, I've got some wrapping on the endpoints to render templates based on a convention which is why you don't see render_template)