LINUX.ORG.RU

Архитектура database API класса

 , ,


1

3

Есть приложение на питоне, использует psycopg.

Есть класс для работы с базой, который инкапсулирует весь SQL код и предоставляет API. В частности, там есть метод для создания схемы, есть методы для чтения и записи определённых данных в/из базу. Условно это выглядит как-то так:

class Database:
    def __init__(self, dsn):
        self.db = psycopg2.connect(dsn)

    def CreateSchema(self):
        self.db.cursor().execute("CREATE TABLE foo ...")
        ...

    def GetFoo(self, id):
        cur = self.db.cursor()
        cur.execute("SELECT...JOIN...JOIN...FROM SELECT(...)...GROUP BY...ORDER BY)

        return [ MyFooObject(a=row[0], b=row[1], c=row[2] for row in cur.fetchall() ]

Этот класс уже разросся на несколько тысяч строк, поскольку методов API накопилось много, посему вопрос: какие best practices есть по разбивке его на небольшие части? Навскидку придумывается только что-то такое:

class FooAPI:
    def __init__(self, db):
        self.db = db

    def Get(self, id):
        self.db.cursor()
        cur.execute("SELECT...JOIN...JOIN...FROM SELECT(...)...GROUP BY...ORDER BY")

        return [ MyFooObject(a=row[0], b=row[1], c=row[2] for row in cur.fetchall() ]

class Database:
    def __init__(self, dsn):
        self.db = psycopg2.connect(dsn)

    def GetFooAPI(self):
        return FooAPI(db)

но как-то это топорно.

PS. ORM, естественно, не предлагать.

★★★★★

You're doing it wrong :-) Не надо никаких «инкапсуляций» врукопашную :-) Не надо никаких ORM :-) Если знаешь SQL, то используй технологию, описанную здесь :-) Как раз Python используется в качестве примера :-) Лол :-)

anonymous
()
Ответ на: комментарий от slovazap

Хотя не всё так идеально, во многих случаях SQL у меня конструируется на лету. Необходимость хранить часть SQL кода снаружи, а часть в питоновском коде меня сильно расстраивает.

PS. Пользуясь случаем, а есть какой-нибудь стандарт на отступы для наиболее читабельного SQL?

INSERT
INTO a (
    foo,
    bar,
    baz
) SELECT
    foo,
    bar,
    baz
FROM (
    SELECT
        foo,
        bar
    FROM abc
    WHERE
    GROUP BY
) AS TEMP
GROUP BY
ON CONFLICT
DO UPDATE SET
    foo = ...

Что-то мне не нравится. И как UNION выравнивать вообще не понятно.

slovazap ★★★★★
() автор топика
Ответ на: комментарий от slovazap

во многих случаях SQL у меня конструируется на лету

А впрочем по ссылке про это написано. Нужно покумекать.

slovazap ★★★★★
() автор топика
Ответ на: комментарий от slovazap

во многих случаях SQL у меня конструируется на лету

You're doing it wrong :-) Для это придумали функции на стороне сервера (аля хранимые процедуры) :-) Также тебе помогут виды :-) Динамический SQL нужен, разве что, для формирования WHERE ... :-) И даже в этом случае, этот самый WHERE можно формировать в функции на сервере (если очень уж надо) :-)

anonymous
()
Ответ на: комментарий от anonymous

You're doing it wrong :-) Для это придумали функции на стороне сервера (аля хранимые процедуры) :-) Также тебе помогут виды :-) Динамический SQL нужен, разве что, для формирования WHERE ... :-)

Материализованные виды у меня используются чтобы заранее подготовить данные для последующих select'ов, и соответственно, ускорить их.

А обычные виды, как и процедуры - всего лишь способ упростить запросы, что мне совершенно не нужно - они не настолько большие чтобы быть сложными для понимания, там нет дублирования кода, и я предпочитаю держать в схеме как можно меньше сущностей, чтобы проще было добавлять функциональность налету и иметь меньше головной боли с миграцией. Т.е. никаких моих задач процедуры и виды не решат.

А динамические запросы у меня строятся по набор фильтров, задаваемых пользователем, и фильтры эти влияют не только на where, но и в том числе на набор с'join'иваемых таблиц и наличие GROUP BY, так что как ни крути, жёстко в один или несколько запросов их не зашить.

И даже в этом случае, этот самый WHERE можно формировать в функции на сервере (если очень уж надо) :-)

Слабо представляю как формировать запросы из запросов.

slovazap ★★★★★
() автор топика
Ответ на: комментарий от true_admin

Потому что ORM применима только если база - тупое хранилище объектов. Тогда да, ORM позволяет не дублировать структуру объектов в приложении и базе, а получить одно из другого, и связать объекты приложения с базой, чтобы при изменении поля в приложении оно сразу менялось в базе.

У меня не такой случай. Нет 1:1 маппинга объектов на базу, много bulk операций и сложных запросов, оверхед ORM неприемлем, птичий DSL призванный заменить SQL не нужен вдвойне.

slovazap ★★★★★
() автор топика
Ответ на: комментарий от slovazap

Слабо представляю как формировать запросы из запросов.

Создай функцию на PL/pgSQL, которая по своим аргументам сгенерирует запрос, выполнит его, вернёт результат :-) Это не сложно и гораздо удобнее, чем формирование на стороне клиента, попробуй :-) Любишь Python? :-) В Postgres можно функции и на PL/Python писать :-) Попробуй :-)

anonymous
()

Ваше решение вполне нормально и удобно если грамотно разбить апи по смыслу на обьекты

ism ★★★
()
Ответ на: комментарий от anonymous

Создай функцию на PL/pgSQL, которая по своим аргументам сгенерирует запрос, выполнит его, вернёт результат :-)

Не знал что так можно, есть примеры?

Это не сложно и гораздо удобнее, чем формирование на стороне клиента, попробуй

Не думаю.

Любишь Python? :-) В Postgres можно функции и на PL/Python писать :-) Попробуй :-)

Нет, спасибо.

slovazap ★★★★★
() автор топика

я обычно разделяю этих «дб-менеджеров» по логике или по таблицам БД. Один класс - работа с одной сущностью.

bvn13 ★★★★★
()
Ответ на: комментарий от bvn13

По каким признакам делить-то я знаю. Меня интересовало как это лучше сделать с точки зрения питоновского интерфейса.

slovazap ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.