Найти - Пользователи
Полная версия: Правильный CRUD репозиторий.
Начало » Python для новичков » Правильный CRUD репозиторий.
1 2 3 4 5
bootcd
Всем привет! Такой вопрос.
Писал веб-приложение (Flask) для БД. Стандартный CRUD.
Изначально реализовал (быстро, чтобы скорее запустить) работу с БД через набор классов, описывающих операции с сущностями. Классы получились просто набором статических методов типа:


 class UserRepo:
@staticmethod
def create(data):
#orm работы
@staticmethod
def get_by_id(instance_id):
#orm работы
@staticmethod
def delete(instance_id):
#orm работы

и т.д.
Пришло время делать из всего этого человеческий вид и я задался вопросом, как именно реализовать ПО ЧЕСТНОМУ такую штуку. Статические методы технически работают, но идеологически - это не очень хороший код так как по сути честнее сделать модуль с методами.
Видел решения, где делают класс с состоянием, но в методах вообще нет никакого вызова self, что тоже неправильно.
Есть ли какие-то паттерны по этому вопросу? Меня интересует не сколько готовая библиотека, а то как по честному такое проектировать.
Спасибо!
py.user.next
А для чего тебе классы-то?

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

  
import sqlite3
 
class Database:
    def __init__(self, db):
        self.db = db
    def read(self):
        return self.db.read()
 
class SQLiteDatabase:
    def __init__(self, filename):
        self.db = sqlite3.connect(filename)
    def read(self):
        result = self.db.execute('select * from tab')
        return result
 
db = Database(SQLiteDatabase('database.sqlite'))
db.read()
ZerG
Ну прежде всего ты получил какой никакой опыт. Но изобретать свой велосипед - всегда не лучший выбор.
Правильно будет использовать уже готовое решение. Сейчас у тебя куча кастомных запросов и в случае если необходимо внести малейшие изменения в модели или логике тебе все их нужно переписывать а так же дополнительно валидировать данные перед записью обрабатывать ошибки и так далее.
Так что ты подошел к моменту использования ОРМ.
Основной инструмент тут SQLAlchemy - там есть все. Но библиотека довольно таки обширна и нахрапом ее не взять.
Попробуй для начала что нибудь простое для тестов типа peewee
https://docs.peewee-orm.com/en/latest/peewee/quickstart.html#quickstart

Идея в том что ты создаешь модели (Таблицы своей БД) и работаешь с ними как с обьектами не влазя в потроха SQL запросов. И оно уже содержит все выше описанное.
В любом случае Алхимию учить прийдется.
bootcd
py.user.next
А для чего тебе классы-то?
Спасибо за ответ!
На данный момент у меня стоит не вопрос универсальности вызова методов CRUD относительно разных БД. Главный вопрос - организация библиотеки работы с предметными областями из БД.
То есть у меня есть модели, описывающие таблицы в БД. Работать напрямую с ORM вызовами через сессии во view методах - это плохой вариант. Я написал набор классов, который состоит из статических методов, как раз, чтобы инкапсулировать работу с ORM.
У каждого класса, делающего такую работу, зачастую, очень разные по сути CRUD методы. ТО есть один такой класс описывает всю возможною работу с сущностью.
Программа - база данных клиентской инфраструктуры для компании ИТ-аутсорсера.
Например, есть раздел в программе - “Серверы” и есть класс ServerRepo, который предоставляет методы получения тех или иных данных о серверах, редактирование информации в БД и удалении записей.
Сейчас такой класс ServersRepo содержит набор статических методов, по сути являясь просто НеймСпейсом.
Сама идея класса из статических методов с одно стороны решает задачу, с другой противоречит принципам ООП.
Во всех вариантах, которые я видел в учебниках, интернете и тп - везде CRUD расшит или по классам с наследование от абстрактного класса, причем в разных вариантах, где-то как у меня через статические методы, где-то с self и cls или просто набором методов на уровне модуля. Модуль с методами меня бы устроил, но так как предметных областей (разделов в программе) порядка 30, я принял решение расшивать все через свои классы (мне показалось так удобнее), которые по итогу превратились в набор статических методов CRUD + специфические методы для конкретной области.

