2013-10-07 39 views
2

我有一個基於SQLAlchemy腳手架的工作金字塔web應用程序。在我的應用程序中,我有一個通過SQLAlchemy發送電子郵件並在我的數據庫中插入/更新表的函數。在網絡上,我通過一個視圖調用這個函數,即網絡上的一個按鈕提交給一個視圖,並在視圖內調用該函數。金字塔控制檯腳本 - DBSession沒有插入?

我想創建一個控制檯腳本,調用這個相同的功能,但在計劃的基礎上。我正在通過setting up a Pyramid console script的示例文檔。在一個完美的世界中,我希望能夠訪問我在我的Web應用程序中使用的所有模型和功能,但能夠從控制檯使用它們。通過試驗和錯誤,我已經設法包含一些基礎知識以獲得某些工作,因爲我可以查詢我的一個模型對象並將其打印到控制檯。我甚至可以調用我想要的功能。

但是,在函數內部,它向數據庫寫入一行併發送一封電子郵件。當我從控制檯調用該函數時,它會完成所有工作(至少打印到控制檯)併發送電子郵件。它在它應該打印的地方打印「INSERT」語句。但它實際上不是執行INSERT或提交它們,我不確定哪個。我正在從我的models.py包中導入DBSession,其餘的金字塔應用程序使用該包,但是有什麼技巧或者我需要知道的嗎?我試着聲明一個新的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() 

另外,我也遇到了,但不是那麼重要了,現在還有一件事:我有記錄的處理程序在我的log.blah進入到數據庫(使用LogSession我已創建)。這也適用於web應用程序,但在運行時不寫入數據庫。我不知道是否是相同的問題,或者如果在我的配置中,處理程序沒有正確設置爲控制檯或其他東西。我不知道,但上面的主要問題是我正在尋找。謝謝!

編輯: 我戳了一下,發現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來處理幕後的東西。

+0

也許更具體一點是,如果我有一個工作金字塔web應用程序,我需要做些什麼來設置一個控制檯腳本,以訪問與web應用程序相同的模型和功能?我包含並根據示例成功加載我的development.ini,並導入到我的特定模型對象中。數據庫讀取工作得很好。但是,我需要額外的配置還是包含什麼? –

+0

所以......這裏還有一個問題需要回答嗎? –

+0

如果出現錯誤,它會回滾所有的東西。如果你想在部分內容中閱讀'transaction'包文檔中的保存點。 – zaquest

回答

0

據我可以告訴你永遠不會提交你的改變。

DBSession.commit() 

在您的closer函數之前調用它。它應該按照你如何使用DBSession

+0

即使我的主金字塔.models包(包括我正在使用的MyObject)使用ZopeTransactionExtension?我認爲如果需要的話,ZTE會自動處理通過DBSession.flush()提交的提交,並且我認爲在過去試圖顯式提交時遇到了SQLAlchemy問題。此外,執行更新/插入的實際數據庫事務處於由控制檯應用程序調用的「SendEmail()」函數內。還是有什麼我需要做的,以獲得相同的數據庫功能,我有我的我們的應用程序,但在控制檯腳本? –

+0

我不能確定(我不知道你如何定義'DBSession'),但是如果你使用ZTE,你應該使用'transaction'包來提交。 – zaquest

+1

@PeterTirrell是的,即使你使用ZopeTransactionExtension。不,中興通訊不會爲您自動提交任何更改,它只是將sqlalchemy會話加入到zope事務中。您可能使用'pyramid_tm'在您的Web應用中自動提交更改,但不會在您的腳本中執行。 – zaquest