SQLAlchemy 学习笔记

news2024/11/17 1:25:15

通信类型:AF_INET
协议家族一般是表示TCP通信的SOC_STREAM和UDP通信的SOCK_DGRAM。对于TCP通信,建立socket连接,:

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

连接socket,

    s.connect((host,port))

socket通信建立连接后,利用它发送接收数据,python提供了两种方式:socket对象和文件类对象

socket对象提供了操作系统的send()、sendto()、recv()、recvfrom()方法,文件对象提供了read()、write()、readline()方法。

SQLAlchemy 学习笔记

SQLAlchemy是Python界的ORM(Object Relational Mapper)框架,它两个主要的组件: SQLAlchemy ORMSQLAlchemy Core

架构图

####安装

pip install SQLAlchemy
#检查安装是否成功:  
>>> import sqlalchemy
>>> sqlalchemy.__version__
0.8.0

没有报错就代表正确安装了,连接MySQL数据库(需要MySQLdb支持):

from sqlalchemy import create_engine
DB_CONNECT_STRING = 'mysql+mysqldb://root:@localhost/test2?charset=utf8'
engine = create_engine(DB_CONNECT_STRING,echo=True)

create_engine方法返回一个Engine实例,Engine实例只有直到触发数据库事件时才真正去连接数据库,如执行:

engine.execute("select 1").scalar()

执行上面的语句是,sqlalchemy就会从数据库连接池中获取一个连接用于执行语句。echo=True是回显命令,sqlalchemy与数据库通信的命令都将打印出来,例如:

2014-12-28 01:00:29,078 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE ‘sql_mode’
2014-12-28 01:00:29,079 INFO sqlalchemy.engine.base.Engine ()
2014-12-28 01:00:29,080 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
2014-12-28 01:00:29,081 INFO sqlalchemy.engine.base.Engine ()
2014-12-28 01:00:29,083 INFO sqlalchemy.engine.base.Engine show collation where `Charset` = ‘utf8’ and `Collation` = ‘utf8_bin’
2014-12-28 01:00:29,083 INFO sqlalchemy.engine.base.Engine ()

####声明一个映射(declare a Mapping)

declarative_base类维持了一个从类到表的关系,通常一个应用使用一个base实例,所有实体类都应该继承此类对象

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

现在就可以创建一个domain类

from sqlalchemy import Column,Integer,String

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer,primary_key=True)
    name = Column(String)
    fullname = Column(String)
    password = Column(String)   #这里的String可以指定长度,比如:String(20)

    def __init__(self,name,fullname,password):
        self.name = name
        self.fullname = fullname
        self.password = password
    
    def __repr(self):
        return "<User('%s','%s','%s')>"%(self.name,self.fullname,self.password)

Base.metadata.create_all(engine)  

sqlalchemy 就是把Base子类转变为数据库表,定义好User类后,会生成Tablemapper(),分别通过User.__table__User.__mapper__返回这两个对象,对于主键,象oracle没有自增长的主键时,要使用:

from sqlalchemy import Sequence
Column(Integer,Sequence('user_idseq'),prmary_key=True)

Base.metadata返回sqlalchemy.schema.MetaData对象,它是所有Table对象的集合,调用create_all()该对象会触发CREATE TABLE语句,如果数据库还不存在这些表的话。

####创建Session

Session是真正与数据库通信的handler,你还可以把他理解一个容器,add就是往容器中添加对象

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)

#创建完session就可以添加数据了  
ed_user = User('ed','Ed jone','edpasswd')
session.add(ed_user)

#也可以使用session.add_all()添加多个对象 
#session.add_all([user1,user2,user3])

print ed_user in session  # True
session.rollback()
print ed_user in session # False

执行完add方法后,ed_user对象处于pending状态,不会触发INSERT语句,当然ed_uesr.id也为None,如果在add方后有查询(session.query),那么会flush一下,把数据刷一遍,把所有的pending信息先flush再执行query。

####对象状态
对象实例有四种状态,分别是:

  1. Transient(瞬时的):这个状态的对象还不在session中,也不会保存到数据库中,主键为None(不是绝对的,如果Persistent对象rollback后虽然主键id有值,但还是Transient状态的)。
  2. Pending(挂起的):调用session.add()后,Transient对象就会变成Pending,这个时候它还是不会保存到数据库中,只有等到触发了flush动作才会存在数据库,比如query操作就可以出发flush。同样这个时候的实例的主键一样为None
  3. Persistent(持久的):session中,数据库中都有对应的一条记录存在,主键有值了。
  4. Detached(游离的):数据库中有记录,但是session中不存在,对这个状态的对象进行操作时,不会触发任何SQL语句。