Суть проблемы вот в чем:
1. Методы С работает с данными из формы. На ее основе создается объект ORM модели и сейвится в базу.
2. Метод U с данными из формы и id записи в БД. Апдейтит данные ОРМ модели и укладывает их по фильтру id в БД.
3. Метод R в первом случае работает с id записью - берет конкретную запись, во втором, если нам надо получить набор записей, с другими данными для фильтра, например все серверы Сервеной стойки номер 7.
4. Метод D работает с id записи в БД. Удаляет по id из БД.

По идее можно делать объект типа ServerRepo:
И в зависимости от необходимости в клиентской части укладывать в него нужное поле:
 server = ServerRepo()
server.data = form_data
server.create()

или
 server.id = id
server.new_data = form_data
server.update()

Но это плохой стиль. Задавать состояние объекта типа ServerRepo через конструктор - тогда придется передавать разный набор полей, а в инициализаторе держать поля по дефолту в None - тоже плохо.
Вот это как раз и непонятно.
Мне говорили, мол, забей и сделать через модули без классов. Но почему тогда в любом туториале на эту тему, всегда, практически, все расшивают через классы, цинично игнорируя принципы ООП?
bootcd
ZerG
Ну прежде всего ты получил какой никакой опыт. Но изобретать свой велосипед - всегда не лучший выбор.Правильно будет использовать уже готовое решение. Сейчас у тебя куча кастомных запросов и в случае если необходимо внести малейшие изменения в модели или логике тебе все их нужно переписывать а так же дополнительно валидировать данные перед записью обрабатывать ошибки и так далее.Так что ты подошел к моменту использования ОРМ.Основной инструмент тут SQLAlchemy - там есть все. Но библиотека довольно таки обширна и нахрапом ее не взять. Попробуй для начала что нибудь простое для тестов типа peeweehttps://docs.peewee-orm.com/en/latest/peewee/quickstart.html#quickstartИдея в том что ты создаешь модели (Таблицы своей БД) и работаешь с ними как с обьектами не влазя в потроха SQL запросов. И оно уже содержит все выше описанное. В любом случае Алхимию учить прийдется.

На данный момент у меня реализовано через sqlalchemy. В самом простом виде. То есть описаны таблицы, связи и внешние ключи, рестрикции и дефолты полей.
Также я использую pydantic, для валидации. То есть когда я получаю форму, он трансформируется в словарь, который дампается в pydantic модель для валидации и затем на основании ее уже создается объект ORM.
Суть проблемы я описал в большом посте выше.
Rodegast
> Сама идея класса из статических методов с одно стороны решает задачу, с другой противоречит принципам ООП.

Почему ты считаешь это проблемой?

> Но почему тогда в любом туториале на эту тему, всегда, практически, все расшивают через классы, цинично игнорируя принципы ООП?

Большая част туториалов написана “дурачками для чебурашек”, по этому не нужно из за них упарываться.
bootcd
Rodegast
Почему ты считаешь это проблемой?
Хочется научиться писать хороший код, за который не стыдно, который можно будет далее передать другому человеку на поддержку или развитие.

Rodegast
Большая част туториалов написана “дурачками для чебурашек”, по этому не нужно из за них упарываться.
Да, так в любом деле. Но тут я в некотором роде тоже чебурашка. Мои знания питона - сисадминские. То есть, написать скрипт для мониторинга, парсер логов, добавить в готовую прогу код для метрик Prometheus, дренуть ручку, что-то с нее получить, передать далее и тп.
Тут же встал вопрос о вполне конкретном приложении. Я его реализовал на чистом говнокоде изначально, чтобы быстрее запустить. И дальше решил на живом примере, так сказать, привести все это к человеческому виду. Трудно найти внятные объяснения подходов. Из доступных материалов (тех самых туториалов) толком не понятно что и куда. Мне не хватает знаний именно в плане понимания проектирования. Ощущение, что постоянно иду как по минному полю. Недавно мне подсказали, мол, посмотри в сторону такой штуки как “депенденси энджекшн”. Я почитал и понял все очень смутно. Мне явно не хватает базы понимания проблематики, которую решают паттерны проектирования, так как нет опыта. Поэтому решил вот сюда сходить.
Как бы вы решали такую задачу?
Спасибо!
Rodegast
> Хочется научиться писать хороший код, за который не стыдно

Хорошо. Как ты отличаешь хороший код от плохого?

> Мне явно не хватает базы понимания проблематики, которую решают паттерны проектирования, так как нет опыта.

У тебя нет высшего образования, по этому и не понятно. Про паттерны, внедрение зависимостей и прочие пока забудь это тебе не поможет.

> Как бы вы решали такую задачу?

