调用 DB (psycopg2) 后如何获得平面列表?

How to get a flat list after making a call to DB (psycopg2)?

如果您只需要数据库中的一列,获得平面的最佳方法是什么 list/set?默认情况下 DB-API 总是需要 return 嵌套列表,例如:ids = [(1,). (2,), (3,),].

sqlite3row_factory 实现,也许 psycopg2 也有类似的选项?我检查了文档但没有找到。

目前,我通过 list comprehension 解包,但这导致了另一个关于代码风格的重要问题:解包的最佳和清晰位置:

  1. 调用后立即 fetchall
  2. 调用一个crud函数后,里面使用了fetchall

例如:

# Crud function, var 1
def read_user_photos(id: int, cursor) -> set:  # Var 1
    cursor.execute('SELECT photo FROM photos WHERE id = %s', (id,))
    return [photos_lst[0] for photos_lst in cursor.fetchall()]

# Some class method that uses crud, var 2
photos = [photo_lst[0] for photo_lst in read_user_photos(id, cursor)]  # Var 2
do anything_with_photos(photos=photos)

crud 函数中解包的优缺点我看到了:

  1. 会让代码更清晰
  2. 下一个遇到此代码的程序员将期望从 crud 函数中获取数据,完全按照 DB-API 的要求,嵌套列表,而不是平面列表。

我只是确保您的文档字符串和类型符合标准——您的文档字符串和类型目前有些欠缺或有误(例如,您返回的是列表,而不是集合)。

像这样的东西会通过我的代码审查(假设 photo 在这里是一个整数,只有你能确定):)

from typing import Set


def get_user_photo_ids(cursor, *, user_id: int) -> Set[int]:
    """
    Get a set of user photo IDs for a given user.
    :param cursor: Database cursor.
    :param user_id: User ID.
    """
    cursor.execute('SELECT photo FROM photos WHERE id = %s', (user_id,))
    return {row[0] for row in cursor}

此函数的用户可以很容易地针对另一个查询对其进行调整,例如(使用虚构的多语言 photo_names table)

def get_photo_names(cursor, *, photo_ids: Iterable[int], language: str) -> Dict[int, str]:
    """
    Get a mapping of photo names for ids in a given language.
    :param cursor: Database cursor.
    :param photo_ids: Photo IDs to retrieve names for. Consumes the iterable.
    :param language: The language to retrieve the names in.
    """
    cursor.execute('SELECT photo_id, name FROM photo_names WHERE photo_id IN %s AND language = %s', (tuple(photo_ids), language))
    return dict(cursor)

在 psycopg2 中,您必须对游标进行子类化。在 psycopg 3 中你可以定义一个 row factory.

>>> import psycopg
>>> from psycopg.rows import args_row
>>> cnn = psycopg.connect()

>>> def first_field(*t):
...     return t[0]

>>> cur = cnn.cursor(row_factory=args_row(first_field))
>>> cur.execute("select generate_series(1,5)").fetchall()
[1, 2, 3, 4, 5]