####查询
Query对象通过Session.query获取,query接收类或属性参数,以及多个类

for instance in session.query(User).order_by(User.id)
    print instance.name

for name,fullname in session.query(User.name,User.fullname):
    print name,fullname

# TODO

filter_by接收的参数形式是关键字参数,而filter接收的参数是更加灵活的SQL表达式结构:

# sqlalchemy源码对filter_by的定义
def filter_by(self, **kwargs):
# 举例:
for user in session.query(User).filter_by(name=’ed’).all():
    print user

for user in session.query(User).filter(User.name==”ed”).all():
    print user

####常用过滤操作:

  • equals

      query.filter(User.name == 'ed')
    
  • not equal

      query.filter(User.name !='ed')
    
  • LIKE

      query.filter(User.name.like('%d%')
    
  • IN:

      query.filter(User.name.in_(['a','b','c'])
    
  • NOT IN:

      query.filter(~User.name.in_(['ed','x'])
    
  • IS NULL:

      filter(User.name==None)
    
  • IS NOT NULL:

      filter(User.name!=None)
    
  • AND

      from sqlalchemy import and_
      filter(and_(User.name == 'ed',User.fullname=='xxx'))    
    

    或者多次调用filter或filter_by

      filter(User.name =='ed').filter(User.fullname=='xx')
    

    还可以是:

      query.filter(User.name == ‘ed’, User.fullname == ‘Ed Jones’)
    
  • OR

      from sqlalchemy import or_
      query.filter(or_(User.name == ‘ed’, User.name == ‘wendy’))
    

对比一下Django:Django中ORM的filter方法里面只有一个等号,比如:

Entry.objects.all().filter(pub_date__year=2006)

#####查询返回结果

  • query.all(),all()返回列表
  • query.first():返回第一个元素
  • query.one()有且只有一个元素时才正确返回。

此外,filter函数还可以接收text对象,text是SQL查询语句的字面对象,比如:

for user in session.query(User).filter(text(“id<224”)).order_by(text(“id”)).all():
    print user.name

####count
有两种count,第一种是纯粹是执行SQL语句后返回有多少行,对应的函数count(),第二个是func.count(),适用在分组统计,比如按性别分组时,男的有多少,女的多少:

session.query(User).filter(User.name==’ed’).count()
session.query(func.count(), User.name).group_by(User.name).all( )

####Relattionship
SQLAlchemy中的映射关系有四种,分别是一对多,多对一,一对一,多对多
#####一对多(one to many)
因为外键(ForeignKey)始终定义在多的一方.如果relationship定义在多的一方,那就是多对一,一对多与多对一的区别在于其关联(relationship)的属性在多的一方还是一的一方,如果relationship定义在一的一方那就是一对多.
这里的例子中,一指的是Parent,一个parent有多个child.

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer,primary_key = True)
    children = relationship("Child",backref='parent')

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer,primary_key = True)
    parent_id = Column(Integer,ForeignKey('parent.id'))

#####多对一(many to one)
这个例子中many是指parent了,意思是一个child可能有多个parent(父亲和母亲),这里的外键(child_id)和relationship(child)都定义在多(parent)的一方

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    child_id = Column(Integer, ForeignKey('child.id'))
    child = relationship("Child", backref="parents")

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)

为了建立双向关系,可以在relationship()中设置backref(详情参考),Child对象就有parents属性.设置 cascade= 'all',可以级联删除

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer,primary_key = True)
    children = relationship("Child",cascade='all',backref='parent')

def delete_parent():
    session = Session()
    parent = session.query(Parent).get(2)
    session.delete(parent)
    session.commit()

不过不设置cascade,删除parent时,其关联的chilren不会删除,只会把chilren关联的parent.id置为空,设置cascade后就可以级联删除children

#####一对一
一对一就是多对一和一对多的一个特例,只需在relationship加上一个参数uselist=False替换多的一端就是一对一:
从一对多转换到一对一:

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    child = relationship("Child", uselist=False, backref="parent")

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))

从多对一转换到一对一:

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    child_id = Column(Integer, ForeignKey('child.id'))
    child = relationship("Child", backref=backref("parent", uselist=False))

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)

#####多对多
多对多关系需要一个中间关联表,通过参数secondary来指定,

from sqlalchemy import Table,Text
post_keywords = Table('post_keywords',Base.metadata,
        Column('post_id',Integer,ForeignKey('posts.id')),
        Column('keyword_id',Integer,ForeignKey('keywords.id'))
)

class BlogPost(Base):
    __tablename__ = 'posts'
    id = Column(Integer,primary_key=True)
    body = Column(Text)
    keywords = relationship('Keyword',secondary=post_keywords,backref='posts')
        