То как у меня реализованы операции с БД тебе точно не подойдет. На твоём месте я бы искал аналогичные проекты на github.
py.user.next
bootcd
У каждого класса, делающего такую работу, зачастую, очень разные по сути CRUD методы. ТО есть один такой класс описывает всю возможною работу с сущностью.
А если база данных сменится с изначальной на другую, что ты делать будешь?

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

Тебе ООП для чего? Чтобы не чувствовать себя стыдно?

Надо-то применять ООП для того, чтобы получить вот это или получить вот то - получать разные эффекты полезные.

bootcd
Хочется научиться писать хороший код, за который не стыдно, который можно будет далее передать другому человеку на поддержку или развитие.
Ну вот ты классов понаписал с красивыми названиями, передал этот код человеку, а у него ситуация - сменилась база данных. Он так тебя вспоминать хорошо будет, будет думать “какой хороший программист это сделал, соблюл все ООП-принципы, такой молодец!”. Да ему будет вообще не весело! и он просто проклинать тебя будет, и себя, и всех окружающих. Досталась ему “умная” параша, с которой сделать ничего нельзя.

А что касаемо учёбы - как этому всему научиться - надо, во-первых, уяснить для себя, что ООП - это не совсем то, что тебе говорят всякие молокососы в видео на YouTube. Оно существует, но это серьёзная тема. И она нихера не для новичков. Это не то, что говорят на YouTube. Это тяжёлая хреновина. Во-вторых, тебе надо сначала пройти уровни до ООП, потому что ООП - это дополнение, а не замена. Если ты не знаешь, как битовые операции выполнять, например, то никакое ООП за тебя их не будет выполнять. Тебе всё так же придётся их выполнять, даже в ООП. Это часто является открытием таким неожиданным для всяких этих молокососов двадцатилетних. Как так?! это что, мне нужно математику вспоминать за третий класс?! а я не помню, я ЕГЭ не сдал, оно сложное, для гениев прямо! И в-третьих, большинство языков не реализует ООП полностью, поэтому ты не можешь полагаться на синтаксис языка, чтобы выразить всё. В ООП есть интерфейсы, а в питоне их нет. В ООП есть множественное наследование, а в Java его нет (хотя Java ближе всего туда по сравнению с другими языками), а в C++ вообще нихера нет, надо вообще всё в голове держать, смотря на эти обрывки кода разрозненные. Поэтому язык, который записан везде как ООП-язык, никак тебе не поможет понять и узнать ООП. Большую часть всего тебе нужно будет держать в голове при любой разработке. Поэтому ООП изучается отдельно от языков, потому что оно полное, а в языках то этого нет, то вот этого нет, то ещё чего-нибудь нет.

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

Давай так: провёл ли ты декомпозицию на объекты, перед тем как вообще классы затрагивать? Сначала нужно выяснить, какие объекты есть и что они делают, когда всё это работает. Это ещё никаких классов нет. Потому что где-то нужно допридумать объекты, где-то их разделить на части (на другие объекты), где-то их соединить (слить в один объект или агрегировать, собрав в одном объекте, не разрушая их границ и не лишая их состояний). Когда у тебя объекты все установлены и функционируют, образуя работающие механизмы, тогда ты думаешь над классами этих объектов - как всю эту массу объектов разбить на отдельные классы по общим признакам. Классы потом тоже нужно увязывать в отношения, чтобы сохранять целостность и компактность программы, устойчивость к изменениям и нововведениям.

Ну вот такое всё.
bootcd
Rodegast
>Хорошо. Как ты отличаешь хороший код от плохого?

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

Rodegast
У тебя нет высшего образования, по этому и не понятно. Про паттерны, внедрение зависимостей и прочие пока забудь это тебе не поможет.
Если вы про именно программистскую вышку - да, у меня такого нет. Я весьма бородатый админ, инженер по образованию. Но сфера разработки приложений сложнее чем скрипты - это для меня новая сфера, да.
Учебные материалы почти любые - это или “Хеловорлды” или “внедряем депенденси инджекшн” ну и все в таком духе.

Rodegast
То как у меня реализованы операции с БД тебе точно не подойдет. На твоём месте я бы искал аналогичные проекты на github.
Пробовал. К сожалению на проектах не написано “хороший код” или “плохой код”.
Там я вижу или нубский код типа моего или какую-то хтонь с дженериками, протоколами и прочими малопонятными мне на данный момент вещами.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB