Conversion from sql to sqlalchemy - python

I'm trying to create classes for sqlalchemy in order to make sqlalchemy create database Sometables.
Here is SQL code:
CREATE table "Sometable" (
id INTEGER UNIQUE NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
database_id INTEGER REFERENCES databases (id) ON DELETE CASCADE NOT NULL,
CONSTRAINT a UNIQUE (name, database_id) ON CONFLICT IGNORE
);
I have tried so far:
class Sometable(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(140))
database_id = db.Column(db.Integer,
db.ForeignKeyConstraint("Sometable.database_id", "databases.id"))
class Databases(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(140))
and:
class Sometable(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
database_id = db.Column(db.Integer, db.ForeignKeyConstraint(["Sometable.database_id"], ["databases.id"]))
class Databases(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
and:
class Sometable(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
database_id = db.Column(db.Integer, db.ForeignKey("databases.id"))
db.ForeignKeyConstraint(["database_id"], ["databases.id"])
class Databases(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
and I cannot figure out how to do it. When I do flask db init; migrate; upgrade I receive schema of Sometable:
CREATE TABLE Sometable(
id INTEGER NOT NULL,
name VARCHAR,
database_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(database_id) REFERENCES databases (id)
#Edit
and also tried:
class Sometable(db.Model):
id = db.Column(db.Integer, primary_key=True, unique=True)
name = db.Column(db.String)
database_id = db.Column(db.Integer, db.ForeignKey("databases.id"))
db.ForeignKeyConstraint(["database_id"], ["databases.id"])
db.UniqueConstraint("name", "database_id")
class Databases(db.Model):
id = db.Column(db.Integer, primary_key=True, unique=True)
name = db.Column(db.String)

Related

sqlalchemy check foreign key refer data exist before insert?

I'm new to the sqlalchemy and fastAPI. I wonder there is any way to check refer data automatically before inserting it. For example, I want to make sure that profile.user_id exists before adding a new profile, but I don't want to do it by myself. Is that possible? Below are my table settings.
class User(Base):
__tablename__ = "user"
id = Column("id", Integer, primary_key=True, autoincrement=True)
email = Column(String, unique=True, nullable=False)
hashed_password = Column(String, nullable=False)
create_time = Column(DateTime, nullable=False, default=func.now())
login_time = Column(DateTime, nullable=False, default=func.now())
class Profile(Base):
__tablename__ = "user_profile"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("user.id"), nullable=False)
name = Column(String, )
age = Column(Integer)
country = Column(Integer)
photo = Column(String, )
What is missing in your mapped classes is a relationship.
In ORMs, these relationships handle ensuring the foreign key exits and makes creating relationships much easier.
Also, if you want to set a default value on the SQL side (since you use default=func.now()), you should use the server_default=func.now() keyword argument. Otherwise, use the python side equivalent, default=datetime.utcnow().
Finally, if your relationship is a one-to-one relationship, use the uselist=False keyword argument in the User.profiles relationship (also best to rename it User.profile).
from datetime import datetime
from sqlalchemy import (
Column,
DateTime,
ForeignKey,
Integer,
String,
create_engine,
)
from sqlalchemy.orm import Session, declarative_base, relationship
Base = declarative_base()
class User(Base):
__tablename__ = "user"
id = Column("id", Integer, primary_key=True, autoincrement=True)
email = Column(String, unique=True, nullable=False)
hashed_password = Column(String, nullable=False)
create_time = Column(DateTime, nullable=False, default=datetime.utcnow())
login_time = Column(DateTime, nullable=False, default=datetime.utcnow())
profiles = relationship(
"Profile", back_populates="user"
) # add uselist=False if one-to-one
class Profile(Base):
__tablename__ = "user_profile"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("user.id"), nullable=False)
name = Column(String)
age = Column(Integer)
country = Column(Integer)
photo = Column(String)
user = relationship("User", back_populates="profiles")
engine = create_engine("sqlite://", echo=True, future=True)
Base.metadata.create_all(engine)
with Session(engine) as session:
ljmc = User(email="ljmc#stack.overflow", hashed_password="00ab")
ljmc_profile = Profile(name="ljmc")
ljmc_profile.user = ljmc
session.add(ljmc)
session.flush()
This emits:
CREATE TABLE user (
id INTEGER NOT NULL,
email VARCHAR NOT NULL,
hashed_password VARCHAR NOT NULL,
create_time DATETIME NOT NULL,
login_time DATETIME NOT NULL,
PRIMARY KEY (id),
UNIQUE (email)
)
CREATE TABLE user_profile (
id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
name VARCHAR,
age INTEGER,
country INTEGER,
photo VARCHAR,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES user (id)
)
INSERT INTO user (email, hashed_password, create_time, login_time) VALUES ('ljmc#stack.overflow', '00ab', '2023-01-17 10:11:48.250845', '2023-01-17 10:11:48.250959')
INSERT INTO user_profile (user_id, name, age, country, photo) VALUES (1, 'ljmc', None, None, None)

Sqlalchemy: How to manage multiple tables (Python)?

I need to create four tables. First table include the "Users", second include "Group Name", the second table should be related to "Users" table. Third table include "Groups Columns Data" which is related to "Group" table, and finally the fourth table is "Group Borrow Lending Data" which is also linked to third table i.e "Groups".
But it's throwing an error when I try to get specific username.
TypeError: sqlalchemy.exc.InvalidRequestError: Can't compare a
collection to an object or collection; use contains() to test for
membership.
#v1.get("/get-specific-groups/{group_name}", tags=["GROUP"])
def get_specific_groups(group_name: str, current_user: CreateGroupSchema = Depends(get_current_user), db: Session = Depends(get_db)):
return db.query(User, Group, GroupColumnsData).join(GroupColumnsData).filter(Group.owner_username == current_user.get("username")).all()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(60), unique=True, nullable=False)
email = Column(String(80), unique=True, nullable=False)
password = Column(String(140), nullable=False)
groups = relationship("Group", backref="owner")
class Group(Base):
__tablename__ = "groups"
id = Column(Integer, primary_key=True, index=True)
group_name = Column(String(60), unique=True, nullable=False)
description = Column(String, nullable=False)
owner_username = Column(String, ForeignKey("users.username"), default=User.username)
group_username = relationship("GroupColumnsData", backref="group_owner")
class GroupColumnsData(Base):
__tablename__ = "groupsColumnsData"
id = Column(Integer, primary_key=True, index=True)
payee_name = Column(String(60))
item_name = Column(String(100))
amount_spent = Column(Integer)
owner_group = Column(String, ForeignKey("groups.group_name"), default=Group.group_name)
class GroupBorrowLendingData(Base):
__tablename__ = "groupsBorrowLendingData"
id = Column(Integer, primary_key=True, index=True)
lender = Column(String(60))
money_borrowed = Column(Integer)
borrower = Column(String(60))
owner_group = Column(String, ForeignKey("groups.group_name"), default=Group.group_name)
The Following code worked!
#v1.get("/get-specific-groups/{group_name}", tags=["GROUP"])
def get_specific_groups(group_name: str, current_user: CreateGroupSchema = Depends(get_current_user), db: Session = Depends(get_db)):
return db.query(Group).filter(Group.owner_username == current_user.get("username")).filter(Group.group_name.match(group_name)).all()

Flask SQL: foreign keys and OnUpdate

I have three SQL tables. Two are independent of each other and third that will have 2 columns (among others) each with relevant id from the first two tables
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
import datetime
class Dad(db.Model):
__tablename__ = "dad"
id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
name = db.Column(db.String, unique=True)
children = db.relationship('Children', backref='dad', lazy='dynamic')
class Mom(db.Model):
__tablename__ = "mom"
id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
name = db.Column(db.String, unique=True)
children = db.relationship('Children', backref='mom', lazy='dynamic')
class Children(db.Model):
__tablename__ = "children"
id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
name = db.Column(db.String, unique=True)
dad_name = db.Column(db.String)
mom_name = db.Column(db.String)
dad_id = db.Column(db.Integer, db.ForeignKey('dad.id'))
mom_id = db.Column(db.Integer, db.ForeignKey('mom.id'))
created_at = db.Column(db.DateTime(6), default=datetime.datetime.utcnow)
updated_at= db.Column(db.DateTime(6), default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
I have 2 issues that I need help with
If I do dad.children.append({'name':'Joe', 'dad_name':'Bill', 'mom_name':'Samantha'}) it attaches the dad.id automatically but not the mom.id. I want it to attach mom.id as well based on her name even if I create it from dad.
When I try to update the record from pgAdmin, it should update the updated_at column with the update time but it doesn't.
Thanks!

Flask-SQLAlchemy polymorphic association

I have two main table which is role and users, and on users I making 3 associate to table operator, teacher and student.
So far, I making it like this:
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
permissions = db.Column(db.Integer)
users = db.relationship('User',
backref='role', lazy='dynamic')
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), index=True)
email = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
__mapper_args__ = {
'polymorphic_identity': 'users',
'with_polymorphic': '*',
}
class Operator(User):
__tablename__ = 'operator'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
__mapper_args__ = {
'polymorphic_identity': 'operator',
'with_polymorphic': '*'
}
class Teacher(User):
__tablename__ = 'teacher'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
phone_number = db.Column(db.Integer)
other_teacher_data = db.Column(db.String)
__mapper_args__ = {
'polymorphic_identity': 'teacher',
'with_polymorphic': '*'
}
class Student(User):
__tablename__ = 'student'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
other_student_data = db.Column(db.String)
__mapper_args__ = {
'polymorphic_identity': 'student',
'with_polymorphic': '*'
}
But I got this error message:
Attempting to flush an item of type as a member of collection
"Role.users". Expected an object of type or a polymorphic subclass of
this type. If is a subclass of , configure mapper "Mapper|User|users"
to load this subtype polymorphically, or set enable_typechecks=False
to allow any subtype to be accepted for flush.
I have tried to set enable_typechecks=False on users field on Role table, and then I got this error message:
psycopg2.errors.UniqueViolation) duplicate key value violates unique
constraint "ix_users_email" DETAIL: Key
(email)=(zidanecr7kaka2#gmail.com) already exists. [SQL: 'INSERT INTO
users (confirmed, first_name, last_name, email, password_hash,
role_id, date_of_birth, address, created_at, updated_at) VALUES
(%(confirmed)s, %(first_name)s, %(last_name)s, %(email)s,
%(password_hash)s, %(role_id)s, %(date_of_birth)s, %(address)s,
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING users.id']
[parameters: {'confirmed': False, 'first_name': 'Tri', 'last_name':
'Nanda', 'email': 'zidanecr7kaka2#gmail.com', 'password_hash':
'pbkdf2:sha1:1000$PtpuVYh4$b5bbb03939cf6ca9013308b62276889d35a8cc1b',
'role_id': 5, 'date_of_birth': None, 'address': None}]
I got that message even when I try with different data, but it still say duplicate key value.
Please, what's wrong with my code..?, or any example with similliar case..?
Spot the difference :)
from app import db
from flask_login import UserMixin
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
permissions = db.Column(db.Integer)
users = db.relationship('User',
backref='role', lazy='dynamic')
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
type = db.Column(db.String(50))
name = db.Column(db.String(64), index=True)
email = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
__mapper_args__ = {
'polymorphic_identity': 'users',
'with_polymorphic': '*',
"polymorphic_on": type
}
class Operator(User):
__tablename__ = 'operator'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
__mapper_args__ = {
'polymorphic_identity': 'operator',
'with_polymorphic': '*'
}
class Teacher(User):
__tablename__ = 'teacher'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
phone_number = db.Column(db.Integer)
other_teacher_data = db.Column(db.String)
__mapper_args__ = {
'polymorphic_identity': 'teacher',
'with_polymorphic': '*'
}
class Student(User):
__tablename__ = 'student'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
other_student_data = db.Column(db.String)
__mapper_args__ = {
'polymorphic_identity': 'student',
'with_polymorphic': '*'
}
It's not a good error message, but you have missed the type field on the base class. It needs somewhere to store the type of the children, otherwise if you ran a query on the base class and expected polymorphism, it would have to search all the other child tables to match up the ids. See:
https://docs.sqlalchemy.org/en/13/orm/inheritance.html
Above, an additional column type is established to act as the discriminator, configured as such using the mapper.polymorphic_on parameter. This column will store a value which indicates the type of object represented within the row. The column may be of any datatype, though string and integer are the most common.
While a polymorphic discriminator expression is not strictly necessary, it is required if polymorphic loading is desired. Establishing a simple column on the base table is the easiest way to achieve this, however very sophisticated inheritance mappings may even configure a SQL expression such as a CASE statement as the polymorphic discriminator.
They also recommend in the tutorial that you don't use a separate id column in the children and make the child id columns both primary and foreign keys back to the base.
You may want to remove the "with_polymorphic": "*" as it loads all the subfields upfront (inefficient). You may want this in certain cases when you are doing filters but you can turn it on as you are doing the queries:
https://docs.sqlalchemy.org/en/13/orm/inheritance_loading.html

SQLAlchemy how to get Column values from association table in many to many relationship

I have Playlist and Tracks class and assigned Foreign keys to playlistsTracks. all ok when want to get Playlist tracks i use playlist.tracks. But now i need to also get the 'id' of the playlistsTracks. How can i get the 'id' or any extra column from association table?
playlistsTracks = db.Table('tbl_test_playlisttracks',
db.Column('id', db.Integer, primary_key=True),
db.Column('playlist_id', db.Integer, db.ForeignKey('tbl_test_playlists.id', ondelete='CASCADE')),
db.Column('track_id', db.Integer, db.ForeignKey('tbl_tracks.id'))
)
class Track(db.Model):
__tablename__ = 'tbl_tracks'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
audio_url = db.Column(db.Text)
movie_id = db.Column(db.Integer)
entry = db.relationship('Playlist', secondary=playlistsTracks, backref=db.backref('tracks', lazy='dynamic'))
class Playlist(db.Model):
__tablename__ = 'tbl_test_playlists'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
user_id = db.Column(db.String(255))
current_rev = db.Column(db.Integer, default=0)
db.session.query(playlistsTracks).join(Track).all()
or
db.session.query(playlistsTracks.c.id).join(Track).all()
The same for the other entity Playlist

Categories

Resources