SQL Alchemy query multiple tables - flask-sqlalchemy

I am trying to make a query using SQL alchemy which would otherwise been very simple if I wasn't using ORM, so I am thinking to myself surely there must be a straightforward way. I have gone through most of the questions on this topic but don't seem to answer my question. I have these two tables
class Artisan(Base):
__tablename__ = 'artisan'
name = Column(String(80), nullable=False)
skill = Column(String(80), nullable=False)
id = Column(Integer, primary_key=True)
bio = Column(String(300))
category = Column(Integer, ForeignKey(Category.id))
user = Column(Integer, ForeignKey(Users.id))
id_no = Column(Integer, nullable=False)
users = relationship(Users)
and
class Address(Base):
__tablename__ = 'address'
building = Column(String(80), nullable=False)
floor = Column(String(80), nullable=False)
house_no = Column(String(80), nullable=False)
telephone = Column(String(80), nullable=False)
kwetu_address = Column(String(80), nullable=False)
id = Column(Integer, primary_key=True)
lat = Column(String(25))
lng = Column(String(25))
artisan = Column(Integer, ForeignKey(Artisan.id))
user = Column(Integer, ForeignKey(Users.id))
users = relationship(Users)
I would like to filter the Artisans by a category, then filter the addresses by the filtered artisans, and present the results in a way that associates the artisan with respective addresses hopefully getting sqlachemy to do the filtering
The best I can come up with involves two queries and post processing which I feel is very inefficient
my_artisans = (session.query(Artisan).filter_by(category=cat_id))
my_addresses = (session.query(Address)
.join(Artisan, Artisan.id ==Address.artisan).filter_by(category=cat_id))
return jsonify(artisans =[art.serialize for art in my_artisans], addresses=[add.serialize for add in my_addresses])
Thanks
Add on - all related classes
import sys
from sqlalchemy import Column, ForeignKey, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class Users(Base):
__tablename__ = 'users'
name = Column(String(80), nullable=False)
email = Column(String(80), nullable=False)
id = Column(Integer, primary_key=True)
bio = Column(String(300))
picture = Column(String(80))
class Category(Base):
__tablename__ = 'category'
name = Column(String(80), nullable=False)
id = Column(Integer, primary_key=True)
user = Column(Integer, ForeignKey(Users.id))
users = relationship(Users)
#property
def serialize(self):
return{
'id': self.id,
'name': self.name
}
class Artisan(Base):
__tablename__ = 'artisan'
name = Column(String(80), nullable=False)
skill = Column(String(80), nullable=False)
id = Column(Integer, primary_key=True)
bio = Column(String(300))
category = Column(Integer, ForeignKey(Category.id))
user = Column(Integer, ForeignKey(Users.id))
id_no = Column(Integer, nullable=False)
users = relationship(Users)
#property
def serialize(self):
return{
'id': self.id,
'name': self.name,
'skill': self.skill,
'category': self.category,
'bio': self.bio,
'id_no': self.id_no
}
class Portfolio(Base):
__tablename__ = 'portfolio'
title = Column(String(80), nullable=False)
details = Column(String(300), nullable=False)
id = Column(Integer, primary_key=True)
artisan = Column(Integer, ForeignKey(Artisan.id))
user = Column(Integer, ForeignKey(Users.id))
users = relationship(Users)
#property
def serialize(self):
return{
'id': self.id,
'title': self.title,
'details': self.details
}
class Endorsements(Base):
__tablename__ = 'endorsements'
title = Column(String(80), nullable=False)
details = Column(String(300), nullable=False)
id = Column(Integer, primary_key=True)
artisan = Column(Integer, ForeignKey(Artisan.id))
user = Column(Integer, ForeignKey(Users.id))
users = relationship(Users)
#property
def serialize(self):
return{
'id': self.id,
'title': self.title,
'details': self.details
}
class Address(Base):
__tablename__ = 'address'
building = Column(String(80), nullable=False)
floor = Column(String(80), nullable=False)
house_no = Column(String(80), nullable=False)
telephone = Column(String(80), nullable=False)
kwetu_address = Column(String(80), nullable=False)
id = Column(Integer, primary_key=True)
lat = Column(String(25))
lng = Column(String(25))
artisan = Column(Integer, ForeignKey(Artisan.id))
user = Column(Integer, ForeignKey(Users.id))
users = relationship(Users)
#property
def serialize(self):
return{
'id': self.id,
'lat': self.lat,
'lng': self.lng,
'kwetu_address': self.kwetu_address,
'artisan': self.artisan
}
engine = create_engine('sqlite:///mycatalog.db')
Base.metadata.create_all(engine)

