我不知道如何在瓶请求中共享会话
I cannot figure out how to share a session in a bottle request
我已经修改了好几个小时了,但我似乎无法找到一种方法来完成这项工作。看起来应该很简单,我敢肯定,但我很困惑。
我有一个名为 'server.py' 的模块,它使用 bottle 处理所有路由,这是主要的执行点。请求处理程序的一个示例是这样的,我正在概括,因为我的代码库相当庞大,而且大部分与问题无关:
server.py
@route('home')
def home():
page = Page('home') # A template manager I made
objects = db.get_objects(10) # This is what I can't get to work
return page.render(objects=objects)
我希望服务器端的代码如此简单,所有数据库交互都在 db.py 中使用辅助函数完成,但是我想使用查询返回的对象,这些对象仍然附加到会话,因此它必须在 db.get_objects 之外关闭。应根据每个请求创建和关闭会话。我可以像这样从 home() 手动执行此操作:
server.py
@route('home')
def home():
session = Session()
page = Page('home') # A jinja template manager I made
objects = db.get_objects(session, 10)
document = page.render(objects=objects)
session.close()
return document
我不介意每次都打开和关闭会话,这似乎合乎逻辑且不可避免,无论是直接还是通过另一个 object/function,但我不想(手动)传递该会话对于每个数据库辅助函数,这对我来说似乎很混乱。
我觉得这个问题可以通过一些 OOP、会话管理器 class 或两者之间共享的东西来解决,但我不知道如何设计或共享它。到目前为止我想到的最好的想法是将我的整个 db.py 包装在 class 中并让构造函数创建会话。这适用于辅助函数,但我在 db.py 中还有一堆其他对象也需要访问会话,如下所示,这是我的代码库中的实际对象:
db.py
class Sticker(_Base):
__tablename__ = 'sticker'
sticker_id = Column(Integer, ForeignKey('product.product_id'), primary_key=True)
sticker_name = Column(String)
svg_location = Column(String, unique=True)
svg_width = Column(Numeric, nullable=False)
svg_height = Column(Numeric, nullable=False)
shaped = Column(Boolean) # Whether the cutpath countors the image
@reconstructor
def _get_related_properties(self, init=False):
'''Fetches and fills out all properties that are created by the
__init__ constructor that are not in the orm constructor.'''
if not init:
session = Session()
self._product = session.query(Product).filter(Product.product_id == self.sticker_id).first()
category_id = session.query(ProductCategory).filter(ProductCategory.product_id == self.sticker_id).first()
session.close()
self.sticker_id = self._product.product_id
self.product_type = self._product.product_type
self.date_added = self._product.date_added
self.sticker_name = self._product.product_name
def _get_svg_size(self):
"""Returns a tuple of the width, height of an svg"""
# Currently only works with pixels I think. I know it fails when saved as points.
# May want to improve this for future versions.
# May also consider moving this function to an external util file or something.
# Possible units: (~"em" | ~"ex" | ~"px" | ~"in" | ~"cm" | ~"mm" | ~"pt" | ~"pc")
# Also may use viewbox attribute to determine aspect ratio and set sizes algorithmically.
import xml.etree.ElementTree as ET
import decimal
# Set decimal precision
decimal.getcontext().prec=7
tree = ET.parse(self.svg_location)
root = tree.getroot()
width = None
height = None
width_attr = root.get('width')
height_attr = root.get('height')
# Get measurement units
units = width_attr[-2:]
if units[-1] == '%':
units = '%'
elif not units.isalpha():
# if units not set assume px
width = decimal.Decimal(width_attr)
height = decimal.Decimal(height_attr)
units = 'px'
if units != 'px':
width = decimal.Decimal(width_attr[:-2])
height = decimal.Decimal(height_attr[:-2])
# Convert to px if not already
# Currently only supports in, cm, mm, and pt
# Assumes DPI is 72
MMPI = 2.834645669291339
DPI = 72
if units == 'in':
width *= DPI
height *= DPI
elif units == 'pt':
width /= DPI
height /= DPI
elif units == 'mm':
width *= MMPI
height *= MMPI
elif units == 'cm':
width *= MMPI * 10
height *= MMPI * 10
else:
raise ValueError('Unsupported svg size unit:',units )
return width, height
def __init__(self, svg_location, name='', category='', metatags=[], sticker_sizes=None, active=False):
# If no name given use filename
if not name:
from os.path import basename
name = basename(svg_location).rstrip('.svg')
# Create parent product and save to db to generate primary key/product id
session = Session()
self._product = Product(product_name = name, product_type = 'sticker', active = active)
session.add(self._product)
session.commit()
# TODO: Handle category and metatags
# Categories should probably be created explicitly by the admin, and so should exist
# at the time of sticker creation. Metatags are more numerous and created on the fly
# and so should be created automatically by the sticker constructor.
# TODO: Expand the sticker table to reference these values from the product table maybe?
self.sticker_id = self._product.product_id
self.svg_location = svg_location
self.svg_width, self.svg_height = self._get_svg_size()
self._get_related_properties(init=True)
# Add to the Database
session.add(self)
session.commit()
# Get sticker sizes
self.sticker_sizes = []
# Check if a size tuple was added, default is empty
if sticker_sizes:
for size in sticker_sizes:
sticker_size = StickerSize(self.sticker_id, size[0], size[1])
session.add(sticker_size)
self.sticker_sizes.append(StickerSize)
session.commit()
session.close()
其中大部分并不重要,但正如您在许多情况下看到的那样,我需要从我的 ORM 映射对象中查询数据库,因此它们也需要访问会话。我希望这是一个简单的问题,我该怎么做?它甚至可以完成还是我以错误的方式接近这个?如果我处理错误怎么办,您能提供一个可行的设计模式吗?
我找到了一个解决方案,那就是将会话附加到请求对象,显然,每个请求都是唯一的,并且可以在模块之间共享。
我已经修改了好几个小时了,但我似乎无法找到一种方法来完成这项工作。看起来应该很简单,我敢肯定,但我很困惑。
我有一个名为 'server.py' 的模块,它使用 bottle 处理所有路由,这是主要的执行点。请求处理程序的一个示例是这样的,我正在概括,因为我的代码库相当庞大,而且大部分与问题无关:
server.py
@route('home')
def home():
page = Page('home') # A template manager I made
objects = db.get_objects(10) # This is what I can't get to work
return page.render(objects=objects)
我希望服务器端的代码如此简单,所有数据库交互都在 db.py 中使用辅助函数完成,但是我想使用查询返回的对象,这些对象仍然附加到会话,因此它必须在 db.get_objects 之外关闭。应根据每个请求创建和关闭会话。我可以像这样从 home() 手动执行此操作:
server.py
@route('home')
def home():
session = Session()
page = Page('home') # A jinja template manager I made
objects = db.get_objects(session, 10)
document = page.render(objects=objects)
session.close()
return document
我不介意每次都打开和关闭会话,这似乎合乎逻辑且不可避免,无论是直接还是通过另一个 object/function,但我不想(手动)传递该会话对于每个数据库辅助函数,这对我来说似乎很混乱。
我觉得这个问题可以通过一些 OOP、会话管理器 class 或两者之间共享的东西来解决,但我不知道如何设计或共享它。到目前为止我想到的最好的想法是将我的整个 db.py 包装在 class 中并让构造函数创建会话。这适用于辅助函数,但我在 db.py 中还有一堆其他对象也需要访问会话,如下所示,这是我的代码库中的实际对象:
db.py
class Sticker(_Base):
__tablename__ = 'sticker'
sticker_id = Column(Integer, ForeignKey('product.product_id'), primary_key=True)
sticker_name = Column(String)
svg_location = Column(String, unique=True)
svg_width = Column(Numeric, nullable=False)
svg_height = Column(Numeric, nullable=False)
shaped = Column(Boolean) # Whether the cutpath countors the image
@reconstructor
def _get_related_properties(self, init=False):
'''Fetches and fills out all properties that are created by the
__init__ constructor that are not in the orm constructor.'''
if not init:
session = Session()
self._product = session.query(Product).filter(Product.product_id == self.sticker_id).first()
category_id = session.query(ProductCategory).filter(ProductCategory.product_id == self.sticker_id).first()
session.close()
self.sticker_id = self._product.product_id
self.product_type = self._product.product_type
self.date_added = self._product.date_added
self.sticker_name = self._product.product_name
def _get_svg_size(self):
"""Returns a tuple of the width, height of an svg"""
# Currently only works with pixels I think. I know it fails when saved as points.
# May want to improve this for future versions.
# May also consider moving this function to an external util file or something.
# Possible units: (~"em" | ~"ex" | ~"px" | ~"in" | ~"cm" | ~"mm" | ~"pt" | ~"pc")
# Also may use viewbox attribute to determine aspect ratio and set sizes algorithmically.
import xml.etree.ElementTree as ET
import decimal
# Set decimal precision
decimal.getcontext().prec=7
tree = ET.parse(self.svg_location)
root = tree.getroot()
width = None
height = None
width_attr = root.get('width')
height_attr = root.get('height')
# Get measurement units
units = width_attr[-2:]
if units[-1] == '%':
units = '%'
elif not units.isalpha():
# if units not set assume px
width = decimal.Decimal(width_attr)
height = decimal.Decimal(height_attr)
units = 'px'
if units != 'px':
width = decimal.Decimal(width_attr[:-2])
height = decimal.Decimal(height_attr[:-2])
# Convert to px if not already
# Currently only supports in, cm, mm, and pt
# Assumes DPI is 72
MMPI = 2.834645669291339
DPI = 72
if units == 'in':
width *= DPI
height *= DPI
elif units == 'pt':
width /= DPI
height /= DPI
elif units == 'mm':
width *= MMPI
height *= MMPI
elif units == 'cm':
width *= MMPI * 10
height *= MMPI * 10
else:
raise ValueError('Unsupported svg size unit:',units )
return width, height
def __init__(self, svg_location, name='', category='', metatags=[], sticker_sizes=None, active=False):
# If no name given use filename
if not name:
from os.path import basename
name = basename(svg_location).rstrip('.svg')
# Create parent product and save to db to generate primary key/product id
session = Session()
self._product = Product(product_name = name, product_type = 'sticker', active = active)
session.add(self._product)
session.commit()
# TODO: Handle category and metatags
# Categories should probably be created explicitly by the admin, and so should exist
# at the time of sticker creation. Metatags are more numerous and created on the fly
# and so should be created automatically by the sticker constructor.
# TODO: Expand the sticker table to reference these values from the product table maybe?
self.sticker_id = self._product.product_id
self.svg_location = svg_location
self.svg_width, self.svg_height = self._get_svg_size()
self._get_related_properties(init=True)
# Add to the Database
session.add(self)
session.commit()
# Get sticker sizes
self.sticker_sizes = []
# Check if a size tuple was added, default is empty
if sticker_sizes:
for size in sticker_sizes:
sticker_size = StickerSize(self.sticker_id, size[0], size[1])
session.add(sticker_size)
self.sticker_sizes.append(StickerSize)
session.commit()
session.close()
其中大部分并不重要,但正如您在许多情况下看到的那样,我需要从我的 ORM 映射对象中查询数据库,因此它们也需要访问会话。我希望这是一个简单的问题,我该怎么做?它甚至可以完成还是我以错误的方式接近这个?如果我处理错误怎么办,您能提供一个可行的设计模式吗?
我找到了一个解决方案,那就是将会话附加到请求对象,显然,每个请求都是唯一的,并且可以在模块之间共享。