How to use sqlalchemy to query in many-to-many relation? - python

To get a member list in current organization, i used following statement,
from ..members import members
from flask.ext.login import current_user
from app.common.database import db_session
#members.route("/", methods=["GET"])
#login_required
def index():
datas = db_session.query(Group_has_Person).filter(Group_has_Person.group.organization==current_user.organization).all()
but a exception threw out at runtime: AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Group_has_Person.group has an attribute 'organization'
how to get the member list in a right way? the code below is about model definition:
class Organization(Base):
__tablename__ = 'Organization'
id = Column(Integer, primary_key = True)
title = Column(String(45), nullable = False)
logo = Column(String(256), nullable = False)
contact_name = Column(String(45), nullable = False)
contact_phone = Column(String(45), nullable = False)
created_time = Column(DateTime, nullable = False, default = datetime.now())
User_id = Column(Integer, ForeignKey('User.id'))
user = relationship("User", back_populates="organization")
groups = relationship("Group", back_populates="organization")
class Group(Base):
__tablename__ = 'Group'
id = Column(Integer, primary_key = True)
title = Column(String(45), nullable = False)
teacher = Column(String(45), nullable = True)
contact = Column(String(45), nullable = True)
created_time = Column(DateTime, nullable = False, default = datetime.now())
status = Column(Integer, nullable = False, default = 1)
Organization_id = Column(Integer, ForeignKey('Organization.id'))
organization = relationship("Organization", back_populates="groups")
members = relationship("Group_has_Person", back_populates="group")
class Person(Base):
__tablename__ = 'Person'
id = Column(Integer, primary_key = True)
nickname = Column(String(45), nullable = False)
avatar = Column(String(256), nullable = False)
gender = Column(Integer, nullable = False)
birthday = Column(DateTime, nullable = False)
User_id = Column(Integer, ForeignKey('User.id'))
user = relationship("User", back_populates="person")
groups = relationship("Group_has_Person", back_populates="person")
class Group_has_Person(Base):
__tablename__ = 'Group_has_Person'
Group_id = Column(Integer, ForeignKey('Group.id'), primary_key = True)
Person_id = Column(Integer, ForeignKey('Person.id'), primary_key = True)
created_time = Column(DateTime, nullable = False, default = datetime.now())
status = Column(Integer, nullable = False, default = 0)
group = relationship("Group", back_populates="members")
person = relationship("Person", back_populates="groups")
table scripts:
CREATE TABLE 'Group' (
'id' int(11) NOT NULL AUTO_INCREMENT,
'title' varchar(45) NOT NULL DEFAULT '' COMMENT '班级名称',
'teacher' varchar(45) DEFAULT NULL COMMENT '辅导老师',
'contact' varchar(45) DEFAULT NULL COMMENT '联系方式',
'created_time' datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
'status' int(11) NOT NULL COMMENT '状态:-1-删除 1-正常',
'Organization_id' int(11) NOT NULL,
PRIMARY KEY ('id'),
KEY 'fk_Classes_Organization1_idx' ('Organization_id'),
CONSTRAINT 'fk_Classes_Organization1' FOREIGN KEY ('Organization_id') REFERENCES 'Organization' ('id') ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='班级';
CREATE TABLE 'Person' (
'id' int(11) NOT NULL AUTO_INCREMENT,
'User_id' int(11) NOT NULL,
'nickname' varchar(45) NOT NULL COMMENT '',
'avatar' varchar(256) NOT NULL COMMENT '',
'gender' int(11) NOT NULL COMMENT '',
'birthday' datetime DEFAULT NULL COMMENT '',
PRIMARY KEY ('id'),
KEY 'fk_Person_User1_idx' ('User_id'),
CONSTRAINT 'fk_Person_User1' FOREIGN KEY ('User_id') REFERENCES 'User' ('id') ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='个人';
CREATE TABLE 'Group_has_Person' (
'Group_id' int(11) NOT NULL,
'Person_id' int(11) NOT NULL,
'created_time' datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '加入时间',
'status' int(11) NOT NULL DEFAULT '0' COMMENT '状态 0-申请 1-成功',
PRIMARY KEY ('Group_id','Person_id'),
KEY 'fk_Group_has_Person_Person1_idx' ('Person_id'),
KEY 'fk_Group_has_Person_Group1_idx' ('Group_id'),
CONSTRAINT 'fk_Group_has_Person_Group1' FOREIGN KEY ('Group_id') REFERENCES 'Group' ('id') ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT 'fk_Group_has_Person_Person1' FOREIGN KEY ('Person_id') REFERENCES 'Person' ('id') ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

I've resolved my question by the following statement:
datas = db_session.query(Group_has_Person).join(Group).filter(Group.organization==current_user.organization).all()

Related

default value dont work in sqlalchemy python

Good day!
This is my table
class My_table(Base):
__tablename__ = 'table'
__table_args__ = {'mysql_engine': 'InnoDB',
"mysql_collate": "utf8mb4_general_ci",
"mysql_charset": "utf8mb4"}
id = Column(INTEGER(unsigned=True), primary_key=True, autoincrement=True)
sport = Column(VARCHAR(30), nullable=False)
active = Column(TINYINT(), nullable=False, default=1, server_default=text('1'))
this is result of model
CREATE TABLE `table` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`sport` varchar(30) COLLATE utf8mb4_general_ci NOT NULL,
`active` tinyint NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
sport = {'sport': Football, 'active': 0 }
sport1 = {'sport': Football}
If i add dict sport then all good , but if i try add sport1 then i got pymysql.err.IntegrityError: (1048, "Column 'active' cannot be null")
If
active = Column(TINYINT(), default='1', server_default=text('1'))
active = Column(TINYINT(), default=1, server_default=text('1'))
active = Column(TINYINT(), default='1', server_default='1')
Then in base column active have NULL. How does it work and what am I doing wrong ?

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)

Integrity Error when creating multiple records with Flask SqlAlchemy

I have a function that creates some new DB entries in Flask app with SQL Alchemy
def add_volunteer_client_record(volunteer_id, **kwargs):
try:
volunteer_client = VolunteerClient(volunteer_id=volunteer_id, **kwargs)
volunteer_report_action_items = VolunteerReportActionItems(volunteer_client_id = volunteer_client.id)
db_session.add(volunteer_client)
db_session.add(volunteer_report_action_items)
db_session.commit()
return volunteer_client
except IntegrityError as e:
db_session.rollback()
message = "Integrity error occurred"
raise BadRequestError(messages={'volunteer_client': [message]})
volunteer_client gets created fine but when volunteer_report_action_items is added to the session I received an IntegrityError and I can not quite understand why.
My Models
class VolunteerClient(Base):
__tablename__ = 'volunteer_client'
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
volunteer_id = Column(Integer, ForeignKey('provider_user.user_id', onupdate='CASCADE', ondelete='RESTRICT'), unique=True)
client_id = Column(Integer, ForeignKey('user.id', onupdate='CASCADE', ondelete='RESTRICT'), unique=True)
class VolunteerReportActionItems(Base):
__tablename__ = 'volunteer_report_action_items'
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
volunteer_client_id = Column(Integer, ForeignKey('volunteer_client.id', onupdate='CASCADE', ondelete='RESTRICT'))
SQL
CREATE TABLE IF NOT EXISTS public.volunteer_client
(
id serial PRIMARY KEY,
volunteer_id integer NOT NULL,
client_id integer NOT NULL,
created_by text,
created_at timestamp NOT NULL DEFAULT now(),
updated_by text,
updated_at timestamp NOT NULL DEFAULT now(),
UNIQUE(volunteer_id, client_id),
CONSTRAINT fk_volunteer_client_volunteer FOREIGN KEY (volunteer_id)
REFERENCES public.user (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT fk_volunteer_client_client FOREIGN KEY (client_id)
REFERENCES public.user (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE RESTRICT
);
ALTER TABLE public.volunteer_client OWNER to navigate;
CREATE TABLE IF NOT EXISTS public.volunteer_report_action_items
(
id serial PRIMARY KEY,
volunteer_client_id integer NOT NULL,
created_by text,
created_at timestamp NOT NULL DEFAULT now(),
updated_by text,
updated_at timestamp NOT NULL DEFAULT now(),
CONSTRAINT fk_volunteer_report_action_items_volunteer_client FOREIGN KEY (volunteer_client_id)
REFERENCES public.volunteer_client (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE RESTRICT
);
ALTER TABLE public.volunteer_report_action_items OWNER to navigate;
Any help or advice here would be great. Thanks!
You need to flush the VolunteerClient to be able to access the VolunteerClient.id
def add_volunteer_client_record(volunteer_id, **kwargs):
try:
volunteer_client = VolunteerClient(volunteer_id=volunteer_id, **kwargs)
db_session.add(volunteer_client)
db_session.flush()
volunteer_report_action_items = VolunteerReportActionItems(volunteer_client_id = volunteer_client.id)
db_session.add(volunteer_report_action_items)
db_session.commit()
...

Foreign key constraint is incorrectly formed -- python sqlalchemy

Can't create table ctfd.discorduser (errno: 150 "Foreign key constraint is incorrectly formed")
CREATE TABLE discorduser (
id BIGINT NOT NULL AUTO_INCREMENT,
username VARCHAR(128),
discriminator INTEGER,
avatar_hash VARCHAR(256),
mfa_enabled BOOL,
verified BOOL,
email VARCHAR(256),
PRIMARY KEY (id),
UNIQUE (id),
UNIQUE (id),
FOREIGN KEY(username) REFERENCES users (name) ON DELETE CASCADE,
CHECK (mfa_enabled IN (0, 1)),
CHECK (verified IN (0, 1))
)
The models are defined like this:
class Users(db.Model):
__tablename__ = "users"
__table_args__ = (db.UniqueConstraint("id", "oauth_id"), {})
# Core attributes
id = db.Column(db.Integer, primary_key=True)
oauth_id = db.Column(db.Integer, unique=True)
# User names are not constrained to be unique to allow for official/unofficial teams.
name = db.Column(db.String(128))
password = db.Column(db.String(128))
email = db.Column(db.String(128), unique=True)
type = db.Column(db.String(80))
secret = db.Column(db.String(128))
# Supplementary attributes
website = db.Column(db.String(128))
affiliation = db.Column(db.String(128))
country = db.Column(db.String(32))
bracket = db.Column(db.String(32))
hidden = db.Column(db.Boolean, default=False)
banned = db.Column(db.Boolean, default=False)
verified = db.Column(db.Boolean, default=False)
# Relationship for Teams
team_id = db.Column(db.Integer, db.ForeignKey("teams.id"))
field_entries = db.relationship(
"UserFieldEntries", foreign_keys="UserFieldEntries.user_id", lazy="joined"
)
created = db.Column(db.DateTime, default=datetime.datetime.utcnow)
__mapper_args__ = {"polymorphic_identity": "user", "polymorphic_on": type}
def __init__(self, **kwargs):
super(Users, self).__init__(**kwargs)
class DiscordUser(db.Model):
__tablename__ = "discorduser"
__table_args__ = (db.UniqueConstraint("id"), {})
# Core variables
id = db.Column(db.BigInteger, primary_key=True, unique=True) # Discord ID, int64
# Discord Username 2-32 characters
username = db.Column(db.String(128), db.ForeignKey("users.name", ondelete="CASCADE"))
What could be causing this? The username type is the same for both, and I am under the impression that Foreign Keys do not need to be constrained.

Flask-SQLAlchemy how to do constraint foreign key with cascade in MySQL (InnoDB)?

I've been looking for ways to implement the CONSTRAINT FOREIGN KEY ON DELETE CASCADE in the below UsersAccessMapping model in SQLAlchemy with PyMySQL driver and MariaDB 10.0 with InnoDB in the database.
Python = 3.5.2
SQLAlchemy = 1.1.13
Flask-SQLAlchemy = 2.2
The SQL:
CREATE TABLE Users (
UserID int AUTO_INCREMENT,
Name varchar(200) NOT NULL,
Email varchar(200),
Username varchar(200) NOT NULL,
Password text NOT NULL,
Created datetime,
Updated datetime,
PRIMARY KEY (UserID)
);
CREATE TABLE UsersAccessLevels (
UsersAccessLevelID int AUTO_INCREMENT,
LevelName varchar(100) NOT NULL,
AccessDescription text,
PRIMARY KEY (UsersAccessLevelID)
);
CREATE TABLE UsersAccessMapping (
UsersAccessMappingID int AUTO_INCREMENT,
UserID int NOT NULL,
UsersAccessLevelID int NOT NULL,
PRIMARY KEY (UsersAccessMappingID),
CONSTRAINT fk_useraccess FOREIGN KEY (UserID)
REFERENCES Users(UserID) ON DELETE CASCADE,
CONSTRAINT fk_useraccess_level FOREIGN KEY (UsersAccessLevelID)
REFERENCES UsersAccessLevels(UsersAccessLevelID) ON DELETE CASCADE
);
What I have in my models.py now:
from app import db
class Users(db.Model):
"""All users' information is stored here"""
__tablename__ = "Users"
UserID = db.Column(db.Integer(), primary_key=True)
Name = db.Column(db.String(200), nullable=False)
Email = db.Column(db.String(200))
Username = db.Column(db.String(200), nullable=False)
Password = db.Column(db.Text, nullable=False)
Created = db.Column(db.DateTime)
Updated = db.Column(db.DateTime)
class UsersAccessLevels(db.Model):
"""This defines the various access levels users can have"""
__tablename__ = "UsersAccessLevels"
UsersAccessLevelID = db.Column(db.Integer, primary_key=True)
LevelName = db.Column(db.String(100), nullable=False)
AccessDescription = db.Column(db.Text)
class UsersAccessMapping(db.Model):
"""Each users' access level is defined here"""
__tablename__ = "UsersAccessMapping"
UsersAccessMappingID = db.Column(db.Integer, primary_key=True)
UserID = db.Column(db.Integer, nullable=False)
UsersAccessLevelID = db.Column(db.Integer, nullable=False)
__table_args__ = (
db.ForeignKeyConstraint(
["fk_useraccess", "fk_useraccess_level"],
["Users.UserID", "UsersAccessLevels.UsersAccessLevelID"],
ondelete="CASCADE"
)
)
There is something wrong with the table_args syntax, but I haven't been able to find any examples on how it should be. I found one that was very similar, but in that the third parameter was an empty dict. However, I want to use the ondelete="CASCADE". How would that be added?
When running the python3 manage.py db init, it throws this:
File "/srv/vortech-backend/venv/lib/python3.5/site-packages/sqlalchemy/ext/declarative/base.py", line 196, in _scan_attributes
"__table_args__ value must be a tuple, "
sqlalchemy.exc.ArgumentError: __table_args__ value must be a tuple, dict, or None
I tried changing ondelete="cascade" to a dict {"ondelete": "cascade"}, but that doesn't work either. It gives the same error as above.
Update:
The problem was that the ondelete is supposed to be outside of the tuple, like this:
__table_args__ = (
db.ForeignKeyConstraint(
["fk_useraccess", "fk_useraccess_level"],
["Users.UserID", "UsersAccessLevels.UsersAccessLevelID"]
),
ondelete="CASCADE"
)
However, with this change there is still a syntax error, as ondelete="CASCADE" is not defined. Changing it to a dict {"ondelete": "cascade"} throws this:
File "/srv/vortech-backend/venv/lib/python3.5/site-packages/sqlalchemy/sql/base.py", line 282, in _validate_dialect_kwargs
"named <dialectname>_<argument>, got '%s'" % k)
TypeError: Additional arguments should be named <dialectname>_<argument>, got 'ondelete'
Okay, after some testing and reading, the answer is that SQLAlchemy does some internal magic to achieve it. So, this will accomplish the same result as the SQL:
from app import db # The value is from: db = SQLAlchemy(app)
class Users(db.Model):
"""All users' information is stored here"""
__tablename__ = "Users"
UserID = db.Column(db.Integer(), primary_key=True)
Name = db.Column(db.String(200), nullable=False)
Email = db.Column(db.String(200))
Username = db.Column(db.String(200), nullable=False)
Password = db.Column(db.Text, nullable=False)
Created = db.Column(db.DateTime)
Updated = db.Column(db.DateTime)
class UsersAccessLevels(db.Model):
"""This defines the various access levels users can have"""
__tablename__ = "UsersAccessLevels"
UsersAccessLevelID = db.Column(db.Integer, primary_key=True)
LevelName = db.Column(db.String(100), nullable=False)
AccessDescription = db.Column(db.Text)
class UsersAccessMapping(db.Model):
"""Each users' access level is defined here"""
__tablename__ = "UsersAccessMapping"
UsersAccessMappingID = db.Column(db.Integer, primary_key=True)
UserID = db.Column(
db.Integer, db.ForeignKey("Users.UserID", ondelete="CASCADE"), nullable=False
)
UsersAccessLevelID = db.Column(
db.Integer,
db.ForeignKey("UsersAccessLevels.UsersAccessLevelID", ondelete="CASCADE"),
nullable=False
)
The Constraints and such are automagically handled with the db.ForeignKey() parameters in the column definition. It does not need to be done on the Table directly, like in SQL.
The names for the foreign keys appear to be automatically generated by SQLAlchemy also. Here's how it looks like in the database:

Categories

Resources