寻找更符合Python风格的访问数据库方式

4

我有一堆遵循以下模式的Python方法:

def delete_session(guid):
    conn = get_conn()
    cur = conn.cursor()

    cur.execute("delete from sessions where guid=%s", guid)

    conn.commit()
    conn.close()

有没有更Pythonic的方法来执行原始SQL。每个方法开头和结尾的两行代码让我感到烦恼。

我不想使用ORM,我想坚持使用原始SQL。

6个回答

8
你可以编写一个上下文管理器并使用with语句。例如,请参见此博客文章(已存档)
此外,Python文档中有一个示例几乎符合您的需求。请参见此页面的8.1节,特别是以以下代码片段开头的部分:
db_connection = DatabaseConnection()
with db_connection as cursor:
    cursor.execute('insert into ...')
    cursor.execute('delete from ...')
    # ... more operations ...

3

在使用execute时需要注意,第二个参数需要是一个包含一个元素的列表 [guid]。至于你的问题,我通常会使用一个封装连接和游标的类,但看起来你更喜欢使用一个执行上下文对象,它的__enter__方法会给你一个游标,而__leave__方法则根据终止方式(正常或异常)来提交或回滚事务;这将使你的代码:

def delete_session():
    with get_cursor() as cur:
        cur.execute(etc etc)

如果您喜欢这种风格,请告诉我们,我会向您展示如何编写get_cursor。其他人可能会建议使用装饰器,这样您就可以编写:
@withcursor
def delete_session(cur):
    cur.execute(etc etc)

但我认为这会使得提交/回滚等问题变得更加复杂。不过,如果您偏好这种方式,请让我们知道,我可以向您展示如何编写这种表单。


仅使用GUID时它可以正常工作。我是否不知道任何问题? - Ben Noland
我认为我喜欢上了上下文管理器的方法。ars给出的链接已经很好地描述了它,但还是谢谢你的提供。 - Ben Noland
@Ben,cursor.execute的第二个参数必须是一个值序列(通常是元组),如果您正在使用位置参数替换(例如您的%s或其他DB API模块中的?或:1),或者是映射(通常是字典),如果您使用命名参数替换(例如在某些DB API模块中的:guid)。不确定您正在使用哪个DB API模块,但它让您使用其他形式(例如一个孤立的、非序列化、非映射的第二个参数)有点奇怪。 - Alex Martelli

3
我有一群遵循这种模式的Python方法:
这让人感到困惑。
要么你有一堆函数,要么你有一个类的一堆方法。
一堆函数。
请改用以下方式。
class SQLFunction( object ):
    def __init__( self, connection ):
        self.connection = connection
    def __call__( self, args=None ):
        self.cursor= self.connection.cursor()
        self.run( args )
        self.cursor.commit()
        self.cursor.close()

class DeleteSession( SQLFunction ):
    def run( self, args ):
        self.cursor.execute( "statement" )

delete_session = DeleteSession( connection )

你的函数声明会多两行,但本质上是相同的。 你可以使用func1( args )因为它是一个可调用对象。你程序的其余部分应保持不变。
一个类中有一堆方法。
class SomeClass( object ):
    def __init__( self, connection ):
        self.connection= connection
    def sql_execute( self, statement, args= None )
        self.cursor= self.connection.cursor() 
        self.cursor.execute( statement, args if args is not None else [] )
        self.connection.commit()
        self.cursor.close()
    def delete_session( self ):
        self.sql_execute( "statement" )

您所有的方法都可以像 delete_session 一样,并利用一个通用的 sql_execute 方法。


0

它不必更具有Python风格,只需要更加结构化:

def execSql(statement):
    conn = get_conn()
    cur = conn.cursor()
    cur.execute(statement)
    conn.commit()
    conn.close()

def delete_session(guid):
    execSql("delete from sessions where guid=%s"%(guid))

我写的一些方法需要与结果进行交互。使用这种方法似乎会很尴尬。在连接关闭后,您能否调用获取方法? - Ben Noland
不,这仅适用于单次操作。 - paxdiablo

0

装饰器?

class SqlExec:
   def __init__ (self, f):
      self.f = f
   def __call__ (self, *args):
      conn = get_conn() 
      cur = conn.cursor()
      cur.execute(self.f (*args))
      conn.commit()
      conn.close()

@SqlExec
def delete_session(guid):
      return "delete from sessions where guid=%s" % guid

0
根据文档,如果您使用的是SQLite3,您甚至不需要一个Cursor,因为文档中说它"经常是多余的"。
相反,您可以直接在连接对象上使用快捷方法executeexecutemanyexecutescript
import sqlite3

persons = [
    ("Hugo", "Boss"),
    ("Calvin", "Klein")
    ]

con = sqlite3.connect(":memory:")

# Create the table
con.execute("create table person(firstname, lastname)")

# Fill the table
con.executemany("insert into person(firstname, lastname) values (?, ?)", persons)

# Print the table contents
for row in con.execute("select firstname, lastname from person"):
    print row

print "I just deleted", con.execute("delete from person").rowcount, "rows"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接