class Keyword(Base):
    __tablename__ = 'keywords'
    id = Column(Integer,primary_key = True)
    keyword = Column(String(50),nullable=False,unique=True)

####关联查询(query with join)
简单地可以使用:
for u, a in session.query(User, Address).filter(User.idAddress.user_id).filter(Address.email’lzjun@qq.com’).all():
print u, a
如果是使用真正的关联SQL语法来查询可以使用:

session.query(User).join(Address).filter(Address.email==”lzjun@qq.com”).all()

因为这里的外键就一个,系统知道如何去关联
####relationship()API
relationship()函数接收的参数非常多,比如:backref,secondary,primaryjoin,等等。列举一下我用到的参数:

  • backref:在一对多或多对一之间建立双向关系,比如:

      class Parent(Base):
          __tablename__ = 'parent'
          id = Column(Integer, primary_key=True)
          children = relationship("Child", backref="parent")
      
      class Child(Base):
          __tablename__ = 'child'
          id = Column(Integer, primary_key=True)
          parent_id = Column(Integer, ForeignKey('parent.id'))
    

    Prarent对象获取children,parent.children,反过来Child对象可以获取parent:child.parent.

  • lazy:默认值是True,说明关联对象只有到真正访问的时候才会去查询数据库,比如有parent对象,只有知道访问parent.children的时候才做关联查询.

  • remote_side:表中的外键引用的是自身时,如Node类,如果想表示多对一的关系,那么就可以使用remote_side

      class Node(Base):
          __tablename__ = 'node'
          id = Column(Integer, primary_key=True)
          parent_id = Column(Integer, ForeignKey('node.id'))
          data = Column(String(50))
          parent = relationship("Node", remote_side=[id])
    

    如果是想建立一种双向的关系,那么还是结合backref:

      class Node(Base):
      __tablename__ = 'node'
      id = Column(Integer, primary_key=True)
      parent_id = Column(Integer, ForeignKey('node.id'))
      data = Column(String(50))
      children = relationship("Node",
                  backref=backref('parent', remote_side=[id])
              )
    
  • primaryjoin:用在一对多或者多对一的关系中,默认情况连接条件就是主键与另一端的外键,用primaryjoin参数可以用来指定连接条件 ,比如:下面user的address必须现address是一’tony’开头:

      class User(Base):
          __tablename__ = 'user'
          id = Column(Integer, primary_key=True)
          name = Column(String)
      
          addresses = relationship("Address",
                          primaryjoin="and_(User.id==Address.user_id, "
                              "Address.email.startswith('tony'))",
                          backref="user")
      
      class Address(Base):
          __tablename__ = 'address'
          id = Column(Integer, primary_key=True)
          email = Column(String)
          user_id = Column(Integer, ForeignKey('user.id'))
    
  • secondary:

  • order_by:
    在一对多的关系中,如下代码:

      class User(Base):
      # ....
      addresses = relationship("Address",
                       order_by="desc(Address.email)",
                       primaryjoin="Address.user_id==User.id")
    

    如果user的address要按照email排序,那么就可以在relationship中添加参数order_by.这里的参数是一字符串形式表示的,不过它等同于python表达式,其实还有另一种基于lambda的方式:

      class User(Base):
      # ...
      addresses = relationship(lambda: Address,
                       order_by=lambda: desc(Address.email),
                       primaryjoin=lambda: Address.user_id==User.id)
    

#####association_proxy
associationproxy是sqlalchemy扩展包里面的一个函数,是一个无关痛痒的提供便捷性的功能,在多的言语也不及官方文档的例子,还是看看文档吧.

#####column_propety
可以用column_property来实现SQL表达式作为映射类的属性(另外一种方式就是用hybrid),

对比Django中的ORM:
Django 中提供了三种通用的数据库关系类型,many-to-one,many-to-many,one-to-one,

many-to-one:
用ForeignKey来定义多对一的关系,假设一个员工只能隶属于一个部门,但部门可以有多个员工,则可以:

class Department(models.Model):
    ...

class Employee(models.Model):
   department = models.ForeignKey(Department)
   ...

如果一个对象和自身有多对一的关系,则可以是:

models.ForeignKey('self'):

class Employee(models.Model):
    manager = models.ForeignKey('self')

many-to-many:
用ManyToManyField来定义多对多的关系,假设Blog可以有多个Tag,一个Tag也可以在多篇Blog里面,那么就可以用ManyToManyField

class Blog(models.Model):
    tags = ManyToManyField(Tag)
class Tag(models.Model):
    ....

