过滤条件一般是通过filter()
方法实现的,常见的过滤条件如下:
equals
filter(Book.name == 'Name 3')
not equals
filter(Book.name != 'Name 3')
like
filter(Book.name.like("%Name%"))
in
filter(Book.name.in_(['Name 1', 'Name 2']))
# in_的参数也可以是query的查询结果
filter(Book.name.in_(session.query(Book.name).filter(Book.name.like('%Name%'))))
not in
filter(~Book.name.in_(['Name 1', 'Name 2']))
is null
filter(Book.name == None)
# 或者是
filter(Book.name.is_(None))
is not null
filter(User.name != None)
# 或者是
filter(User.name.isnot(None))
and
filter(Book.name == 'Name 1', Book.price <= 50).all()
filter(and_(Book.name == 'Name 1', Book.price <= 50)).all()
filter(Book.name == 'Name 1').filter(Book.price <= 50).all()
or
filter(or_(Book.name == 'Name 1', Book.price >= 50)).all()
equals测试如下:
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy import create_engine, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_demo'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False)
price = Column(Float)
def __str__(self):
return 'Book(id:{}, name:{}, price:{})'.format(self.id, self.name, self.price)
Session = sessionmaker(bind=engine)
session = Session()
results = session.query(Book).filter(Book.name == 'Name 3').all()
for result in results:
print(result)
打印:
Book(id:3, name:Name 3, price:14.0)
not equals测试:
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy import create_engine, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_demo'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False)
price = Column(Float)
def __str__(self):
return 'Book(id:{}, name:{}, price:{})'.format(self.id, self.name, self.price)
Session = sessionmaker(bind=engine)
session = Session()
results = session.query(Book).filter(Book.name != 'Name 3').all()
for result in results:
print(result)
打印:
Book(id:1, name:Name 1, price:45.0)
Book(id:2, name:Name 2, price:49.0)
Book(id:4, name:Name 4, price:16.0)
Book(id:5, name:Name 5, price:17.0)
Book(id:6, name:Name 6, price:3.0)
Book(id:7, name:Name 7, price:25.0)
Book(id:8, name:Name 8, price:16.0)
Book(id:9, name:Name 9, price:32.0)
Book(id:10, name:Name 10, price:22.0)
like一般用于模糊查询,测试如下:
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy import create_engine, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_demo'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False)
price = Column(Float)
def __str__(self):
return 'Book(id:{}, name:{}, price:{})'.format(self.id, self.name, self.price)
Session = sessionmaker(bind=engine)
session = Session()
results = session.query(Book).filter(Book.name.like('%me%')).all()
for result in results:
print(result)
打印:
Book(id:1, name:Name 1, price:45.0)
Book(id:2, name:Name 2, price:49.0)
Book(id:3, name:Name 3, price:14.0)
Book(id:4, name:Name 4, price:16.0)
Book(id:5, name:Name 5, price:17.0)
Book(id:6, name:Name 6, price:3.0)
Book(id:7, name:Name 7, price:25.0)
Book(id:8, name:Name 8, price:16.0)
Book(id:9, name:Name 9, price:32.0)
Book(id:10, name:Name 10, price:22.0)
其中,%表示匹配表示零个或者多个任意字符,只要name字段中含有me字符串,就会被查询到;
还可以使用下划线_,表示一个任意字符(必须有一个字符)。
in测试:
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy import create_engine, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_demo'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False)
price = Column(Float)
def __str__(self):
return 'Book(id:{}, name:{}, price:{})'.format(self.id, self.name, self.price)
Session = sessionmaker(bind=engine)
session = Session()
results = session.query(Book).filter(Book.name.in_(['Name 2', 'Name 5'])).all()
for result in results:
print(result)
打印:
Book(id:2, name:Name 2, price:49.0)
Book(id:5, name:Name 5, price:17.0)
为了避免和Python中的关键字in冲突,ORM将in后面添加一个下划线;
除了列表,in_()
中也可以传入元组,但是建议使用列表。
not in两种方式测试:
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy import create_engine, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_demo'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False)
price = Column(Float)
def __str__(self):
return 'Book(id:{}, name:{}, price:{})'.format(self.id, self.name, self.price)
Session = sessionmaker(bind=engine)
session = Session()
results1 = session.query(Book).filter(~Book.name.in_(['Name 2', 'Name 5'])).all()
print('~ test:')
for result in results1:
print(result)
results2 = session.query(Book).filter(Book.name.notin_(['Name 2', 'Name 5'])).all()
print('Notin test:')
for result in results2:
print(result)
打印:
~ test:
Book(id:1, name:Name 1, price:45.0)
Book(id:3, name:Name 3, price:14.0)
Book(id:4, name:Name 4, price:16.0)
Book(id:6, name:Name 6, price:3.0)
Book(id:7, name:Name 7, price:25.0)
Book(id:8, name:Name 8, price:16.0)
Book(id:9, name:Name 9, price:32.0)
Book(id:10, name:Name 10, price:22.0)
Notin test:
Book(id:1, name:Name 1, price:45.0)
Book(id:3, name:Name 3, price:14.0)
Book(id:4, name:Name 4, price:16.0)
Book(id:6, name:Name 6, price:3.0)
Book(id:7, name:Name 7, price:25.0)
Book(id:8, name:Name 8, price:16.0)
Book(id:9, name:Name 9, price:32.0)
Book(id:10, name:Name 10, price:22.0)
显然,not in两种实现方式( ~ 取反和notin_()
方法)的查询结果是一样的,但是使用notin_()
方法的可读性更高。
null和not null测试:
修改book表name字段允许为null之后手动插入一条name字段为空的数据,再测试:
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy import create_engine, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_demo'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50))
price = Column(Float)
def __str__(self):
return 'Book(id:{}, name:{}, price:{})'.format(self.id, self.name, self.price)
Session = sessionmaker(bind=engine)
session = Session()
results1 = session.query(Book).filter(Book.name == None).all()
print('Null Test: ')
for result in results1:
print(result)
results2 = session.query(Book).filter(Book.name != None).all()
print('Not null Test: ')
for result in results2:
print(result)
打印:
Null Test:
Book(id:11, name:None, price:33.0)
Not null Test:
Book(id:1, name:Name 1, price:45.0)
Book(id:2, name:Name 2, price:49.0)
Book(id:3, name:Name 3, price:14.0)
Book(id:4, name:Name 4, price:16.0)
Book(id:5, name:Name 5, price:17.0)
Book(id:6, name:Name 6, price:3.0)
Book(id:7, name:Name 7, price:25.0)
Book(id:8, name:Name 8, price:16.0)
Book(id:9, name:Name 9, price:32.0)
Book(id:10, name:Name 10, price:22.0)
还可以通过is_()
和isnot()
方法实现:
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy import create_engine, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_demo'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50))
price = Column(Float)
def __str__(self):
return 'Book(id:{}, name:{}, price:{})'.format(self.id, self.name, self.price)
Session = sessionmaker(bind=engine)
session = Session()
results1 = session.query(Book).filter(Book.name.is_(None)).all()
print('Null Test: ')
for result in results1:
print(result)
results2 = session.query(Book).filter(Book.name.isnot(None)).all()
print('NNot null Test: ')
for result in results2:
print(result)
and测试:
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy import create_engine, and_
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_demo'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50))
price = Column(Float)
def __str__(self):
return 'Book(id:{}, name:{}, price:{})'.format(self.id, self.name, self.price)
Session = sessionmaker(bind=engine)
session = Session()
results1 = session.query(Book).filter(Book.name == 'Name 1', Book.price <= 50).all()
print('And Test 1: ')
for result in results1:
print(result)
results2 = session.query(Book).filter(and_(Book.name == 'Name 1', Book.price <= 50)).all()
print('And Test 2: ')
for result in results2:
print(result)
results3 = session.query(Book).filter(Book.name == 'Name 1').filter(Book.price <= 50).all()
print('And Test 3: ')
for result in results3:
print(result)
打印:
And Test 1:
Book(id:1, name:Name 1, price:45.0)
And Test 2:
Book(id:1, name:Name 1, price:45.0)
And Test 3:
Book(id:1, name:Name 1, price:45.0)
and可以通过3种方式实现,显然,第一种方法更简单易用。
or测试:
from sqlalchemy import Column, Integer, String, Float
from sqlalchemy import create_engine, or_
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_demo'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50))
price = Column(Float)
def __str__(self):
return 'Book(id:{}, name:{}, price:{})'.format(self.id, self.name, self.price)
Session = sessionmaker(bind=engine)
session = Session()
results = session.query(Book).filter(or_(Book.name == 'Name 1', Book.price >= 30)).all()
for result in results:
print(result)
打印:
Book(id:1, name:Name 1, price:45.0)
Book(id:2, name:Name 2, price:49.0)
Book(id:9, name:Name 9, price:32.0)
Book(id:11, name:None, price:33.0)
or_()
方法中除了放多个过滤条件,还可以只放一个过滤条件。
在Mysql中,外键可以让表之间的关系更加紧密,SQLAlchemy也支持外键,通过ForeignKey类来实现,并且可以指定表的外键约束。
创建两个存在外键关系的表示例如下:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id'))
Base.metadata.drop_all()
Base.metadata.create_all()
运行成功即创建表。
如果查看数据库表,未发现外键,可能是因为MySQL默认存储引擎为MyISAM,不支持外键,需要改成InnoDB,在MySQL配置文件种修改示例如下:
要注意,修改之后,需要重启MySQL服务。
再添加数据:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id'))
# Base.metadata.create_all()
session = sessionmaker(bind=engine)()
user = User(username='Corley')
session.add(user)
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
查看数据库中,可以看到user和article表中都已经有插入的数据了。
因为添加了外键,此时Article的uid参数必须是User中已经有的id,如果插入Article的数据的uid在User中不存在对应的id,就会报错、不能插入。
外键约束有4种:
RESTRICTrestrict测试:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='RESTRICT'))
Base.metadata.drop_all()
Base.metadata.create_all()
session = sessionmaker(bind=engine)()
user = User(username='Corley')
session.add(user)
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
运行后,删除user表数据:
delete from user where id = 1;
打印:
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`flask_orm`.`article`, CONSTRAINT `article_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`))
显然,提示有外键约束,不能删除数据。
通过ORM删除数据库表测试:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='RESTRICT'))
Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
user = User(username='Corley')
session.add(user)
session.commit()
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
user = session.query(User).filter(User.username == 'Corley').all()[0]
session.delete(user)
session.commit()
打印:
Traceback (most recent call last):
File "D:\.virtualenvs\Test-gftU5mTd\lib\site-packages\mysql\connector\connection_cext.py", line 489, in cmd_query
raw_as_string=raw_as_string)
_mysql_connector.MySQLInterfaceError: Cannot delete or update a parent row: a foreign key constraint fails (`flask_orm`.`article`, CONSTRAINT `article_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`))
During handling of the above exception, another exception occurred:
......
raw_as_string=self._raw_as_string)
File "D:\.virtualenvs\Test-gftU5mTd\lib\site-packages\mysql\connector\connection_cext.py", line 492, in cmd_query
sqlstate=exc.sqlstate)
sqlalchemy.exc.IntegrityError: (mysql.connector.errors.IntegrityError) 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`flask_orm`.`article`, CONSTRAINT `article_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`))
[SQL: DELETE FROM user WHERE user.id = %(id)s]
[parameters: {'id': 1}]
(Background on this error at: http://sqlalche.me/e/gkpj)
显然,此时报错,不允许删除有外键约束的数据。
cascade测试:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='CASCADE'))
Base.metadata.drop_all()
Base.metadata.create_all()
session = sessionmaker(bind=engine)()
user = User(username='Corley')
session.add(user)
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
运行后,再删除user表数据:
delete from user where id = 1;
打印:
Query OK, 1 row affected (0.01 sec)
显然,此时删除成功,再查询user表:
select * from user;
打印:
Empty set (0.00 sec)
再查询article表:
select * from article;
打印:
Empty set (0.00 sec)
显然,此时删除user表中数据,article表的数据也被同步删除。
用ORM删除数据测试:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='CASCADE'))
Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
user = User(username='Corley')
session.add(user)
session.commit()
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
user = session.query(User).filter(User.username == 'Corley').all()[0]
session.delete(user)
session.commit()
运行后查询数据库与之前结果一致。
set null测试:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='SET NULL'))
Base.metadata.drop_all()
Base.metadata.create_all()
session = sessionmaker(bind=engine)()
user = User(username='Corley')
session.add(user)
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
运行后,删除user表数据:
delete from user where id = 1;
打印:
Query OK, 1 row affected (0.01 sec)
显示删除成功,此时查询user表:
select * from user;
打印:
Empty set (0.00 sec)
再查询article表:
select * from article;
打印:
+----+--------+-----------------------------+------+
| id | title | content | uid |
+----+--------+-----------------------------+------+
| 1 | Python | 人生苦短,我用Python | NULL |
+----+--------+-----------------------------+------+
1 row in set (0.00 sec)
显然,此时当删除user表中的数据,article表中与其关联的数据被设为null。
用ORM删除数据测试:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='SET NULL'))
Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
user = User(username='Corley')
session.add(user)
session.commit()
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
user = session.query(User).filter(User.username == 'Corley').all()[0]
session.delete(user)
session.commit()
运行后查询数据库与之前一致。
3.存在外键时数据的查询查询数据示例如下:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
def __str__(self):
return 'User(id: {}, username: {})'.format(self.id, self.username)
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='SET NULL'))
Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
user = User(username='Corley')
session.add(user)
session.commit()
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
article = session.query(Article).first()
uid = article.uid
user = session.query(User).get(uid)
print(user)
打印:
User(id: 1, username: Corley)
显然,这种方式先通过Article找到用户的uid,再通过uid查找用户,显得很麻烦,可以简化:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
def __str__(self):
return 'User(id: {}, username: {})'.format(self.id, self.username)
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='SET NULL'))
Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
user = User(username='Corley')
session.add(user)
session.commit()
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
user = session.query(User).filter(User.id == Article.uid).first()
print(user)
查询结果是一样的。
三、Flask数据库表关系表之间的关系有三种:
一对一 一对多 多对多SQLAlchemy中的ORM可以实现这三种关系。
1.一对多关系例如:
一个人可以有多辆车,但是一辆车只能对应一个人。
测试如下:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30), nullable=False)
articles = relationship("Article")
def __str__(self):
return 'User(id: {}, username: {})'.format(self.id, self.username)
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='SET NULL'))
author = relationship('User')
def __str__(self):
return 'User(id: {}, title: {}, content: {}, uid: {})'.format(self.id, self.title, self.content, self.uid)
Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
user = User(username='Corley')
session.add(user)
session.commit()
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
article = session.query(Article).first()
print(article)
print(article.author)
user = session.query(User).first()
print(user)
for article in user.articles:
print(article)
打印:
User(id: 1, title: Python, content: 人生苦短,我用Python, uid: 1)
User(id: 1, username: Corley)
User(id: 1, username: Corley)
User(id: 1, title: Python, content: 人生苦短,我用Python, uid: 1)
显然,此时根据文章查询作者和根据作者查询文章都能正常查询,因为作者和文章时一对多的关系,所以通过作者查询文章得到的是一个列表,可以遍历列表来获取每一篇文章;
同时,表之间的关系是建立在两个表之间存在外键约束的前提下的,如果未建立外键约束,但在两个表之间建立一对多的关系,会出错。
联合表添加单条数据测试:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30), nullable=False)
articles = relationship("Article")
def __str__(self):
return 'User(id: {}, username: {})'.format(self.id, self.username)
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='SET NULL'))
author = relationship('User')
def __str__(self):
return 'User(id: {}, title: {}, content: {}, uid: {})'.format(self.id, self.title, self.content, self.uid)
Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
user = User(username='Corley')
session.add(user)
session.commit()
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
user = User(username='Lord')
article = Article(title='Java', content='Spring')
article.author = user
session.add(article)
session.commit()
运行后,查询user表:
select * from user;
打印:
+----+----------+
| id | username |
+----+----------+
| 1 | Corley |
| 2 | Lord |
+----+----------+
2 rows in set (0.01 sec)
再查询article表:
select * from article;
打印:
+----+--------+----------------------+------+
| id | title | content | uid |
+----+--------+----------------------+------+
| 1 | Python | 人生苦短,我用Python | 1 |
| 2 | Java | Spring | 2 |
+----+--------+----------------------+------+
2 rows in set (0.00 sec)
显然,两条数据都插入成功,并形成映射关系。
添加多条数据测试:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30), nullable=False)
articles = relationship("Article")
def __str__(self):
return 'User(id: {}, username: {})'.format(self.id, self.username)
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='SET NULL'))
author = relationship('User')
def __str__(self):
return 'User(id: {}, title: {}, content: {}, uid: {})'.format(self.id, self.title, self.content, self.uid)
Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
user = User(username='Corley')
session.add(user)
session.commit()
article = Article(title='Python', content='人生苦短,我用Python', uid=1)
session.add(article)
session.commit()
user = User(username='Jessica')
article1 = Article(title='Java', content='Spring')
article2 = Article(title='C', content='Clang')
article1.author = user
article2.author = user
session.add_all([article1, article2])
session.commit()
运行后,查询user表:
select * from user;
打印:
+----+----------+
| id | username |
+----+----------+
| 1 | Corley |
| 2 | Jessica |
+----+----------+
2 rows in set (0.00 sec)
再查询article表:
select * from article;
打印:
+----+--------+----------------------+------+
| id | title | content | uid |
+----+--------+----------------------+------+
| 1 | Python | 人生苦短,我用Python | 1 |
| 2 | Java | Spring | 2 |
| 3 | C | Clang | 2 |
+----+--------+----------------------+------+
3 rows in set (0.00 sec)
显然,此时也通过一对多的映射将一个user和对应的两个article插入数据库。
之前都是通过在User和Article模型中都定义关系映射来实现一对多的关系,但是也可以只在一个模型中定义relationship来实现一对多的关系,此时需要定义反向访问属性backref,示例如下:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
# 数据库连接配置
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'root'
DATABASE = 'flask_orm'
DB_URL = 'mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URL)
Base = declarative_base(engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30), nullable=False)
articles = relationship("Article", backref='author') # 定义反向访问属性
def __str__(self):
return 'User(id: {}, username: {})'.format(self.id, self.username)
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50))
content = Column(Text, nullable=False)
uid = Column(Integer, ForeignKey('user.id', ondelete='SET NULL'))
# author = relationship('User')
def __str__(self):
return 'User(id: {}, title: {}, content: {}, uid: {})'.format(self.id, self.title, self.content, self.uid)
# Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
article = session.query(Article).first()
print(article)
print(article.author)
user = session.query(User).first()
print(user)
for article in user.articles:
print(article)
打印:
User(id: 1, title: Python, content: 人生苦短,我用Python, uid: 1)
User(id: 1, username: Corley)
User(id: 1, username: Corley)
User(id: 1, title: Python, content: 人生苦短,我用Python, uid: 1)
User(id: 4, title: Python, content: 人生苦短,我用Python, uid: 1)
显然,此时可以互相查询;
在Article模型中未定义关系,只在User模型中定义了关系,同时在定义关系时定义了backref反向访问属性,来保证不仅能通过User来查询Article,还要能通过Article来查询User。