Sqlalchemy 金字塔控制台脚本-DBSession未插入?

Sqlalchemy 金字塔控制台脚本-DBSession未插入?,sqlalchemy,pyramid,Sqlalchemy,Pyramid,我有一个基于SQLAlchemy脚手架的金字塔网络应用程序。在我的应用程序中,我有一个函数,可以通过SQLAlchemy发送电子邮件和插入/更新数据库中的表。在web上,我通过一个视图调用此函数,即web上的一个按钮提交到一个视图,并在视图中调用此函数 我想创建一个控制台脚本来调用同一个函数,但要按计划进行。我正在浏览的示例文档。在一个完美的世界里,我希望能够访问我在web应用程序中使用的所有模型和功能,但能够从控制台使用它们。通过反复试验,我成功地包含了一些基本的东西来让它工作,因为我能够查询

我有一个基于SQLAlchemy脚手架的金字塔网络应用程序。在我的应用程序中,我有一个函数,可以通过SQLAlchemy发送电子邮件和插入/更新数据库中的表。在web上,我通过一个视图调用此函数,即web上的一个按钮提交到一个视图,并在视图中调用此函数

我想创建一个控制台脚本来调用同一个函数,但要按计划进行。我正在浏览的示例文档。在一个完美的世界里,我希望能够访问我在web应用程序中使用的所有模型和功能,但能够从控制台使用它们。通过反复试验,我成功地包含了一些基本的东西来让它工作,因为我能够查询我的一个模型对象并将其打印到控制台。我甚至可以调用我想要的函数

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

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

以下是我的控制台脚本:

# 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()

这似乎正是我想要的,或者至少实际提交并写入数据库。我还需要进一步处理它,因为我不确定如果调用的函数中出现DB错误,如果它回滚整个过程,如果它提交了部分内容,或者发生了什么。在函数本身中有异常处理和清理,但我认为它通常依赖于pyramid_tm和Zope来处理幕后工作。

据我所知,您永远不会提交更改

DBSession.commit()

关闭
函数之前调用它。即使我的主Pyramid.models包(包括我正在使用的MyObject)使用ZopeTransactionExtension,它也应该根据您使用DBSession的方式工作?我认为如果需要,中兴通讯会自动通过DBSession.flush()处理提交,而且我认为在过去尝试显式提交时遇到了SQLAlchemy问题。此外,执行更新/插入的实际数据库事务位于console应用程序调用的“sendmail()”函数中。或者我还需要做些什么来获得与我的we应用程序中相同的db功能,但在控制台脚本中?我不能确定(我不知道如何定义
DBSession
),但是如果你使用中兴通讯,你应该使用
transaction
包来提交。@peterrell是的,即使你使用ZopeTransactionExtension。不,中兴不会自动为您提交任何更改,它只是将sqlalchemy会话加入zope事务。您可能使用
pyramid_tm
自动提交web应用程序中的更改,但不会在脚本中执行。啊,很有意思。今晚我会试着承诺;或者,既然是的,我想我在我的web应用程序中使用了pyramid_tm——有没有办法将其与我的脚本集成?如果能让这两者尽可能的相似,那就太好了。谢谢你的帮助@彼得雷尔:我认为没有好办法
pyramid_tm
实际上只是一个调用视图并处理周围事务的函数。直接使用
事务
将使您的代码更简单,也更容易
pyramid_tm
专门设计用于处理pyramid应用程序中的请求,而不是用于脚本。也许更具体地说,如果我有一个工作的pyramid web应用程序,我需要做什么来设置一个控制台脚本,以访问与web应用程序相同的模型和功能?我正在根据示例成功地包括和加载development.ini,并导入到我的特定模型对象中。数据库读取工作正常。但我需要额外的配置或包括什么吗?所以。。。这里还有一个问题需要回答吗?如果出现错误,它会将整个过程回滚。如果您想分部分进行,请阅读
事务
包文档中的保存点。
DBSession.commit()