【问题标题】:Pyramid console script - DBSession not inserting?金字塔控制台脚本 - DBSession 没有插入?
【发布时间】:2013-10-13 13:01:07
【问题描述】:

我有一个基于 SQLAlchemy 脚手架的 Pyramid Web 应用程序。在我的应用程序中,我有一个函数可以通过 SQLAlchemy 发送电子邮件并在我的数据库中插入/更新表。在 web 上,我通过视图调用这个函数,即 web 上的一个按钮提交给一个视图,并在视图内调用该函数。

我想创建一个控制台脚本来调用这个相同的函数,但要按计划进行。我正在研究setting up a Pyramid console script 的示例文档。在一个完美的世界中,我希望能够访问我在 Web 应用程序中使用的所有模型和功能,但能够从控制台使用它们。通过反复试验,我设法包含了一些基础知识以使某些东西正常工作,因为我能够查询我的模型对象之一并将其打印到控制台。我什至可以调用我想要的函数。

但是,在函数内部,它将一行写入数据库并发送一封电子邮件。当我从控制台调用该函数时,它会完成所有工作(至少打印到控制台)并发送电子邮件。它在应该打印的地方打印“插入”语句。但它实际上并没有执行插入或提交它们,我不确定是哪个。我正在从金字塔应用程序的其余部分使用的 models.py 包中导入 DBSession,但是有什么技巧或我需要知道的吗?我尝试声明一个新的 DBSession 并将其自定义到控制台脚本,但这引发了某种“找不到映射器”错误。

在下面的示例中,为每条记录调用 SendEmail;该函数本身基本上是查找相应的记录,将一行插入另一个模型对象的数据库中,然后发送电子邮件。它作为网络应用程序的一部分非常有用。在这里的控制台端,它打印出它正在做它应该做的所有事情并发送电子邮件,但实际上并没有插入数据库记录。

这是我的控制台脚本:

# describe the script here

import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import logging
import optparse
import smtplib
from smtplib import SMTPException
import sys
import textwrap

import pyramid.paster
from pyramid.paster import bootstrap
from pyramid.request import Request

from sqlalchemy.exc import DBAPIError
from sqlalchemy import (
    or_,
    and_,
    not_,
    asc,
    desc,
    func
    )

from functions import SendEmail
from models import DBSession, LogSession, groupfinder

from models import MyObject

from pyramid.session import UnencryptedCookieSessionFactoryConfig

my_session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet')

from pyramid.config import Configurator

from sqlalchemy import engine_from_config

from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy

from zope.sqlalchemy import ZopeTransactionExtension


def main():
    description = """\
        Print the deployment settings for a Pyramid application.  Example:
        'show_settings deployment.ini'
        """
    usage = "usage: %prog config_uri"
    parser = optparse.OptionParser(
        usage=usage,
        description=textwrap.dedent(description)
    )
    parser.add_option(
        '-o', '--omit',
        dest='omit',
        metavar='PREFIX',
        type='string',
        action='append',
        help=("Omit settings which start with PREFIX (you can use this "
              "option multiple times)")
    )
    options, args = parser.parse_args(sys.argv[1:])
    if not len(args) >= 1:
        print('You must provide at least one argument')
        return 2
    config_uri = args[0]
    omit = options.omit
    if omit is None:
        omit = []

    request = Request.blank('/', base_url='http://localhost:13715/')
    env = bootstrap(config_uri, request=request)
    settings = env['registry'].settings
    pyramid.paster.setup_logging(config_uri)

    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    LogSession.configure(bind=engine)
    authn_policy = AuthTktAuthenticationPolicy(
        'itsaseekreet', callback=groupfinder)
    authz_policy = ACLAuthorizationPolicy()
    config = Configurator(settings=settings,
                          root_factory='myapp.models.RootFactory',
                          session_factory=my_session_factory)
    config.set_authentication_policy(authn_policy)
    config.set_authorization_policy(authz_policy)
    config.add_static_view('static', 'static', cache_max_age=3600)

    log = logging.getLogger(__name__)

    log.info('Starting EmailSender...')

    init_time = datetime.datetime.utcnow()

    log.info('Current datetime (UTC): {0}'.format(str(init_time)))

    items_to_process = DBSession.query(MyObject). \
        filter(and_(MyObject.startdate <= init_time,
                    MyObject.enddate >= init_time,
                    MyObject.manual_send_only == False)).all()


    for item in items_to_process:
        log.info('{0}: runtime: {1}'.format(item.description, item.send_time))
        item_url = request.route_url('itemresponse', responseid='XXXXXX')

        rtn = SendEmail(item.id, item_url)

    env['closer']()


