How can I print out the schema of all tables using sqlalchemy?
This is how I do it using SQLite3: I run an SQL to print out the schema of all tables in the database:
import sqlite3
conn = sqlite3.connect("example.db")
cur = conn.cursor()
rs = cur.execute(
"""
SELECT name, sql FROM sqlite_master
WHERE type='table'
ORDER BY name;
""")
for name, schema, *args in rs:
print(name)
print(schema)
print()
With output that can look like this:
albums
CREATE TABLE "albums"
(
[AlbumId] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
[Title] NVARCHAR(160) NOT NULL,
[ArtistId] INTEGER NOT NULL,
FOREIGN KEY ([ArtistId]) REFERENCES "artists" ([ArtistId])
ON DELETE NO ACTION ON UPDATE NO ACTION
)
artists
CREATE TABLE "artists"
(
[ArtistId] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
[Name] NVARCHAR(120)
)
Is there a way to do it with pure sqlalchemy api calls, something better than this?
metadata = sqlalchemy.MetaData()
metadata.reflect(engine)
insp = sqlalchemy.inspect(engine)
for table_name in metadata.tables:
print(table_name)
for column in insp.get_columns(table_name):
for name,value in column.items():
print(' ', end='')
if value:
field = name if value in [True, 'auto'] else value
print(field, end=' ')
print()
Output:
albums
AlbumId INTEGER autoincrement primary_key
Title NVARCHAR(160) autoincrement
ArtistId INTEGER autoincrement
artists
ArtistId INTEGER autoincrement primary_key
Name NVARCHAR(120) nullable autoincrement
This bit in the SQLAlchemy docs may help: they suggest doing this:
def dump(sql, *multiparams, **params):
print(sql.compile(dialect=engine.dialect))
engine = create_engine('postgresql://', strategy='mock', executor=dump)
metadata.create_all(engine, checkfirst=False)
Related
Below you can see the tables in my sqlite3 database:
songs
files
tags
playlists
These are the relationships between the tables:
One To One: Songs and files
Many To Many: Songs and tags, Songs and playlists
Below you can see the table queries I am using:
create_songs_table_query = """ CREATE TABLE IF NOT EXISTS songs (
song_id integer PRIMARY KEY AUTOINCREMENT,
title text NOT NULL,
artist text NOT NULL,
added_timestamp integer NOT NULL,
file_id INTEGER NULL,
FOREIGN KEY (file_id)
REFERENCES files (file_id)
ON DELETE CASCADE
); """
create_files_table_query = """ CREATE TABLE IF NOT EXISTS files (
file_id integer PRIMARY KEY AUTOINCREMENT,
filename text NULL,
size integer NULL,
song_id INTEGER NOT NULL,
FOREIGN KEY (song_id)
REFERENCES songs (song_id)
ON DELETE CASCADE
); """
create_tags_table_query = """CREATE TABLE IF NOT EXISTS tags (
tag_id integer PRIMARY KEY AUTOINCREMENT,
tag_text text NOT NULL,
tag_timestamp integer NULL,
); """
create_songs_tags_table_query = """CREATE TABLE IF NOT EXISTS songs_tags (
song_tag_id integer PRIMARY KEY AUTOINCREMENT,
song_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
FOREIGN KEY (song_id)
REFERENCES songs (song_id)
ON DELETE CASCADE,
FOREIGN KEY (tag_id)
REFERENCES tags (tag_id)
ON DELETE CASCADE
); """
create_playlists_table_query = """CREATE TABLE IF NOT EXISTS playlists (
playlist_id integer PRIMARY KEY AUTOINCREMENT,
playlist_title text NOT NULL,
created_timestamp INTEGER NOT NULL,
updated_timestamp INTEGER NULL,
); """
create_songs_playlists__table_query = """CREATE TABLE IF NOT EXISTS songs_playlists (
song_playlist_id integer PRIMARY KEY AUTOINCREMENT,
song_id INTEGER NOT NULL,
playlist_id INTEGER NOT NULL,
FOREIGN KEY (song_id)
REFERENCES songs (song_id)
ON DELETE CASCADE,
FOREIGN KEY (playlist_id)
REFERENCES playlists (playlist_id)
ON DELETE CASCADE
); """
I am trying sucessfully to get the total songs each tag has and order by it:
SELECT tags.tag_id, tags.tag_text, COUNT(tags.tag_id) AS total, tags.included, tags.tag_timestamp
FROM tags
JOIN songs_tags ON tags.tag_id = songs_tags.tag_id
GROUP BY songs_tags.tag_id
ORDER BY total DESC
This is the query to order by tags.tag_text:
SELECT tags.tag_id, tags.tag_text, COUNT(tags.tag_id) AS total, tags.included, tags.tag_timestamp
FROM tags
JOIN songs_tags ON tags.tag_id = songs_tags.tag_id
WHERE tags.included = 1
GROUP BY songs_tags.tag_id
ORDER BY tags.tag_text
I am using Python and Pycharm. Python doesn't return any records and Pycharm shows me the following pop up in the editor window:
Nondeterministic value: column tag_text is neither aggregated, nor mentioned in GROUP BY clause
Although, if I run the query from PyCharm's database console I get the desired results.
It's a bit tricky, any ideas ?
Writer the query correctly, so the SELECT and GROUP BY columns are consistent:
SELECT t.tag_id, t.tag_text, COUNT(*) AS total, t.included, t.tag_timestamp
FROM tags t JOIN
songs_tags st
ON t.tag_id = st.tag_id
WHERE t.included = 1
GROUP BY t.tag_id, t.tag_text, t.included, t.tag_timestamp
ORDER BY t.tag_text;
This also introduced table alias so the query is easier to write and to read.
I have created a function that is supposed to send all the items, with a stock level of less than 10, in my database to a text file. But i am not receiving any data when I press the reorder button.
def Database():
global conn, cursor
conn = sqlite3.connect("main_storage.db")
cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS `admin` (admin_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username TEXT, password TEXT)")
cursor.execute("CREATE TABLE IF NOT EXISTS `product` (product_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, product_name TEXT, product_qty TEXT, product_price TEXT)")
cursor.execute("CREATE TABLE IF NOT EXISTS `basket` (product_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, product_name TEXT, product_qty TEXT, product_price TEXT)")
cursor.execute("SELECT * FROM `admin` WHERE `username` = 'admin' AND `password` = 'admin'")
if cursor.fetchone() is None:
cursor.execute("INSERT INTO `admin` (username, password) VALUES('admin', 'admin')")
conn.commit()
def reorder():
global items
Database()
cursor.execute("SELECT `product_name` FROM `product` WHERE `product_qty` <= 10")
items = cursor.fetchall()
print(items)
cursor.close()
conn.close()
I expect the output to be an array of items within my database e.g. [44, 'motherboard', 9, 80] where 44 is product_id, motherboard is product_name, 9 is product_stock and 80 is product_price. I am actually getting an array with nothing in like: []
product_qty is defined as a TEXT column, so comparisons like <= will be performed between the string values of operands. This may not give the results that you expect:
>>> '8' < '10'
False
Recreate your tables with INTEGER or REAL as the column type for numeric values to get the behaviour that you want. For example:
cursor.execute("""CREATE TABLE IF NOT EXISTS `product` """
"""(product_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"""
"""product_name TEXT, product_qty INTEGER, product_price REAL)""")
I am new to sqlite3, need your help to join these two table, I need to joint the team id between a coach and the teams table, but when compiling it tells me that there is a problem with teamID in the coach table.
Any idea where is my mistake?
def creationTeamDB():
with sqlite3.connect("teams.db") as db1:
cursor = db1.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS team (
teamID INTEGER PRIMARY KEY,
teamName VARCHAR(20) NOT NULL
)
''')
def creationCoachDB():
with sqlite3.connect("coachs.db") as db2:
cursor = db2.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS coach (
coachID INTEGER PRIMARY KEY,
coachName VARCHAR(20) NOT NULL
teamID INTEGER
)
''')
Thanks in advance, :)
G.B
You missed the comma in the second query
CREATE TABLE IF NOT EXISTS coach (
coachID INTEGER PRIMARY KEY,
coachName VARCHAR(20) NOT NULL,
teamID INTEGER
)
I am using sqlalchemy 0.7 and MySQL server version 5.1.63.
I have the following table on my database:
CREATE TABLE `g_domains` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `name` (`name`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
The corresponding model is :
class GDomain(Base):
__tablename__ = 'g_domains'
__table_args__ = {
'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8',
'mysql_collate': 'utf8_general_ci'
}
id = Column(mysql.BIGINT(unsigned=True), primary_key=True)
name = Column(mysql.VARCHAR(255, collation='utf8_general_ci'),
nullable=False, unique=True)
The following query in sql alchemy returns no rows :
session.query(GDomain).filter(GDomain.name.in_(domain_set)).
limit(len(domain_set)).all()
where domain_set is a python list containing some domain names like
domain_set = ['www.google.com', 'www.yahoo.com', 'www.AMAZON.com']
Although the table has a row (1, www.amazon.com) the above query returns only
(www.google.com, www.yahoo.com).
When I run the sql query :
SELECT * FROM g_domains
WHERE name IN ('www.google.com', 'www.yahoo.com', 'www.AMAZON.com')
Do you have an idea why this is happening?
Thanks in advance
What is the model_domain variable? Usually it looks like this:
session.query(GDomain).filter(GDomain.name.in_(domain_set)).
limit(len(domain_set)).all()
Note that the GDomain is used in both places. Alternatively you can use aliases:
domains = orm.aliased(GDomain, name='domain')
session.query(domains).filter(domains.name.in_(domain_set))
You can always try debugging, print the query that produced by sqlalchemy (see: SQLAlchemy: print the actual query)
I want to have a base entity with a field deleted which marks a deleted record. And i have 2 subclasses, each of them to have their own table with all own columns:
from elixir import *
from sqlalchemy import create_engine
class Catalog(Entity):
using_options(inheritance='concrete')
deleted = Boolean
class Contact(Catalog):
using_options(inheritance='concrete')
name = Field(String(60))
class Location(Catalog):
using_options(inheritance='concrete')
name = Field(String(100))
setup_all()
metadata.bind = create_engine('sqlite:///', echo=True)
metadata.create_all()
And the result:
CREATE TABLE __main___catalog (
id INTEGER NOT NULL,
PRIMARY KEY (id)
)
CREATE TABLE __main___contact (
id INTEGER NOT NULL,
name VARCHAR(60),
PRIMARY KEY (id)
)
CREATE TABLE __main___location (
id INTEGER NOT NULL,
name VARCHAR(100),
PRIMARY KEY (id)
)
Questions:
How to avoid creation of a table for the base entity? - solved: using_options(abstract = True)
Why field deleted is not in the created tables? - this solved - i forgot to put it inside a Field
I want to avoid typing in each subclass using_options(inheritance='concrete') but still have "concrete inheritance". Is there a way to make it default for all subclasses?
This works:
class Catalog(Entity):
deleted = Field(Boolean)
using_options(abstract = True, inheritance = 'concrete')
class Contact(Catalog):
name = Field(String(60))
class Location(Catalog):
name = Field(String(100))
and creates the following tables:
CREATE TABLE __main___contact (
id INTEGER NOT NULL,
deleted BOOLEAN,
name VARCHAR(60),
PRIMARY KEY (id),
CHECK (deleted IN (0, 1))
)
CREATE TABLE __main___location (
id INTEGER NOT NULL,
deleted BOOLEAN,
name VARCHAR(100),
PRIMARY KEY (id),
CHECK (deleted IN (0, 1))
)