This gave me what I needed in terms of associating my artisan with respective addresses, though still using two queries
my_artisans = (session.query(Artisan).filter_by(category=cat_id))
my_addresses = (session.query(Address)
.join(Artisan, Artisan.id ==Address.artisan).filter_by(category=cat_id))
these_addresses = []
for art in my_artisans:
art_id = art.id
for add in my_addresses:
if art_id == add.artisan:
grouped_address = {"Artisan Id" : art.id, "name" : art.name, "skill" : art.skill, "Lat" : add.lat, "lng" : add.lng}
these_addresses.append(grouped_address)
return jsonify({'Addresses': these_addresses })

Related

ArgumentError: Mapper mapped class Roles_users->User Roles could not assemble any primary key columns for mapped table 'User Roles'

I'm trying to write a web app with user roles and I keep getting this error no matter the changes I make.
Could you please help me find what's wrong?
Thank you in advance!!
This is my models.py file:
from sqlalchemy import Column, Integer, String, Float, Boolean, ForeignKey
from flask_login import UserMixin
from sqlalchemy.orm import relationship, backref
import db
class Rol(db.Base):
__tablename__ = 'Roles'
id_rol = Column(Integer, primary_key=True)
nombre_rol = Column(String, nullable=False)
user_rol = relationship("User rol", back_populates="rol", uselist =False)
def __init__(self, nombre_rol):
self.nombre_rol = nombre_rol
class User(UserMixin, db.Base):
__tablename__ = 'Users'
user_id = Column(Integer, primary_key=True)
username = Column(String, unique=True, nullable=False)
password = Column(String, unique=True, nullable=False)
is_active = Column(Boolean, default=False)
user_rol = relationship("User rol", back_populates="user", uselist =False)
def __init__(self,username,password,is_active):
self.username = username
self.password = password
self.is_active = is_active
def get_id(self):
return self.id
def is_active(self):
return self.is_active
def activate_user(self):
self.is_active = True
def get_username(self):
return self.username
def get_rol(self):
return self.rol
class Roles_users(db.Base):
__tablename__ = 'User Roles'
user_id = Column(Integer, ForeignKey("Users.user_id"))
id_rol = Column(Integer, ForeignKey("Roles.id_rol"))
user = relationship("User", back_populates="user roles")
rol = relationship("Rol", back_populates="user roles")

sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed

I am trying to create table for some courses, wհօse last column is another table and presents the lecturers names.
class Teachers(Base):
__tablename__ = 'teacher'
full_name = Column(String, unique=False, primary_key=True)
company = Column(String, unique = False)
class Course(Base):
__tablename__ = 'course'
course_name = Column(String, nullable=False)
course_id = Column(String,primary_key=True)
level = Column(String,unique=False)
teachers = Teachers
But I found following issue when the name of one lecturer is repeated in different courses:
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: teacher.full_name
How can I solve this problem?
Your teachers column is not a column. Instead you are adding a whole table as an attribute.
What you should do, is defining a column in Course that contains the primary key of the Teacher (assuming there is only one teacher per course) and mark it as a ForeignKey. You can than also add a relationship for easier work.
from pathlib import Path
import sqlalchemy.orm
from sqlalchemy import Column, String, ForeignKey, create_engine
from sqlalchemy.orm import declarative_base, relationship
Base = declarative_base()
class Teacher(Base):
__tablename__ = "teachers"
full_name = Column(String, unique=False, primary_key=True)
company = Column(String, unique=False)
class Course(Base):
__tablename__ = "courses"
course_id = Column(String, primary_key=True)
course_name = Column(String, nullable=False)
level = Column(String, unique=False)
teacher_name = Column(ForeignKey("teachers.full_name"))
teacher = relationship("Teacher")
def main():
if Path("test_database.db").exists():
Path("test_database.db").unlink()
engine = create_engine("sqlite:///test_database.db")
Base.metadata.create_all(engine)
session = sqlalchemy.orm.Session(autocommit=False, autoflush=False, bind=engine)
teacher_1 = Teacher(full_name="Bob")
course_1 = Course(course_id=1, course_name="Course 1", level="beginner", teacher=teacher_1)
course_2 = Course(course_id=2, course_name="Course 2", level="advanced", teacher=teacher_1)
session.add_all([course_1, course_2])
session.commit()
if __name__ == "__main__":
main()

