flask_sqlalchemy 에서 db. session 은 요청 간 의 독립 을 어떻게 유지 하 는 지 - 소스 읽 기 노트

14168 단어
본 고 는 주로 두 가지 문 제 를 검증 하기 위해 서 이다.
  • fllask 처리 요청 시 새 스 레 드, 프로 세 스, 협 정 을 통 해 차이 점 (추가)
  • flask_sqlalchemy 는 db. session 을 어떻게 사용 하여 여러 요청 에서 보증서 의 변 화 를 같은 표 의 sql 작업 에 서로 영향 을 주지 않 습 니까? 전문 명 사 는 세 션 범위 나 Session 역할 영역 (주요 검토)
  • 입 니 다.
    간단 한 예
    # -*- coding:utf-8 -*-
    from sqlalchemy.orm.session import Session #      
    from sqlalchemy.orm import scoped_session  #     
    
    import time
    from flask_sqlalchemy import SQLAlchemy
    from flask import Flask
    
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:[email protected]:3306/mytest?charset=utf8'
    db = SQLAlchemy(app)    # db.init_app(app)       app db     ,       ,      
    db_session = db.session
    
    
    class role(db.Model):
        id = db.Column(db.INT, primary_key=True,autoincrement=True)
        name = db.Column(db.String(99), unique=False)
        name_cn = db.Column(db.String(99), unique=False)
    
        def __init__(self, name, name_cn):
            self.name = name
            self.name_cn = name_cn
    
        def __repr__(self):
            return '' % self.name
    
    # db.create_all()
    
    @app.route('/add1')
    def add1():
        print("db.session:", vars(db_session))
        print("id(db_session)",db_session)
        test_role1 = role('supervisol', '11')
        # test_role2 = role('your try', '11')
        db_session.add(test_role1)
        #db_session.add(test_role2)
        #db.session.commit() #       
        time.sleep(60)
        return "add1"
    
    @app.route('/add2')
    def add2():
        print("db.session:",vars(db.session))
        print("id(db_session)", db_session)
        test_role1 = role('supervisol', '22')
        #test_role2 = role('your try', '22')
        db_session.add(test_role1)
        #db_session.add(test_role2)
        db_session.commit()
        time.sleep(60)
        return "add2"
    
    if __name__ == '__main__':
        app.run(threaded=True)

    세 가지 run 방식.
    #            ,         socket,          ,    。(flask          greenlet ,             ,     time.sleep        )
    # threaded                    ,          ,            。
    # processes         。
    
    """
    root@(none):# date ;curl "http://127.0.0.1:5000/add2";date
    Tue Aug 14 08:38:14 CST 2018
    add2Tue Aug 14 08:39:14 CST 2018
    
    root@(none):~# date ;curl "http://127.0.0.1:5000/add1";date
    Tue Aug 14 08:38:16 CST 2018
    add1Tue Aug 14 08:39:16 CST 2018
    
    root@(none):~# ps -T -p 8657
      PID  SPID TTY          TIME CMD
     8657  8657 pts/7    00:00:00 python
     8657  8662 pts/7    00:00:00 python
     8657  8666 pts/7    00:00:00 python
    
    """

    db. session 탐색
    # db_session   route       ,  db_session    
    #   flask_sqlalchemy.SQLAlchemy      self.session = self.create_scoped_session(session_options)       
    # return orm.scoped_session(self.create_session(options), scopefunc=scopefunc)     
    # sqlalchemy.orm.session sqlalchemy.orm.scoped_session   
    #      http://www.cnblogs.com/ctztake/p/8277372.html             session   id  
    # _app_ctx_stack.__ident_func__   
    #             https://stackoverflow.com/questions/39480914/why-db-session-remove-must-be-called
    #                      https://blog.csdn.net/yueguanghaidao/article/details/40016235
    """
    #   app     sql  
    if app is not None:
        self.init_app(app)
        
    #     ,               ,    session
    @app.teardown_appcontext
    def shutdown_session(response_or_exc):
        if app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']:
            if response_or_exc is None:
                self.session.commit()
    
        self.session.remove()
        return response_or_exc  
        
    # sqlalchemy.orm.scoping.scoped_session
    # sqlalchemy.util._collections.ScopedRegistry   
    def clear(self):
        #Clear the current scope, if any.
        try:
            del self.registry[self.scopefunc()]
        except KeyError:
            pass
    """

    바보 방법 print
    # sqlalchemy.util._collections.ScopedRegistry                      id,     session   
    ('db.session:', {'session_factory': 127.0.0.1 - - [15/Aug/2018 15:48:19] "GET /add1 HTTP/1.1" 200 -
    sessionmaker(class_='SignallingSession', autocommit=False, query_cls=, expire_on_commit=True, bind=None, db=, autoflush=True), 'registry': })
    ('id(db_session)', )
    ('1 __call__:', )
    ('2 __call__:', {})
    ('3 has:', {: })
    ('1 __call__:', )
    ('2 __call__:', {: })
    ('4 clear start:', {: })
    ('5 clear end:', {})
    
    
    ('db.session:', {'session_factory': sessionmaker(class_='SignallingSession', autocommit=False, query_cls=, expire_on_commit=True, bind=None, db=, autoflush=True), 'registry': })
    ('id(db_session)', )
    ('1 __call__:', )
    ('2 __call__:', {})
    ('1 __call__:', )
    ('2 __call__:', {: })
    127.0.0.1 - - [15/Aug/2018 15:49:29] "GET /add2 HTTP/1.1" 200 -
    ('3 has:', {: })
    ('1 __call__:', )
    ('2 __call__:', {: })
    ('4 clear start:', {: })
    ('5 clear end:', {})
    

    위의 절 차 를 총괄 하 다
    Web Server          Web Framework        SQLAlchemy ORM Code
    --------------      --------------       ------------------------------
    startup        ->   Web framework        # Session registry is established
                        initializes          Session = scoped_session(sessionmaker())
    
    incoming
    web request    ->   web request     ->   # The registry is *optionally*
                        starts               # called upon explicitly to create
                                             # a Session local to the thread and/or request
                                             Session()
    
                                             # the Session registry can otherwise
                                             # be used at any time, creating the
                                             # request-local Session() if not present,
                                             # or returning the existing one
                                             Session.query(MyClass) # ...
    
                                             Session.add(some_object) # ...
    
                                             # if data was modified, commit the
                                             # transaction
                                             Session.commit()
    
                        web request ends  -> # the registry is instructed to
                                             # remove the Session
                                             Session.remove()
    
                        sends output      

    포인트 가 왔 습 니 다.
    sqlalchemy python     orm  ,  sqlalchemy    django   orm     ,
      flask sqlalchemy      django       。
                
    
    app.config['SQLALCHEMY_ECHO'] = True =》    sql  
    app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True =》  request    db.session.commit(),
                    db.session.add,   db.session.commit,    ,           。
        app.teardown_appcontext    
            @teardown
            def shutdown_session(response_or_exc):
                if app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']:
                    if response_or_exc is None:
                        self.session.commit()
                self.session.remove()
                return response_or_exc
    response_or_exc    ,   sys.exc_info()[1]
      self.session.remove()           self.session,        ?
         sqlalchemy session   。
    from sqlalchemy.orm import sessionmaker
    session = sessionmaker()
           sessionmaker()        session,   session         ,       
      ,sqlalchemy   scoped_session,       scoped_session           
                   session
    from sqlalchemy.orm import scoped_session, sessionmaker
    session = scoped_session(sessionmaker())
    
         scoped_session             
    class scoped_session(object):
        def __init__(self, session_factory, scopefunc=None):
            
            self.session_factory = session_factory
            if scopefunc:
                self.registry = ScopedRegistry(session_factory, scopefunc)
            else:
                self.registry = ThreadLocalRegistry(session_factory)
    __init__ ,session_factory   session     , sessionmaker       (      __call__ 
      ) scopefunc             ,        ThreadLocalRegistry
    class ThreadLocalRegistry(ScopedRegistry):
        def __init__(self, createfunc):
            self.createfunc = createfunc
            self.registry = threading.local()
     
        def __call__(self):
            try:
                return self.registry.value
            except AttributeError:
                val = self.registry.v
       __call__    ,        session,          ,      __call__       ?
    def instrument(name):
        def do(self, *args, **kwargs):
            return getattr(self.registry(), name)(*args, **kwargs)
        return do
     
    for meth in Session.public_methods:
        setattr(scoped_session, meth, instrument(meth))
            ,     session.query     getattr(self.registry(), 'query'),self.registry()  
      __call__   ,   flask_sqlalchemy      ThreadLocalRegistry,  scoped_session    
    # Which stack should we use?  _app_ctx_stack is new in 0.9
    connection_stack = _app_ctx_stack or _request_ctx_stack
     
        def __init__(self, app=None,
                     use_native_unicode=True,
                     session_options=None):
            session_options.setdefault(
                'scopefunc', connection_stack.__ident_func__
            )
            self.session = self.create_scoped_session(session_options)
     
        def create_scoped_session(self, options=None):
            """Helper factory method that creates a scoped session."""
            if options is None:
                options = {}
            scopefunc=options.pop('scopefunc', None)
            return orm.scoped_session(
                partial(_SignallingSession, self, **options), scopefunc=scopefunc
            )
        scopefunc    connection_stack.__ident_func__, connection_stack  flask app   ,
                  __ident_func__           thrading.get_ident,     id
        ScopedRegistry     _   
    class ScopedRegistry(object):
        def __init__(self, createfunc, scopefunc):
            self.createfunc = createfunc
            self.scopefunc = scopefunc
            self.registry = {}
     
     
        def __call__(self):
            key = self.scopefunc()
            try:
                return self.registry[key]
            except KeyError:
                return self.registry.setdefault(key, self.createfunc())
          ,         id     session  ,            flask_sqlalchemy 
       , flask cookie,g       ,        ?
    1.flask_sqlalchemy    ThreadLocalRegistry?
                  ,   wsgi        greenlet        
    2.  create_scoped_session partial    ?
              scoped_session session_factory      , _SignallingSession      __call__,    partial  
    
                     self.session.remove(),       session     
    
          db.relationship lazy   ,     
    class Role(db.Model):
        __tablename__ = 'roles'
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(64), unique=True)
        users = db.relationship('User', backref='role', lazy='dynamic')
     
     
    class User(db.Model):
        __tablename__ = 'users'
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(64), unique=True, index=True)
        role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
      role        Role   
    lazy:dynamic => role.users    User   ,     sqlalchemy.orm.dynamic.AppenderBaseQuery  
                       role.users.all()       sql,             
    
    lazy:select => role.users    User     ,       sql
    
      :db.session.commit               update
    

    레 퍼 런 스
    https://stackoverflow.com/questions/39480914/why-db-session-remove-must-be-called 문 제 를 야기 하 다.http://www.cnblogs.com/ctztake/p/8277372.html 모피https://blog.csdn.net/yueguanghaidao/article/details/40016235 사내 의 발자취http://docs.sqlalchemy.org/en/latest/orm/contextual.html#using- thread - local - scope - with - web - applications 문서

    좋은 웹페이지 즐겨찾기