同样可以通过ManyToManyField(‘self’)和自身建立多对多的关系.

one-to-one:
用OneToOneField来定义一对一的关系

相比较而言,django的orm可谓简单很多,但是性能方面未必优于sqlalchemy,不同点,sqlalchemy的model需要指定id,而django会自动帮你生成id.

####Session
Session 使用 connection发送query,把返回的result row 填充到一个object中,该对象同时还会保存在Session中,Session内部有一个叫 Identity Map的数据结构,为每一个对象维持了唯一的副本。primary key 作为 key ,value就是该object。
session刚开始无状态,直到有query发起时。

对象的变化会被session的跟踪维持着,在数据库做下一次查询后者当前的事务已经提交了时,it fushed all pendings changes to the database.
这就是传说中的 Unit of work 模式

例如:

def unit_of_work():
    session = Session()
    album = session.query(Album).get(4)
    album.name = "jun"   #这里不会修改album的name属性,不会触发update语句

def unit_of_work():
    session = Session()
    album = session.query(Album).get(4)
    album.name = "jun"   #这里修改了album的name属性,会触发一个update语句
    session.query(Artist).get(11)
    session.commit()

####构造了session,何时commit,何时close
规则:始终保持session与function和objecct分离

####transaction scope 和 session scope

#####对象的四种状态
对象在session中可能存在的四种状态包括:

  • Transient :实例还不在session中,还没有保存到数据库中去,没有数据库身份,想刚创建出来的对象比如User(),仅仅只有mapper()与之关联
  • Pending :用add()一个transient对象后,就变成了一个pending对象,这时候仍然没有flushed到数据库中去,直到flush发生。
  • Persistent :实例出现在session中而且在数据库中也有记录了,通常是通过flush一个pending实例变成Persistent或者从数据库中querying一个已经存在的实例。
  • Detached:一个对象它有记录在数据库中,但是不在任何session中,
Hibernate中的Session

SessionFactory创建Session,SessionFactory是线程安全的,而Session是线程不安全的。Session是轻量级的,创建和删除都不需要耗太大的资源,这与JDBC的connection不一样,Connection的创建时很好资源的。
Session对象内部有一个缓存,称之为Hibernate第一级缓存,每个session实例都有自己的缓存,存放的对象是当前工作单元中加载的对象。
Hibernate Session 缓存三大作用:

  1. 减少数据库的访问频率,提高访问性能
  2. 保证缓存的对象与数据库同步,位于缓存中的对象称为持久化对象
  3. 当持久化对象存在关联时,session保证不出现对象图的死锁

####Session什么时候清理缓存

  1. commit()方法调用的时候
  2. 查询时会清理缓存,保证查询结果能反映对象的最新状态
  3. 显示调用session的flush方法

####Querying

q = session.query(SomeMappedClass)

session的query方法就可以创建一个查询对象,

def add_before_query():
    session = Session()
    ed_user = User(name='zhangsan')
    session.add(ed_user)
    user = session.query(User).filter_by(name='zhangsan').first()
    print ed_user == user

这里的ed_user == user 返回True,session中会根据用主键作为key,object作为vlaue缓存在session中

def test1():
    session = Session()
    jack = session.query(User).filter_by(name='lzjun').one()
    print jack
    print jack.addresses

默认sqlalchemy 使用的时懒加载的模式,查询user的时候,并不会查询user.addresses,只有真正使用user.addresses的时候
才会触发user.addresses的查询语句。

from sqlalchemy.orm import subqueryload
def subquery_load_test():
    session = Session()
    jack = session.query(User).\
            options(subqueryload(User.addresses)).\
            filter_by(name='lzjun').one()
    print jack

使用subqueryload操作,饿汉式加载,查询user的时候,就把addresses查询出来了。

####传统映射
用Table构建一个table metadata,然后通过映射函数mapper与User关联起来

from sqlalchemy import Table,Metadata
metadata = Metadata()

user = Table('user',metadata,
        Column('id',Integer,primary_key = True),
        )
class User(object):
    def __init__(self,name):
        self.name = name
mapper(User,user)

等价于:

class User(Base):
    id = Column(Integer,primary_key = True)
    name = Column(String)
    def __init__(self,name):
        self.name = name

###使用加载策略(懒加载,饿加载)
SQLAlchemy 默认使用 Lazy Loading 策略加载对象的 relationships。因此,如果你在对象 detached 之后访问对象的 relationships,会报 “DetachedInstanceError” 错误。例如:

user = session.query(User).get(id)
_session.close()
print user.comments # this will raise DetachedInstanceError
如果你需要在对象 detach 后访问 relationships(例如需要跨进程共享对象),则应该使用 Eager Loading 策略:

