psycopg2中的类内的连接和游标

时间:2018-08-17 12:19:10

标签: python psycopg2

我想在定制类中使用连接和游标类。我还想继承与连接和游标类关联的所有方法。我对此进行了一些研究,发现docs和这个question与我的问题有关。我有一些部分起作用的代码。即我可以插入和更新数据库。但是,我无法从数据库中进行选择,因为即使该行在数据库中,这样做也不会返回任何内容。这是我的代码

from datetime import datetime, timedelta
import psycopg2
import psycopg2.extensions


class DataBase():
    """A class used to create tables in the database. Inherits from 
    psycopg2.extensions.connection in order to gain access to the cursor,
    commit, close, and many other features from the pyscopg module.
    """
    def __init__(self):
        self.my_connection = psycopg2.connect(database="public", user="public",
                                  password="general", host="127.0.0.1",
                                  port="5432")
        self.my_cursor = self.my_connection.cursor()

    def query_database(self, sql_statement, *args):
        return self.my_cursor.execute(sql_statement, *args)

    def commit_query(self):
        return self.my_connection.commit()

    def fetch_one(self, sql_statement, *args):
        result = self.query_database(sql_statement, *args)
        if result is None:
            return False
        return result.fetchone()

    def fetch_all(self, sql_statement, *args):
        result = self.query_database(sql_statement, *args)
        if result is None:
            return False
        return result.fetchall()

    def __del__(self):
        self.my_cursor.close()
        self.my_connection.close()


############################################################################
class CreateTables(DataBase):
    def create_user_table(self):
        """Helper function used to create the user_table"""
        sql_statement = '''CREATE TABLE IF NOT EXISTS USERS
                (ID                 SERIAL    PRIMARY KEY,
                FIRSTNAME           TEXT      NOT NULL,
                LASTNAME            TEXT      NOT NULL,
                USERNAME            TEXT      NOT NULL UNIQUE,
                EMAIL               TEXT      NOT NULL UNIQUE,
                PASSWORD            TEXT      NOT NULL,
                DATETIMEREGISTERED  TIMESTAMP NOT NULL);'''
        user_table = DataBase.query_database(self, sql_statement)
        DataBase.commit_query(self)
        return user_table

    def create_entries_table(self):
        """Helper function used to create an entries table."""
        sql_statement = '''CREATE TABLE IF NOT EXISTS ENTRIES
                        (ID             SERIAL      PRIMARY KEY,
                        TITLE           TEXT        NOT NULL,
                        DRINK           TEXT        NOT NULL,
                        DATEOFORDER     TIMESTAMP   NOT NULL,
                        TIMETODELIVERY  TIMESTAMP   NOT NULL,
                        SETREMINDER     TIMESTAMP   NOT NULL,
                        USERID      INT REFERENCES USERS ON DELETE CASCADE);'''
        entries_table = DataBase.query_database(self, sql_statement)
        DataBase.commit_query(self)
        print("entries table created.")
        return entries_table

# test = CreateTables() This is working well
# print(test.create_entries_table())

#####################################################################

class OperateDatabase(CreateTables):

    def create_user(self, email, username, *args):
        """Helper function used to create a user"""
        sql_statement = """SELECT ID FROM USERS WHERE EMAIL = %s OR
                         USERNAME = %s;"""
        user_in_database = CreateTables.fetch_one(self, sql_statement,
                                                  (email, username,))
        print("the user in database is :>>", user_in_database)

        sql_statement2 = """INSERT INTO USERS (FIRSTNAME, LASTNAME, USERNAME,
                         EMAIL, PASSWORD, DATETIMEREGISTERED)
                         VALUES (%s, %s, %s, %s, %s, %s);"""
        if not user_in_database:
            CreateTables.query_database(self,sql_statement2, *args)
            CreateTables.commit_query(self)
            return True
        return False

data = ("Jkdai", "Jkdal", "Jkdai", "jkdai@gmail.com", "password", datetime.now())
test = OperateDatabase()
print(test.create_user("jkdai@gmail.com", "jkdai", data))
#Inserts the user the very first time implying the insert statement is working
#raises an integrity error the second time implying the select statement is not working. 
#Also the print statement defaults to false when it is supposed to return the user's id.

1 个答案:

答案 0 :(得分:2)

cursor.execute()返回供应商定义的值(在db-api规范中未指定),而对于pyscopg2,它实际上被记录为确实返回了None,因此:

def query_database(self, sql_statement, *args):
    return self.my_cursor.execute(sql_statement, *args)

def fetch_one(self, sql_statement, *args):
    result = self.query_database(sql_statement, *args)
    if result is None:
        return False
    return result.fetchone()

显然不会按您预期的那样工作。您可以改为从self.my_cursor()返回query_database(),即:

def query_database(self, sql_statement, *args):
    self.my_cursor.execute(sql_statement, *args)
    return self.my_cursor

def fetch_one(self, sql_statement, *args):
    cursor = self.query_database(sql_statement, *args)
    return cursor.fetchone()

但是该代码有一个根本缺陷,那就是它不能重入(也不是线程安全的FWIW)。实际上,您的类不应将游标存储为状态的一部分,而应一遍又一遍地重复使用(db-api游标并不意味着以这种方式使用),而应为每个操作创建一个新的游标(这是缩进的用法)。

此外,您不要依靠__del__(self)来关闭连接。 __del__()方法不是正确的C ++ / Java样式终结器,甚至在收集对象时也不建议调用该方法。实际上,尝试将数据库连接和游标包装在类中通常不是一个好主意,至少不是这样。