if __name__ == '__main__':
    main()

另外,我遇​​到的另一件事,但现在不是那么重要:我有日志处理程序,我的 log.blah 进入数据库(使用我创建的 LogSession)。这在网络应用程序中也可以正常工作,但在我运行它时没有写入数据库。我不知道这是否是同一个问题,或者在我的配置中,处理程序是否没有为控制台或其他东西设置正确。我不知道,但上面的主要问题是我正在寻找的。谢谢!

编辑: 我又戳了几下,发现the tutorial talking about SQLAlchemy setup 并正在查看initializedb.py 脚本,因为它会像我想要的那样修改数据库并连接到模型。我做了import transaction 并用

包装了上面
with transaction.manager:
    items_to_process = DBSession.query(MyObject). \
        filter(and_(MyObject.startdate <= init_time,
                    MyObject.enddate >= init_time,
                    MyObject.manual_send_only == False)).all()


    for item in items_to_process:
        log.info('{0}: runtime: {1}'.format(item.description, item.send_time))
        item_url = request.route_url('itemresponse', responseid='XXXXXX')

        rtn = SendEmail(item.id, item_url)

这似乎完全符合我的要求,或者至少实际确实提交并写入数据库。我将不得不更多地使用它,因为我不确定如果被调用函数中存在数据库错误,如果它回滚整个事情,如果它提交它的一部分,或者什么,会发生什么。在函数本身内有异常处理和清理,但我认为它通常依赖于 pyramid_tm 和 Zope 来处理幕后的东西。

【问题讨论】:

  • 也许更具体地说,如果我有一个工作的金字塔网络应用程序,我需要做什么来设置一个控制台脚本,它可以访问与网络应用程序相同的模型和功能?我根据示例成功包含并加载了我的 development.ini,并导入到我的特定模型对象中。并且数据库读取工作得很好。但是我需要额外的配置或包含什么的吗?
  • 那么……这里还有问题需要回答吗?
  • 如果出现错误,它会回滚整个事情。如果您想分部分执行此操作,请阅读 transaction 包文档中的保存点。

标签: sqlalchemy pyramid


【解决方案1】:

据我所知,永远不要提交更改。

DBSession.commit()

在您的 closer 函数之前调用它。它应该根据您使用DBSession 的方式工作。

【讨论】:

  • 即使我的主要 Pyramid .models 包(包括我正在使用的 MyObject)使用 ZopeTransactionExtension?我认为中兴通讯会在需要时通过 DBSession.flush() 自动处理提交,并且我认为过去尝试显式提交时遇到了 SQLAlchemy 问题。此外,执行更新/插入的实际数据库事务在控制台应用程序调用的“SendEmail()”函数中。或者我需要做些什么来获得我在我们的应用程序中但在控制台脚本中拥有的相同数据库功能?
  • 我不能确定(我不知道你怎么定义DBSession),但是如果你使用ZTE,你应该使用transaction包提交。
  • @PeterTirrell 是的,即使您使用 ZopeTransactionExtension。不,中兴通讯不会自动为您提交任何更改,它只是将 sqlalchemy 会话加入 zope 事务。您可能使用pyramid_tm 在您的网络应用程序中自动提交更改,但它不会在您的脚本中执行。
  • 啊,那很有趣。今晚我会尝试提交;或者,既然是的,我想我在我的网络应用程序中使用了 pyramid_tm - 有没有办法将它与我的脚本集成?让两者尽可能像我想的那样相似,那就太好了。感谢您的所有帮助!
  • @PeterTirrell 我认为没有好的方法可以做到这一点。 pyramid_tm 实际上只是一个调用您的视图并处理事务的函数。直接使用transaction 会让你的代码更简单,也更容易。 pyramid_tm 专门用于处理金字塔应用程序中的请求,而不是脚本。
猜你喜欢
  • 2013-11-23
  • 2018-11-23
  • 1970-01-01
  • 2011-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-26
相关资源
最近更新 更多