session.query(User).options(joinedload(‘comments’)).get(id)
_session.close()
print user.comments # OK
如果需要加载所有的 relationships ,可以设置 Default Loading Strategies :

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer,primary_key = True)
    children = relationship("Child",backref='parent')

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer,primary_key = True)
    parent_id = Column(Integer,ForeignKey('parent.id'))

在one的那端设置了backref后,反过来就是多对一,在保存child时不需要显示的保存parent

def save_child():
    parent = Parent()
    child1 = Child(parent = parent)
    child2 = Child(parent = parent)
    child3 = Child(parent = parent)
    session = Session()
    session.add_all([child1,child2,child3])
    session.flush()
    session.commit()

设置 cascade= 'all',可以级联删除

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer,primary_key = True)
    children = relationship("Child",cascade='all',backref='parent')

def delete_parent():
    session = Session()
    parent = session.query(Parent).get(2)
    session.delete(parent)
    session.commit()

不过不设置cascade,删除parent时,其关联的chilren不会删除,只会把chilren关联的parent.id置为空,设置cascade后就可以级联删除children

####Session
Session 使用 connection发送query,把返回的result row 填充到一个object中,该对象同时还会保存在Session中,Session内部有一个叫 Identity Map的数据结构,为每一个对象维持了唯一的副本。primary key 作为 key ,value就是该object。
session刚开始无状态,直到有query发起时。

对象的变化会被session的跟踪维持着,在数据库做下一次查询后者当前的事务已经提交了时,it fushed all pendings changes to the database.
这就是传说中的 Unit of work 模式

例如:

def unit_of_work():
    session = Session()
    album = session.query(Album).get(4)
    album.name = "jun"   #这里不会修改album的name属性,不会触发update语句

def unit_of_work():
    session = Session()
    album = session.query(Album).get(4)
    album.name = "jun"   #这里修改了album的name属性,会触发一个update语句
    session.query(Artist).get(11)
    session.commit()

####构造了session,何时commit,何时close
规则:始终保持session与function和objecct分离

####transaction scope 和 session scope

#####对象的四种状态
对象在session中可能存在的四种状态包括:

  • Transient :实例还不在session中,还没有保存到数据库中去,没有数据库身份,想刚创建出来的对象比如User(),仅仅只有mapper()与之关联
  • Pending :用add()一个transient对象后,就变成了一个pending对象,这时候仍然没有flushed到数据库中去,直到flush发生。
  • Persistent :实例出现在session中而且在数据库中也有记录了,通常是通过flush一个pending实例变成Persistent或者从数据库中querying一个已经存在的实例。
  • Detached:一个对象它有记录在数据库中,但是不在任何session中,
Hibernate中的Session

SessionFactory创建Session,SessionFactory是线程安全的,而Session是线程不安全的。Session是轻量级的,创建和删除都不需要耗太大的资源,这与JDBC的connection不一样,Connection的创建时很好资源的。
Session对象内部有一个缓存,称之为Hibernate第一级缓存,每个session实例都有自己的缓存,存放的对象是当前工作单元中加载的对象。
Hibernate Session 缓存三大作用:

  1. 减少数据库的访问频率,提高访问性能
  2. 保证缓存的对象与数据库同步,位于缓存中的对象称为持久化对象
  3. 当持久化对象存在关联时,session保证不出现对象图的死锁

####Session什么时候清理缓存

  1. commit()方法调用的时候
  2. 查询时会清理缓存,保证查询结果能反映对象的最新状态
  3. 显示调用session的flush方法

session.query(User).options(joinedload(’*’)).get(id)
_session.close()
print user.comments # OK
print user.posts # OK

####Relattionship

#####一对多 (one to many)

mapping class link to table metadata

print session.query(func.count(User.id)).all()
print session.query(func.count(User.id)).first()
print session.query(func.count(User.id)).scalar()


all()返回的是list,[(10,)]
first()返回的是tuple,(10,),就是all()里面的的第0个元组
scalar()返回的就是单一值,元组中的第0个值,而且scalar只使用于当前返回的是单个值,比如all()里面返回的10

####Classic mapping

from sqlalchemy import Table, MetaData
from sqlalchemy.orm import mapper
metadata = MetaData()
subject = Table('subject', metadata,
            Column('id', Integer, primary_key=True),
            Column('title', String(100))
        )
class Subject(object):
    def __init__(self, name):
        self.name = name
metadata.create_all(engine)  #生成数据库表
mapper(Subject,subject)   #建立映射

####Hybrid Attributes 混合属性
属性在类和实例上有特殊的行为

from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, aliased
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method

Base = declarative_base()

