Ш̴̴̜̥͍͕̼̙̱͙͎͍̘̀̐̔́̾̃͒̈̔̎́́͜р̧̛̺͖͖̯̖ͧͤ͋̅̽ͧ̈̐̽̆̐͋ͤͦͬ͛̃̑͞͞и̒ͥͤͯ͂ͣ̐̉̑ͫ̉̑҉̛͏̸̻͕͇͚̤͕̯̱̳͉ͅф̴̴̡̟̞͙̙̻͍̦͔̤̞̔̓́̍͗̚͢͞ͅт̨̐ͫ̂͊̄̃ͥͪ͏̫̺͍̞̼͈̩̥̜͔͜͜ы̸̴̱̺̼̠̦͍͍͍̱̖͔̖̱͉̅͑͌͒ͫ͒̀ͥ͐ͤ̅͘̕.̵̴̡̭̼̮͖͈̙͖͖̲̮̬͍͙̼̯̦̮̮ͦ̆̀̑̌ͮͧͣͯ̔̂́͟г͌ͮ̏̈͂ͯ̚҉̛̙̬̘̲̗͇͕̠̙͙̼̩͚̀͘͞ͅо̷̥̯̘̓ͤ̽͒̋̉̀̂̄̒̓̊ͨ͛́̌ͤ̂̀͠в̶̒͒̓̏̓̚҉̛̙̘̺̰̮̼̟̼̥̟̘̠̜͜н̸̷̸̲̝͈͙̰̟̻̟̰̜̟̗͎̻̻͍̿̔̃ͨ͑о̔̀̋ͫ̇̿̐ͫ͌͗ͩ҉̨̜̙̙͈͍̮̮̼̙̘̞̕͜͡ Войти !bnw Сегодня Клубы

Испытал экстаз от того, как красиво и элегантно в persistent пишется функция, которая инсертит запись в таблицу, если там такой ещё нет, и возвращает id записи (либо новой, либо найденной):

insertLanguage language = do
    let lang = Language language
    entity <- getByValue lang
    case entity of
        Nothing  ->  do insert lang
        Just e   ->  return $ entityKey e

Пять строк, пять строк! И не какого-то там мозговыносящего матана с функторами, а нечто такое, что питонист, взглянувший на это, просто подумает: «о, в третий питон ещё и стрелочки какие-то впилили, прикольно».

Тот факт, что insert возвращает id созданной записи, ранее было поводом отдельного экстаза.

#A13SMB / @minoru / 3573 дня назад

