在给定的项目结构中,在flask应用程序中管理sqlalchemy会话的最佳方法是什么?

时间:2019-07-16 15:30:20

标签: python python-3.x flask design-patterns sqlalchemy

您好,抱歉,这是一个非常开放的问题,更像是编码建议。寻找对烧瓶项目使用sqlalchemy会话的最佳实践。找不到可以回答我问题的博客,因此请在这里提问。

我有一个烧瓶应用程序的项目结构,类似于以下内容:

---services
     --- service_1.py
     --- service_2.py
---models.py
---adapter.py
---app.py

所有业务逻辑都驻留在服务中。服务中的方法调用适配器中的方法以与数据库进行任何交互。

适配器是数据访问层,所有SQL查询都在其中进行。 Adaper代码如下:

from .models import *
class DBAdapter(object):
    def __init__(self, session):
        self._db_session = get_session() # get a sql alchemy session

    def create_customer(**kwargs):
        c = Customer(**kwargs)
        self._db_session.add(c)
        return c
    .
    .
    .
    def commit(self):
        self.session.commit()

    def close_session(self):
        self.session.close()

服务看起来像这样:

from ..adapter import DBAdapter

class Service1(object):
    def __init__(self, a):
       self.a = a
       self._db_adapter = DBAdapter()

    def do_something(self, x, y):
       if x != 10:
           self.create_something(x)
           self._db_adapter.commit()
           self._db_adapter.close_session()
           return

       self._db_adapter.create_customer(y)
       self._db_adapter.create_something_else(x)
       self._db_adapter.commit()
       self._db_adapter.close_session()
       return

现在的问题是如何在每个return语句之前不重复self._db_adapter.close_session()的情况下关闭会话。需要先在两个位置关闭一次,然后再返回条件。

是否应该以其他方式创建数据访问层DBAdapter

我想保持会话整洁并仅在服务初始化时创建它,因此不能将其放在烧瓶中的g对象中,

Flask and sqlalchemy: handling sessions

2 个答案:

答案 0 :(得分:2)

Python有上下文管理器来处理类似的事情。考虑这样的事情:

import contextlib

@contextlib.contextmanager
def db_session():
  db_adapter = None
  try:
    db_adapter = DBAdapter();
    yield db_adapter
  finally:
    if db_adapter:
      db_adapter.close_session()

现在您可以自动关闭会话:

with db_session() as session:
  customer = session.create_customer(...)
  if something is wrong:
    session.rollback()
    return  # Exits the `with` scope, so closes session.
  customer.set_name('Joe')
  session.commit()
# Here session is closed.

或者您可以考虑使用一个even more convenient pattern来为您提交/回滚的

答案 1 :(得分:2)

让会话/事务充当上下文管理器并封装来自定制/客户端程序的行为:

[BITS 16]

KERNEL_LOAD_SEGMENT     EQU 0x100

;set data segment   ; the code segment is already set by the jmp in real mode in the bootloader.stage2
    mov ax, KERNEL_LOAD_SEGMENT
    mov ds, ax

    ;hide cursor
    mov ah, 0x01
    mov cx, 0x2607
    int 0x10

    ;move cursor at top left position
    mov ah, 0x02
    xor bx, bx
    xor dx, dx
    int 0x10

    ;clear screen
    mov ah, 0x06
    xor al, al
    xor bx, bx
    mov bh, 0x07
    xor cx, cx
    mov dh, 24
    mov dl, 79
    int 0x10

    mov si, kernel_hello_world_string
    call BIOS_print_string

    ;enable A20 gate
    in al, 0x92
    or al, 2
    out 0x92, al

    ;disable interrupts
    cli

    ;load gdt
    lgdt [gdt_desc]

    ;set bit 1(32bit protected mode) in cr0
    mov eax, cr0
    or eax, 1
    mov cr0, eax

    jmp CODE_SEGMENT:main32

BIOS_print_newline:
    ; removed for clarity
    ret
BIOS_print_string:
    ; removed for clarity
    ret

kernel_hello_world_string:
    db "[KERNEL] Hello world! Entering 32-bit protected mode", 0x0


[BITS 32]

gdt_start:
gdt_null_segment:
    dq 0
gdt_code_segment:
    dw 0xFFFF
    dw 0x1000
    db 0
    db 10011010b
    db 11001111b
    db 0
gdt_data_segment:
    dw 0xFFFF
    dw 0x1000
    db 0
    db 10010010b
    db 11001111b
    db 0
gdt_end:

gdt_desc:
    db gdt_end - gdt_start
    dw gdt_start

CODE_SEGMENT equ gdt_code_segment - gdt_start
DATA_SEGMENT equ gdt_data_segment - gdt_start

print32:
    ; removed for clarity
    ret

main32:
    jmp $ ;;; WITH THIS JUMP THE REBOOTING IS GONE
    mov ax, DATA_SEGMENT
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov ebp, 0x2000
    mov esp, ebp

    mov ebx, message
    ;call print32

    jmp $

message:
    db "32 bit protected mode entry point", 0

;pad
times 512 - ($ - $$) db 0

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from contextlib import contextmanager


# at the module level, the global sessionmaker,
# bound to a specific Engine
Session = sessionmaker(bind=engine)

class DBAdapter:

    @contextmanager
    def get_session(self):
        session = Session()
        try:
            yield session
            session.commit()
        except:
            session.rollback()
            raise
        finally:
            session.close()