class Interval(Base):
    __tablename__ = 'interval'

    id = Column(Integer, primary_key=True)
    start = Column(Integer, nullable=False)
    end = Column(Integer, nullable=False)

    def __init__(self, start, end):
        self.start = start
        self.end = end

    @hybrid_property
    def length(self):
        print self
        return self.end - self.start

    @hybrid_method
    def contains(self,point):
        return (self.start <= point) & (point < self.end)

    @hybrid_method
    def intersects(self, other):
        return self.contains(other.start) | self.contains(other.end)

这个hybrid_property和python中的@property有什么区别呢?区别就在这个hybrid上,既然是混合的属性,也就是说,既可以作为实例属性
也可以作为类属性,

if __name__ == '__main__':
    Base.metadata.create_all(engine)
    i = Interval(5, 10)
    print i.length
    print Interval.length

输出结果是:

<__main__.Interval object at 0x9cfdd8c> ----
5
<class '__main__.Interval'> ----
interval."end" - interval.start

而Interval.length的type是

<class 'sqlalchemy.sql.expression.BinaryExpression'>

那它有什么用呢?

Session().query(Interval).filter(Interval.length > 10)

它的用法看起来跟属性start、end一样的,而你却无需在数据库中像start、end一样定义一个字段,多好。

那@hibrid_method有什么用呢? ,如果是判断point是不是contains,直接:

i.contains(7) 

就好了啊,干嘛要用@hibrid_method呢?

Session().query(Interval).filter(Interval.contains(15))

看到了吧,和hybrid_property有相似之处

####区别于属性的表达式装饰器

from sqlalchemy import func

class Interval(object):
    # ...

    @hybrid_property
    def radius(self):
        return abs(self.length) / 2

    @radius.expression
    def radius(cls):
        return func.abs(cls.length) / 2

这里为什么还要用radius.expression呢,对于查询:

Session().query(Interval).filter(Interval.radius > 5)

直接像length一样不行吗?当然不行,不信,注释掉radius.expression试试。

TypeError: bad operand type for abs(): 'BinaryExpression'

其实它接收的是一个sqlahclemy里面的函数,func.abs,因为这里面使用的length也是一个hybrid的属性

@length.setter
def length(self, value):
    self.end = self.start + value

也支持setter

####mapping class inheritance hierarchies

使用memecache做缓存的时候,出现了错误:读取一篇article,异常信息:

DetachedInstanceError: Parent instance <Article at 0xb22da4c> is not bound to a Session; lazy load operation of attribute 'user' cannot proceed

访问代码:

 session = DBSession()
    @cache_region('long_term')
    def func_to_cache(session):
        article = session.query(Article).get(articleId)
        return article

    article = func_to_cache(session)

这段代码的意思相当于:

article = mc.get(key)
if not article:
    article = session.query(Article).get(articleid)
    mc.set(key, article)

因为Article类还关联了user

userId = Column('user_id', IdDataType, ForeignKey(
    'user_profile.user_id'), nullable=False)
user = relationship(UserProfile)

默认SQLAlchemy的实体是使用Lazyload模式,也就是说只有真正访问article.user的时候才会去数据库查询该用户,而这里事先把article缓存起来了,在访问article的时候延迟查询所使用的session跟原对象的关联被切断了。没法触发sql查询了。可以使用 eager loading,通过joinedload(),
http://docs.sqlalchemy.org/en/latest/orm/inheritance.html
http://docs.sqlalchemy.org/en/latest/orm/loading.html

####错误总结:
1.用column_property()函数做为类属性的时候:

Article.recommendCnt = column_property(select([func.count(ArticlePGoal.id)]).where(and_( 
                Article.id == ArticlePGoal.articleId, 
                ArticlePGoal.artType == ArticlePGoal.ART_TYPE_RECOMMEND, 
                Article.id == ArticlePGoal.articleId, 
                ~Article.isDeleted)))  

抛出的异常:

InvalidRequestError: Select statement 'SELECT count(article_pgoal.id) AS count_1 
FROM article_pgoal, article 
WHERE article.id = article_pgoal.article_id AND article_pgoal.art_type = %s AND article.id = article_pgoal.article_id AND NOT article.is_deleted' returned no FROM clauses due to auto-correlation; specify correlate(<tables>) to control correlation manually.

关键看错误信息的最后一句,它告诉我们需要手动指定关联的表

Article.recommendCnt = column_property(select([func.count(ArticlePGoal.id)]).where(and_( 
                Article.id == ArticlePGoal.articleId, 
                ArticlePGoal.artType == ArticlePGoal.ART_TYPE_RECOMMEND, 
                Article.id == ArticlePGoal.articleId, 
                ~Article.isDeleted)).correlate(Article.__table__))
  1. 使用memecache做缓存的时候,异常:

    DetachedInstanceError: Instance <Article at 0xb3b239ec> is not bound to a Session; attribute refresh operation cannot proceed