крут
#A13SMB/O10 / @sin-ok / 3573 дня назад
Олсо tfw код вообще понятнее и проще, чем попытки объяснить его на русском.
#A13SMB/CQJ / @minoru / 3573 дня назад
Было в рельсах из коробки //
#A13SMB/W2P / @anonymous / 3573 дня назад
@anonymous Покажи! А генерация и типов данных, и CREATE TABLE (да ещё и с миграцией) из деклараций типа нижеприведённой в рельсах есть? ```` Author first_name String middle_name String last_name String ````
#A13SMB/T9A / @minoru --> #A13SMB/W2P / 3573 дня назад
@anonymous Круто, спасибо. Таки есть в руби ещё что-то кроме хайпа :)
#A13SMB/T6G / @minoru --> #A13SMB/MU8 / 3573 дня назад
>>> Book.objects.get_or_create(name="Meow-meow", defaults={'author': 'John Doe', 'in_stock': 10}) джанга, либо получает объект, либо создает новый c параметрами в defaults
#A13SMB/ITL / @partizan / 3573 дня назад
@partizan Это база. Покажи мне, если интересно, аналоги getByValue и того, что я показал в /T9A.
#A13SMB/LWL / @minoru --> #A13SMB/ITL / 3573 дня назад
@minoru что значит "база"? работает с бд? да, а с чем надо? get_or_create это и есть аналог find_or_create, ну а миграции у нас тоже есть: http://south.readthedocs.org/en/latest/tutorial/part1.html
#A13SMB/464 / @partizan --> #A13SMB/LWL / 3573 дня назад
@minoru а getByValue это я там не совсем понял что это, ну типа как Book.objects.get(lang='ru') ?
#A13SMB/64Y / @partizan --> #A13SMB/LWL / 3573 дня назад
@partizan Под «базой» я имел в виду, что штука простая и, в принципе, вполне ожидаемо, что любой нормальный язык что-то такое предоставляет.
#A13SMB/12G / @minoru --> #A13SMB/464 / 3573 дня назад
@partizan Если твой get возвращает первую подходящую запись — то оно. Дополнительные очки, если у вас есть аналог getBy; ему передаётся объект, содержащий все поля, входящие в primary key, и, таким образом, результатов гарантированно не больше одного. В Haskell это проверяется с помощью типов, в Python, наверное, ты просто на меня посмотришь странным взглядом и снова скажешь «get()».
#A13SMB/3SI / @minoru --> #A13SMB/64Y / 3573 дня назад
@partizan Миграция засчитывается, да. Блин, дойдёт сейчас до того, что вы меня убедите, что всё то же самое можно писать на любом мейнстримном языке, без потери выразительности :(
#A13SMB/KBT / @minoru --> #A13SMB/464 / 3573 дня назад
@minoru на самом деле так и есть
#A13SMB/V7R / @anonymous --> #A13SMB/KBT / 3573 дня назад
@minoru мой get и get_or_create возвращает всегда один объект, т.е. ему желательно передавать primary key, если найдет больше - эксепшн. первый попавшийся `Book.objects.filter(name='meow')[0]`, типа так
#A13SMB/PA5 / @partizan --> #A13SMB/3SI / 3573 дня назад
@partizan Т. е. `получить первый или создать` нельзя? Только эксепшн обрабатывать?
#A13SMB/2JM / @anonymous --> #A13SMB/PA5 / 3573 дня назад
@anonymous ну типа да, впрочем это не так сложно ``` try: book = Book.objects.filter(name='Meow')[0] except: book = Book.objects.create(name='Meow') ``` но такого я никогда не юзал, всегда хватало get_or_create
#A13SMB/ZUV / @partizan --> #A13SMB/2JM / 3573 дня назад
@anonymous двачую славика
#A13SMB/7W9 / @238328 --> #A13SMB/W2P / 3573 дня назад
@minoru ну итт вроде простые примеры просто, надо глубже погружаться
#A13SMB/L27 / @238328 --> #A13SMB/KBT / 3573 дня назад
@anonymous нет, эксепшн это для get, в `get_or_create` так, как ты описал
#A13SMB/055 / @238328 --> #A13SMB/2JM / 3573 дня назад
@238328 >If multiple objects are found, get_or_create raises MultipleObjectsReturned.
#A13SMB/XAY / @anonymous --> #A13SMB/055 / 3573 дня назад
Какое-то ебанатство, если честно, вот этот вот getByValue. Оно что, по всем полям ищет? А что если ты добавишь колоночку "active :: Bool"? Придётся не забыть зарефакторить вот это место? Почему не выбрать явно по полю language (или code или как там).
#A13SMB/L9F / @kb / 3573 дня назад
@238328 >2017 >рубли
#A13SMB/LJK / @anonymous --> #A13SMB/055 / 3573 дня назад
@kb Подозреваю, что да, все поля тыкаются в WHERE. При наличии индекса проблемы не вижу, тем более что оптимизатор может взять поля, составляющие PK, и поискать сначала по ним — для ключа наверняка есть индекс. Если добавлю колонку, то рефакторить придётся по-любому; это проблема ADT в общем. Про понимается под «выбрать по полю явно»? Там таблица из одного поля, в котором коды языков.
#A13SMB/P88 / @minoru --> #A13SMB/L9F / 3573 дня назад
@minoru Ну, и что будет происходить, если ты добавишь колонку "name"? Подозреваю, или потенциально создашь несколько языков с одинаковым кодом но разным именем, или будешь получать ошибку т.к. индекс и все дела. Но это ведь хуёво, не?
#A13SMB/89L / @kb --> #A13SMB/P88 / 3573 дня назад
@kb Код сейчас primary key, я не могу несколько записей с одинаковым кодом создать. Куда ты клонишь?
#A13SMB/NZE / @minoru --> #A13SMB/89L / 3573 дня назад
@anonymous (( // логично
#A13SMB/D88 / @238328 --> #A13SMB/XAY / 3573 дня назад
@minoru Клоню туда, что твой код начнёт бросать эксепшн и рисовать ошибку. То есть будешь делать ненужные действия по обработке этих ошибок. В случае выборки лишь по нужным колонкам ты не будешь делать ненужную работу и захламлять код ненужной логикой.
#A13SMB/LGS / @kb --> #A13SMB/NZE / 3572 дня назад
@kb Ну и изначально ты хотел вот это: - если запись существует -- вернуть её id А получишь вот это: - если запись существует -- вернуть id или ошибку То есть программа работает не так, как ты хочешь, кароч.
#A13SMB/84R / @kb --> #A13SMB/LGS / 3572 дня назад
@kb Не начнёт, потому что если я добавлю в таблицу новое поле, то мне придётся и getByValue поменять (потому что тайпчекер скажет, что я передаю в конструктор Language на один параметр меньше, чем требуется). getByValue следует понимать как «найдите-ка мне вот такую строку в таблице». С точки зрения реляционных баз данных это действительно ебланство, считается, что спрашивать нужно по-другому — «найдите-ка мне строчку с вот таким вот primary key». Для этого есть getBy. Я сначала не понял разницы и юзал getByValue потому, что это проще. Сейчас я вообще понял, что делаю лишнюю работу, и переписал вот так: ````haskell insertLanguage language = do result <- insertBy $ Language language case result of Left duplicate -> return $ entityKey duplicate Right key -> return key ````
#A13SMB/6GQ / @minoru --> #A13SMB/LGS / 3572 дня назад
@minoru Вся суть пользователей статически-типизированных языков: делай хуйню лишь бы компилировалась. // В то время как какой-нибудь питонист бы написал `ensure_by`, которая бы избавила от дублирований логики на каждый чих, и не требовала бы рефакторингов при изменениях моделей.
#A13SMB/7Y7 / @kb --> #A13SMB/6GQ / 3572 дня назад
@kb Потом этот питонист добавил бы в PK ещё одно поле и начал бы получать ошибки в рантайме. Давай не будем начинать здесь этот срачик, ок? Тем более что ты не показываешь мне полный код, а только говоришь о каких-то там функциях. Мне самому за тебя код писать?
#A13SMB/37K / @minoru --> #A13SMB/7Y7 / 3572 дня назад
@minoru Вот тебе копипаста с стековерфлоу: ``` def get_or_create(session, model, **kwargs): instance = session.query(model).filter_by(**kwargs).first() if instance: return instance else: instance = model(**kwargs) session.add(instance) return instance ``` Используется примерно так: ``` get_or_create(session, Language, code="en") ``` От количества добавленных примари/юник и прочих полей (как и моделей) этот метод меняться не будет.
#A13SMB/CAN / @kb --> #A13SMB/37K / 3572 дня назад
@kb Окей, getByValue сделали. Что с getBy? Ты можешь написать метод, который будет уверен, что ему передали все поля из PK?
#A13SMB/CL0 / @minoru --> #A13SMB/CAN / 3572 дня назад
@minoru Что ты имеешь в виду под "будет уверен"? Будет уверен когда?
#A13SMB/0C6 / @kb --> #A13SMB/CL0 / 3572 дня назад
@kb В compile time.
#A13SMB/WGX / @minoru --> #A13SMB/0C6 / 3572 дня назад
@minoru пук.
#A13SMB/DTP / @ulidtko --> #A13SMB/WGX / 3572 дня назад
@ulidtko пукнул во время компиляции
#A13SMB/ZXF / @238328 --> #A13SMB/DTP / 3572 дня назад
@minoru > Ты можешь написать метод, который будет уверен, что ему передали все поля из PK? Блять, у меня (в питоновой части проекта, т.к. я-то как раз пишу процентов 90 на хаскеле) модель строится динамически из базы данных, не то что такие пустяки. А у тебя хаскель проверяет во время компиляции соответствие хаскелевых описаний индексам описаниям внутри БД?
#A13SMB/SBX / @kb --> #A13SMB/WGX / 3572 дня назад
@kb s/описаний индексам/описаний индексов/
#A13SMB/ZAR / @kb --> #A13SMB/SBX / 3572 дня назад
@kb Нет. Persistent отталкивается от того, что база создана им же из описания, предоставленного программистом. Проверяется относительно этого описания (причём в основном тайпчекером). Если схема базы почему-то изменится, persistent мигрирует её обратно к той, которую он хочет. А как ты в своём проекте убеждаешься, что после добавления нового поля в PK ты отрефакторил все места, где ты по этому PK что-то выбирал?
#A13SMB/T33 / @minoru --> #A13SMB/ZAR / 3572 дня назад
@minoru Так же как и убеждаюсь что код выборки вообще вызвался в нужных мне моментах. Вот как ты убеждаешься, что выборка вообще вызвалась?
#A13SMB/2QK / @kb --> #A13SMB/T33 / 3572 дня назад
@kb Но погоди, тот факт, что select вызвался, вовсе не значит, что он вернул единственную запись. Ты добавляешь поле в PK, а выбираешь только по части ключа, следовательно, можешь получить больше одного результата, а get вернёт тебе только первый. При этом ты будешь думать, что выбираешь по PK. Вызывается в нужных случаях или нет можно проверить тестами (не у меня, но в теории). А вот сколько тестов нужно нагородить (и сколько тестовых данных сгенерировать), чтобы покрыть все возможные проколы с выборкой по части ключа?
#A13SMB/H2G / @minoru --> #A13SMB/2QK / 3572 дня назад
@kb Да. Заодно научишься не играть часами ;)
#A13SMB/Q0I / @minoru --> #A13SMB/2QK / 3572 дня назад
@minoru Бывает нужно выбрать первый, бывает нужно выбрать максимум один, бывает нужно чтоб он всегда был один, бывает что нужно чтобы "сумма продуктов в корзине была меньше 1000 долларов". Это уже зависит от того, что тебе нужно.
#A13SMB/Y4L / @kb --> #A13SMB/H2G / 3572 дня назад
@minoru Это я к тому что мне вполне может быть нужно выбирать по части ключа. С чего ты взял что обязательно по всему ключу выбирать?
#A13SMB/79W / @kb --> #A13SMB/H2G / 3572 дня назад
@kb Потому что в этом треде мы обсуждаем не твои требования, а мою ситуацию. В первом посте, по-моему, достаточно чётко описано, что мне нужно чтобы результатов было не больше одного. Так всё-таки, как тестировать? Городить кучу тестов и данных на все случаи?
#A13SMB/830 / @minoru --> #A13SMB/79W / 3572 дня назад
@minoru Из этого ещё не понятно, что делать если результатов больше или меньше одного, потому не достаточно. Тестировать -- ну я тестирую просто как сценарий общий, т.к. всё равно ж руками будешь потом этот же сценарий кликать (если теста не будет). Ну и потом только то, где не уверен. К сожалению, типы совсем не спасают и не дают "компилируется -- значит работает". Просто разработку, читабельность и поддерживаемость ускоряют.
#A13SMB/GO9 / @kb --> #A13SMB/830 / 3571 день назад
ipv6 ready BnW для ведрофона BnW на Реформале Викивач Котятки

Цоперайт © 2010-2016 @stiletto.