Issue when creating on-to-many relationships in SQLAlchemy database

I created a Flask app with a database, with following classes:
db = SQLAlchemy(app)
class Category(db.Model):
__tablename__ = 'Category'
children = relationship("Child")
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.Text())
icon = db.Column(db.Text())
subcategories = db.relationship('Subcategory', backref="category")
def __init__(self, name, subcategories, icon):
self.name = name
self.color = icon
self.subcategories = subcategories
class Subcategory(db.Model):
__tablename__ = 'Subcategory'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.Text())
color = db.Column(db.Text())
reward_points = db.Column(db.Integer())
category = db.Column(db.Text())
tasks = db.relationship('Task', backref="subcategory")
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
def __init__(self, name, color, reward_points, category, tasks, category_id):
self.name = name
self.color = color
self.reward_points = reward_points
self.category = category
self.tasks = tasks
self.category_id = category_id
class Task(db.Model):
__tablename__ = 'Task'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
task = db.Column(db.Text())
start = db.Column(db.DateTime())
end = db.Column(db.DateTime())
duration = db.Column(db.Integer)
scheduled = db.Column(db.DateTime())
created = db.Column(db.DateTime())
status = db.Column(db.Text())
category = db.Column(db.Text())
subcategory = db.Column(db.Text())
tags = db.Column(db.Text())
subcategory_id = db.Column(db.Integer, db.ForeignKey('subcategory.id'))
def __init__(self, task, start, end, duration, category, subcategory, tags, created, status, scheduled, subcategory_id):
self.task = task
self.start = start
self.end = end
self.duration = duration
self.category = category
self.subcategory = subcategory
self.tags = tags
self.created = created
self.status = status
self.scheduled = scheduled
self.subcategory_id = subcategory_id
My goal is to create one to many relationships between the classes. I am trying to run the db.create_all() command, but am getting following error:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'Subcategory.category_id' could not find table 'category' with which to generate a foreign key to target column 'id'
What am I doing wrong? Other questions on Stackoverflow with similar issues did not resolve my error.
Changing
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
to
category_id = db.Column(db.Integer, db.ForeignKey('Category.id'))
solved the issue.
Changing "category.id" to "category.c.id" solved the issue as well. If anyone knows why, pls let me know ;-)

Sqlalchemy relationship issue working with datatables