可以设置 session.expire_on_commit = False

####列与数据类型
http://docs.sqlalchemy.org/en/rel_0_9/core/types.html
BigInteger对应数据库中的BIGINT
Boolean对应BOOLEAN或SAMLLINT,Python端是True或False
Date对应datetime.date()对象
DateTime就是datetime.datetime()对象
Float
Integer
Interval对应datetime.timedelta(),在数据库中如果是PostgreSQL对应本地的INTERVAL类型,其它数据库用date保存.(相对于1970,1,1)
Text继承自String,在SQL中使用CLOB或TEXT,一般没有长度
Time datetime.time()
Unicode继承String,有length参数
UnicodeText

####映射类继承层次
SQLAlchemy支持三种形式的继承,单表继承, 多个类对应单独的一个表,具体表继承:

####Querying
http://docs.sqlalchemy.org/en/rel_0_9/orm/query.html#sqlalchemy.orm.query.Query.join

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2049354.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PostWigger的xss漏洞

文章目录 Lab: Exploiting DOM clobbering to enable XSS Lab: Exploiting DOM clobbering to enable XSS 这是一道dom破坏题。 首先进入&#xff0c;发现都是一个个博客。 随便点击看看。 发现是一篇文章之后是一些评论以及咱们也可以发布评论。这里的Email使用了html的正…

Redis的缓存淘汰策略

1. 查看Redis 最大的占用内存 打开redis配置文件, 设置maxmemory参数&#xff0c;maxmemory 是bytes字节类型, 注意转换 2. Redis默认内存多少可以用 注意: 在64bit系统下&#xff0c; maxmemory 设置为 0 表示不限制Redis内存使用 3. 一般生产上如何配置 一般推荐Redis 设置内…

微信小程序骨架屏

骨架屏是常用的一种优化方案&#xff0c;针对于页面还未加载完时给用户的一种反馈方式。如果自己要写骨架屏有点复杂因为页面的元素过多且不稳定&#xff0c;这边直接使用微信开发工具生成骨架屏。也不只有微信开发工具有像常用的抖音开发工具&#xff0c;字节开发工具都有对应…

Python自准直仪双筒望远镜光学ABCD矩阵行为算法

&#x1f3af;要点 &#x1f3af;平面&#xff1b;曲面&#xff1b;圆柱面&#xff1b;非球面光&#xff0c;双凸透镜&#xff1b;90 度棱镜&#xff1b;分束立方体&#xff0c;双透镜棱&#xff1b;镜分光镜光线&#xff1b;横置隔膜&#xff1b;全内反射&#xff1b;多个分束…

【Django开发】前后端分离django美多商城项目第1篇:欢迎来到美多 项目主要页面介绍【附代码文档】

本教程的知识点为&#xff1a; 项目准备 项目准备 配置 1. 修改settings/dev.py 文件中的路径信息 2. INSTALLED_APPS 3. 数据库 用户部分 图片 1. 后端接口设计&#xff1a; 视图原型 2. 具体视图实现 用户部分 使用Celery完成发送 判断帐号是否存在 1. 判断用户名是否存在 后…

看图学sql之sql 中的窗口函数

数据分析社区直达 免费数据分析资料下载。定期分享数据分析领域的最新动态、实战案例、技术工具评测、数据可视化技巧以及行业洞察报告。

【Arduino】ATmega328PB 单片机初始化配置,连接使用配置 arduino

总览 1.下载资料 2.配置 arduino 首选项 3.配置开发板管理器 4.配置不同 晶振频率 的 mega328PB 的参数设置 一、下载资料 1.你也可以看着资料自己来弄&#xff0c;如果嫌我麻烦 网盘&#xff1a;https://pan.baidu.com/s/13FCKXE8t_AZeixcR_bEhXg 提取密码&#xff1a;123…

从Linux内核探索 Socket 的本质

目录 一、引言 二、Socket 的概念 三、Socket 的使用场景 四、Socket 的设计 五、提供 Socket 层 六、Socket 如何实现网络通信 &#xff08;一&#xff09;建立连接 &#xff08;二&#xff09;数据传输 七、Socket 怎么实现“继承” 八、总结 一、引言 相信大家刚…

[Zer0pts2020]Can you guess it?1

打开题目 看到信息随便输入一个数&#xff0c;显示错误 查看源代码 看到php代码&#xff0c;代码审计 <?php include config.php; // FLAG is defined in config.php if (preg_match(/config\.php\/*$/i, $_SERVER[PHP_SELF])) { exit("I dont know what you are t…

以node / link文件表征的道路网络-----dijkstra算法yyds-----基于南京公路公开数据做路径规划(上)

前文已经基于公开数据&#xff0c;获得了南京的全域高速公路的路网数据&#xff0c;这些以node / link文件表征的道路网络不仅延续了osm地图中所包含的经纬度、名称、容量等信息 &#xff0c;还包含了一个重要的道路等级字段 “link_type_name”。 交通部门一般以高速公路、国…

ThinkPHP的SQL注入漏洞学习

目录 漏洞环境 漏洞概要 函数学习 call_user_func函数 mplode函数 漏洞分析 漏洞修复 攻击总结 漏洞环境 漏洞存在于 Builder 类的 parseData 方法中。由于程序没有对数据进行很好的过滤&#xff0c;将数据拼接进 SQL 语句&#xff0c;导致 SQL注入漏洞 的产生。 漏洞…

Shell参考 - Linux Shell 训练营

出品方<Linux.cn & 阿里云开发者学堂> 一&#xff0c;Linux 可以划分为以下四个部分&#xff1a; 1. 应用软件 2. 窗口管理软件 Unity Gnome KDE 3. GNU 系统工具链 Software- GNU Project - Free Software Foundation 4. Linux 内核 二&#xff0c;什么是shell 1. L…

一款免费开源电脑流量监控软件,电脑流量统计工具!

TrafficMonitor是一个开源的网络速度监控工具&#xff0c;它能够在Windows平台上以悬浮窗的形式显示当前的网速、CPU和内存使用情况。该工具支持多种显示模式&#xff0c;包括悬浮窗和任务栏显示&#xff0c;并且允许用户更换不同的皮肤来自定义外观样式。此外&#xff0c;Traf…

【MySQL】数据库基础(库的操作)

目录 一、MySQL安装、连接、修改密码操作 二、库的操作 2.1 创建数据库 2.2 字符集和校验规则 2.3 操控数据库 2.4 修改数据库 2.5 删除数据库 2.6 数据库的备份和恢复 2.7 查看连接情况 前情提要&#xff1a; 我的服务器操作系统是Ubuntu20.04&#xff0c;安装的是M…

eNSP 华为远程访问路由器

华为远程访问路由器 前提&#xff1a;主机能与路由器通信就行&#xff0c;如果不同网段就配路由协议&#xff0c;这里直接模拟直连通信 Cloud&#xff1a; R&#xff1a; <Huawei>sys [Huawei]sys R [R]int g0/0/0 [R-GigabitEthernet0/0/0] [R-GigabitEthernet0/0/0]i…

Vue50 todolist自定义事件版本

代码 MyFooter.vue <template><div class"todo-footer" v-show"total"><label><!-- <input type"checkbox" :checked"isAll" change"checkAll"/> --><input type"checkbox" v…

【html】颜色随机产生器(补充包)

上一篇文章我们讲了如何制作一个通过滑动产色纯色背景的网页&#xff0c;今天&#xff0c;我们对那个网页进行一个补充&#xff0c;&#xff08;&#xff09; 因为很多人在设计网页的时候没有颜色的灵感这个时候我们我们就可以考虑通过随机产生一种颜色并且能够实时看到效果的…

Delphi5实现密码、姓名生成器

文章目录 目的效果图密码生成器类类定义成员函数 点击“密码生成”事件名字生成器类类的成员功能概述注意点 点击“姓名生成”事件点击“清空”事件“导出txt”事件“备注”输入框画图软件完整代码 目的 写这个程序的目的是生成一个密码和用于快递的名字&#xff08;生成密码和…

keepalived详细讲解

keepalived&#xff1a; Keepalived是一个基于VRRP&#xff08;‌Virtual Router Redundancy Protocol&#xff0c;‌虚拟路由冗余协议&#xff09;‌协议实现的LVS&#xff08;‌Linux Virtual Server&#xff09;‌服务高可用方案。‌它的主要作用是进行虚拟路由的故障切换&…

算法打卡 Day24(二叉树)-二叉搜索树的最近公共祖先 + 二叉搜索树中的插入操作 + 删除二叉搜索树中的节点

文章目录 Leetcode 235-二叉搜索树的最近公共祖先题目描述解题思路 Leetcode 701-二叉搜索树中的插入操作题目描述解题思路 Leetcode 450-删除二叉搜索树中的节点题目描述解题思路 Leetcode 235-二叉搜索树的最近公共祖先 题目描述 https://leetcode.cn/problems/lowest-comm…