I'm currently struggling creating web tables using bootstrap datatables, sqlalchemy and sqlalchemy-datatables.
Sqlalchemy seems generating correct sql query, datatables is populated with correct information.
However when I'm trying to search for record in datatable search field I'm getting an error:
DataTables warning: table id=main_table - Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with VrfMain.scope has an attribute 'cast'
I tried to remove any relationships from query, and it works.
So problem is with relationship somewhere. Can anyone help me please ?
Here is my sql models:
class VrfMain(db.Model):
__tablename__ = 'vrf_main'
id = Column(Integer, primary_key=True, autoincrement=True)
vrf_name = Column(String, unique=True)
rd = Column(String, unique=True)
primary_rt = Column(String, unique=True)
additional_rt = Column(String, unique=True)
description = Column(String)
scope_id = Column(Integer, ForeignKey('subnet_scopes.id'))
scope = relationship('SubnetScopes')
def __init__(self, vrf_name, rd, primary_rt, description, scope_id):
self.vrf_name = vrf_name
self.rd = rd
self.primary_rt = primary_rt
self.description = description
self.scope_id = scope_id
class SubnetScopes(db.Model):
__tablename__ = 'subnet_scopes'
id = Column(Integer, primary_key=True, autoincrement=True)
scope_name = Column(String, unique=True)
def __init__(self, scope_name):
self.scope_name = scope_name
def __repr__(self):
return str(self.scope_name)
Here is part of flask code:
# defining datatable columns
columns = [
ColumnDT(VrfMain.id),
ColumnDT(VrfMain.vrf_name),
ColumnDT(VrfMain.rd),
ColumnDT(VrfMain.primary_rt),
ColumnDT(VrfMain.additional_rt),
ColumnDT(VrfMain.description),
ColumnDT(VrfMain.scope)
]
query = VrfMain.query.\
join(SubnetScopes).\
filter(VrfMain.scope_id == SubnetScopes.id).\
with_entities(VrfMain.id, VrfMain.vrf_name, VrfMain.rd, VrfMain.primary_rt, VrfMain.additional_rt, VrfMain.description, SubnetScopes.scope_name)
print(query)
params = request.args.to_dict()
rowTable = DataTables(params, query, columns)
return jsonify(rowTable.output_result())
Here is sql query that is generated
SELECT vrf_main.id AS vrf_main_id, vrf_main.vrf_name AS vrf_main_vrf_name, vrf_main.rd AS vrf_main_rd, vrf_main.primary_rt AS vrf_main_primary_rt, vrf_main.additional_rt AS vrf_main_additional_rt, vrf_main.description AS vrf_main_description, subnet_scopes.scope_name AS subnet_scopes_scope_name
FROM vrf_main INNER JOIN subnet_scopes ON subnet_scopes.id = vrf_main.scope_id
WHERE vrf_main.scope_id = subnet_scopes.id
Here is javascript code:
$(document).ready(function() {
var table = $('#main_table').DataTable({
"processing": true,
"serverSide": true,
"ajax": {
"url": "{{ url_for('home_blueprint.get_vrf_data') }}"
},
"lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]],
I decide not to use sqlalchemy-datatables. instead I'm will use code:
all_vrf_data = []
for row in VrfMain.query.all():
row_proccessed = dict(row.__dict__); row_proccessed.pop('_sa_instance_state', None)
all_vrf_data.append(row_proccessed)
return_data = {"data": all_vrf_data}
return json.dumps(return_data, indent=4, sort_keys=True, default=str)

How to add a self increased field by category

I want to set a sub id for my post table. This table already has a id field as primary. But post has categories. I want a sub_id for post in each category as self increased.
I am writing my blog. Post is has PostCategory, PostCategory to Post is one-to-many relationship.
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
# I tried this, but this did not work
sub_id = db.Column(db.Integer, nullable=True, default=int(f'select count(*) from post where category_id={category_id}')+1)
title = db.Column(db.String(120), unique=True, nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
# user in Foreignkey is table name
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
category_id = db.Column(db.Integer, db.ForeignKey('post_category.id'), nullable=False)
def post_content_render_markdown(self):
return markdown(self.content, extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
])
def __repr__(self):
return f"Post('{self.title}', {self.category.name}, '{self.date_posted}')"
class PostCategory(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), unique=True, nullable=False)
description = db.Column(db.Text, nullable=False)
posts = db.relationship('Post', backref='category', lazy=True)
def __repr__(self):
return f"Post Category('{self.id}', '{self.name}')"
error msg
sub_id = db.Column(db.Integer, nullable=True, default=int(f'select count(*) from post where category_id={category_id}')+1)
NameError: name 'category_id' is not defined
There is an another existing argument that can increase your sub_id
https://docs.sqlalchemy.org/en/13/core/defaults.html#python-executed-functions
sub_id = db.Column(db.Integer, nullable=True, autoincrement=True)
Try this