Спасибо за видео! P.S. Есть еще пару пожеланий: 1. Хотя бы раз в 3 урока было бы неплохо проводить манипуляции с html что бы наглядно было видно для чего мы все это делали. 2. подумал, столкнувшись с этой проблемами, что если вы даете "домашку" (а это полезно), то в следующем видео ее показывали
Хм, userbookrelation__like, это как бы userbookrelation__set, только к одному полю like и мы подбираемся к лайкам через foreign key в userbookrelation модели. А можно (но не нужно) найти юзеров которые поставили лайк и посчитать их) Book.objects.get(name="abc").readers.filter(userbookrelation__like=True).count() Спасибо за урок
На ночь глядя, пришло вот такое решение последней задачи: Book.objects.annotate(price_with_discount=F('price')-F('discount')) . Что-то решила discount == models.DecimalField(...), чтобы с приведением типов данных не морочиться.
а как аннотировать только 1 элемент ? например у меня есть тест, где я проверяю test_get, но с выдачей 1 элемента по id если перед конструкцией с annotate сделать get(id=...) , то выдает ошибку нашел решение сделать через filter(id=...) но в сериализатор передавать books[0] , иначе тоже не работает
Отлично,спасибо! По возможности больше "фич", "хаков" и "штучек" в коде ), этого очень не хватает при изучении, и мало кто делиться подобными вещами. Вы случайно на Java не пишите? Вот бы подобный курс по Spring MVC )
Стараюсь чтобы все было просто и с большим функционалом. Фичи и хаки не так часто получается сделать в тестовых условиях, тут как-бы нет реальных проблем, которые нужно хакать. На Java не пишу, к сожалению
Спасибо немного сложнее, но очень интересно. Жаль, что сейчас мало смотрят, но может? Нигде не могу найти ответ почему PyCharm упорно не хочет делать автоимпорт. Я уже даже и временную полную версию установил, все равно предлагает импорт только собственных моделей, а стороннее игнор. Поддержка молчит как в танке.
Первое что приходит в голову , не настроен virtual env или pip env . Но если делать как на видео то должен работать . Возможно проблема в ОС. У вас windows?
Почему то возникает ошибка при создании юзеров в тестах : ОШИБКА: повторяющееся значение ключа нарушает ограничение уникальности "auth_user_username_key" DETAIL: Ключ "(username)=(user1)" уже существует.
Что-то не так в конфгурации тестов. Они должно каждый раз на новой базе проходить. Можно попробовать удалить базу данных проекта и почистить базы, если какие-то еще лишние тестовые есть
Такая же история. И именно в тесте сериализатора! Почистил базу, но после прогона зашел проверить и тест создал в базе трех пользователей и ещё две книги. Не могу понять почему так происходит.
Здравствуйте, спасибо за курс, также спасибо, что учите как правильно писать тесты, прививаете эту хорошую привычку нам с самого начала обучения!! У меня вопрос: оптимально ли мое решение для сложного задания, а именно, я создал булевое поле discount в модели Book, и если оно True тогда annotate считает price with discount: Book.objects.all().annotate(price_w_discount=Case(When(discount=True, then=F('price')-100))) price_w_discount создается в сериализаторе
Здравствуйте ! Рад что курс понравился! Вроде бы, решение хорошее. Единственное, я бы вынес число 100 в переменную , куда-то. Лучше в самом qs использовать переменные , чтобы не хардкодить
Спасибо! Очень ценная информация. Аннотация - это красиво! Правильно ли я понял, что аннотация нужна, что бы не делать дополнительные поля в самой базе данных (лайк для каждой книги и пр.)?
Да тут сериализатор и апи не важны . Тут именно фишка в том что вычисления должны быть в базе , остальное можно по разному делать . Можно создать метод в менеджере или не создавать . Главное правильно queryset написать . Он может быть во вью или в любом другом месте .
@@SeniorPomidorDeveloper Спс. В любом случае данное задание помогло мне совместить пред-ие знания об annotate and F(). Почему-то запомнила раньше,что annotate используется в основном только по отношению к связанной через FK таблице, а F() - это очень приближённый к базе данных и соот-но быстрый способ изменить сразу ВСЕ значения в колонке таблицы (например: всем товарам сделать скидку, всем сотрудникам повысить зарплату). Однако совмещение этих методов оказалось применимо внутри одной таблицы . Ну и пришлось в очердной раз понять глубину своего невежества, прочитав статью об ORM : под девизом "simple queries - Managers, complex queries - QuerySets " (medium.com/@jairvercosa/manger-vs-query-sets-in-django-e9af7ed744e0). + ещё нашла кучу неведомых зверей типа OuterRef, Subquery in docs, которые тоже ждут своей очереди.
Извините за предидущий огромный комментарий (который удалился), вроде решил проблему, так и не понял в чем дело, переписал файлы serializers и test_serializers и заработало. Где то была ошибка которую я так и не нашел )
Да, этот ютюб постоянно удаляет комментарии, не знаю почему. Иногда из-за ссылок, иногда просто так. Хорошо что проблема решилась . Если что могу ссылку прислать на репозиторий
@@SeniorPomidorDeveloper так я с вашего GitHub и взял код, долго сравнивал со своим, так и не нашел отличия ) В итоге вставил ваш код в эти файлы, так как свой я редактировал в попытках найти причину. Видимо опять где то одна буква или запятая делала всю погоду) Чем плох Python это форматирование кода через табы и пробелы, в итоге даже неверный отступ в один пробел может сломать код.
Для этого есть редакторы кода с подсветкой синтаксиса. В языках программирования, где нужно использовать разные скобочки вместо отступов, еще легче допустить ошибку.
@@U7116-k7d в пайчарме есть функция сравнить два файла или сравнить файл с буфером обмена, удобно, можно копировать чужой код в буфер и сравнивать со своим файлом
может кто подскажет, я как то поменял название таблицы ALTER TABLE books_shop_book RENAME TO books_shop; и потом django при отработке url выдает такую ошибку: django.db.utils.ProgrammingError: ОШИБКА: отношение "books_shop_book" не существует. Я так понял, что старое отношение осталось и как можно "безболезненно" поменять название таблицы ?) При чем если меняю название таблицы обратно на "books_shop_book", то все окей, django видит эту таблицу. Что то мне подсказывает, что лучше не менять название таблицы, судя по различным источникам, но все же хочется до истины докопаться. Думал кешируются где данные эти, почистил в __pycache__ все, что было связано с БД, но, увы, это результат не принесло. Видимо, где то хранится настройка..
Вы можете в момент создания модели указать db_table , и тогда можно будет его менять когда хотите и модель должна продолжать работать . Но если имя таблицы создано автоматически то лучше уже не не переименовывать
Мдя, с annotate у меня как то совсем грустно. вообще ни чо не понял, где вообще можно почитать как строить такую мудрёную конструкцию как на 16:17, тут получается типа match - case c условием, но как узнать что так можно было? от фонаря такую конструкцию не напишешь... Когда-то мне казалось что лямбда и рекурсия это что то запутанное, но теперь я понимаю что то был просто сахар, на уровне детского сада.
Я тоже это в голове не держу . Точно не напишу по памяти такой кейс с Case, When, then=1. Просто я примерно представляю возможности ORM и когда есть подобная задача то начинаю гуглить как через ORM это сделать. Тут задача это главное. Без нее я бы и не подумал копаться в этом .
добрый день почитал документацию и нашел про Sum, написал: books = Book.objects.all().annotate( annotated_likes=Count(Case(When(userbookrelation__like=True, then=1))), rating=Avg('userbookrelation__rate'), price_discount=Sum(F('price')-F('discount'), output_field=FloatField())).order_by('id') но оно выдает лютую дичь, погуглил про F не нашел ответ на свой впорос. Что в данном случае делает функция F? и как правильно делается дз?
а зачем суммировать в price_discount? и ещё, меня так напугали FloatField в отношении денежных расчётов, что я вместо них повсеместно применяю поле DecimalField
В вашей реализации лайки получаются неуникальными. Один пользователь может миллион раз лайкать одну книгу. А для того чтоб убрать лайк по факту нужно передавать "False". Причем когда несколько "True" от этого пользователя, то получится каша))
@@SeniorPomidorDeveloper я решил эту проблему, добавив в модель мета класс с "UniqueConstraint(fields=['user', 'book'], name='unique-like')" тесты проходит, больше одного лайка не ставит. надеюсь, что это хорошее решение)
@@SeniorPomidorDeveloper я немного дальше пошел. Сделал так, чтобы при отправке запроса лайк менялся на True или False автоматически, без отправки тела запроса. Во вью переопределил метод perform_update. Ппц как долго искал информацию, а решение оказалось очень простым)) def perform_update(self, serializer): fav_post = self.get_object() if fav_post.like: serializer.save(like=False) else: serializer.save(like=True) Респект за уроки. Очень помогают в развитии. Ускорили меня раз в 150)
final_price = StoreBook.objects.filter(id=self.book1.id).annotate(discounted=Sum('price')-Sum('discount')).order_by('id') Задание по окончанию урока: пол часа сидел с этим заданием, по ходу выполнения многое узнал но не смог найти другого решения кроме этого. Не делая дополнительный запрос вычитать сумму из поля скидки из поле цены которые находятся в одной таблице(там было макс, мин, сум итд, но не было вычитание)
Спасибо. Очень помогает,когда "понимал,понимал про annotate" и теперь в конце концов допонимался.
Спасибо. Отличный урок, правда пришлось потратить весь день, чтобы разобраться, но без такой практики освоить annotate() было бы тяжелее.
Спасибо за видео!
P.S. Есть еще пару пожеланий:
1. Хотя бы раз в 3 урока было бы неплохо проводить манипуляции с html что бы наглядно было видно для чего мы все это делали.
2. подумал, столкнувшись с этой проблемами, что если вы даете "домашку" (а это полезно), то в следующем видео ее показывали
Спасибо за очень полезную информацию!
Хм, userbookrelation__like, это как бы userbookrelation__set, только к одному полю like и мы подбираемся к лайкам через foreign key в userbookrelation модели.
А можно (но не нужно) найти юзеров которые поставили лайк и посчитать их)
Book.objects.get(name="abc").readers.filter(userbookrelation__like=True).count()
Спасибо за урок
В одном проекте удалось аннотациями-агрегациями и прочими релейтедами уменьшить кол-во SQL-запросов с 54 до 6! )))
Супер! Поздравляю!
очень крутой урок
спасибо большое)
На ночь глядя, пришло вот такое решение последней задачи: Book.objects.annotate(price_with_discount=F('price')-F('discount'))
. Что-то решила discount == models.DecimalField(...), чтобы с приведением типов данных не морочиться.
Похоже на правду
Пишу комментарий чтобы по быстрей вышли новые ролики))
лучший!
а как аннотировать только 1 элемент ?
например у меня есть тест, где я проверяю test_get, но с выдачей 1 элемента по id
если перед конструкцией с annotate сделать get(id=...) , то выдает ошибку
нашел решение сделать через filter(id=...)
но в сериализатор передавать books[0] , иначе тоже не работает
Верно, один элемент больше никак не получится . Но тут как раз лучше не анатировать, а просто в функции вычислять
Отлично,спасибо! По возможности больше "фич", "хаков" и "штучек" в коде ), этого очень не хватает при изучении, и мало кто делиться подобными вещами. Вы случайно на Java не пишите? Вот бы подобный курс по Spring MVC )
Стараюсь чтобы все было просто и с большим функционалом. Фичи и хаки не так часто получается сделать в тестовых условиях, тут как-бы нет реальных проблем, которые нужно хакать. На Java не пишу, к сожалению
спасибо!😎
case when then можно было бы опустить и сразу передать userbookrelation__like=True в count
Спасибо! 2 часа пытался впихнуть эту анатацию в серилайзер, а его надо во вью 🤦♂️
😯
press F to pay respect to the homework =)
😁
Спасибо немного сложнее, но очень интересно. Жаль, что сейчас мало смотрят, но может? Нигде не могу найти ответ почему PyCharm упорно не хочет делать автоимпорт. Я уже даже и временную полную версию установил, все равно предлагает импорт только собственных моделей, а стороннее игнор. Поддержка молчит как в танке.
Первое что приходит в голову , не настроен virtual env или pip env . Но если делать как на видео то должен работать . Возможно проблема в ОС. У вас windows?
@@SeniorPomidorDeveloper Спасибо за ответ, достучался до поддержки, пока только попросили скрины, жду. Virtual env настроен, Mac os
Круто, Спасибо! А почему второй 7ой урок?)
Спасибо, поправил .
Почему то возникает ошибка при создании юзеров в тестах : ОШИБКА: повторяющееся значение ключа нарушает ограничение уникальности "auth_user_username_key"
DETAIL: Ключ "(username)=(user1)" уже существует.
Что-то не так в конфгурации тестов. Они должно каждый раз на новой базе проходить. Можно попробовать удалить базу данных проекта и почистить базы, если какие-то еще лишние тестовые есть
@@mirok8658 Спасибо, это сработало.
Такая же история. И именно в тесте сериализатора! Почистил базу, но после прогона зашел проверить и тест создал в базе трех пользователей и ещё две книги. Не могу понять почему так происходит.
@@point_crash Вряд ли это еще актуально, но возможно из-за использование TestCase из юниттестов, вместо джанги
@@dotco22 Спасибо. Правда решило проблему.
Здравствуйте, спасибо за курс, также спасибо, что учите как правильно писать тесты, прививаете эту хорошую привычку нам с самого начала обучения!! У меня вопрос: оптимально ли мое решение для сложного задания, а именно, я создал булевое поле discount в модели Book, и если оно True тогда annotate считает price with discount:
Book.objects.all().annotate(price_w_discount=Case(When(discount=True, then=F('price')-100)))
price_w_discount создается в сериализаторе
Здравствуйте ! Рад что курс понравился!
Вроде бы, решение хорошее. Единственное, я бы вынес число 100 в переменную , куда-то. Лучше в самом qs использовать переменные , чтобы не хардкодить
@@SeniorPomidorDeveloper Спасибо, исправлюсь 😅
Спасибо! Очень ценная информация. Аннотация - это красиво! Правильно ли я понял, что аннотация нужна, что бы не делать дополнительные поля в самой базе данных (лайк для каждой книги и пр.)?
Не совсем . Аннотация нужна чтобы экономить запросы и при получении книг не проверять у каждого юзера лайкал ли он книгу
@@SeniorPomidorDeveloper Спасибо. Домашнее задание очень легкое :)
тесты сериалайзера создают записи в базе данных и неудаляют их... второй раз тесты не проходят пока их не удалиш, в чём может быть проблема?
Хм. Может класс тестов не TestCase. Другой причины сложно себе представить
почистил базу и from rest_framework.test import APITestCase и унаследовать сериалайзер тест от него, помогло.
а если мы решаем задачу с discount-ом без сериализатора и api, то нужно созадавать метод в manager-е модели с annotate quesrySet-a? Спасибо.
Да тут сериализатор и апи не важны . Тут именно фишка в том что вычисления должны быть в базе , остальное можно по разному делать . Можно создать метод в менеджере или не создавать . Главное правильно queryset написать . Он может быть во вью или в любом другом месте .
@@SeniorPomidorDeveloper Спс. В любом случае данное задание помогло мне совместить пред-ие знания об annotate and F().
Почему-то запомнила раньше,что annotate используется в основном только по отношению к связанной через FK таблице, а F() - это очень приближённый к базе данных и соот-но быстрый способ изменить сразу ВСЕ значения в колонке таблицы (например: всем товарам сделать скидку, всем сотрудникам повысить зарплату). Однако совмещение этих методов оказалось применимо внутри одной таблицы .
Ну и пришлось в очердной раз понять глубину своего невежества, прочитав статью об ORM : под девизом "simple queries - Managers, complex queries - QuerySets " (medium.com/@jairvercosa/manger-vs-query-sets-in-django-e9af7ed744e0). + ещё нашла кучу неведомых зверей типа OuterRef, Subquery in docs, которые тоже ждут своей очереди.
Да, тема очень большая ! Рад что получается и приходит понимание .
А есть где нибудь архивчик с этим проектом?
github.com/chepe4pi/books_lessons вот
@@SeniorPomidorDeveloper спасибо
Извините за предидущий огромный комментарий (который удалился), вроде решил проблему, так и не понял в чем дело, переписал файлы serializers и test_serializers и заработало. Где то была ошибка которую я так и не нашел )
Да, этот ютюб постоянно удаляет комментарии, не знаю почему. Иногда из-за ссылок, иногда просто так.
Хорошо что проблема решилась . Если что могу ссылку прислать на репозиторий
@@SeniorPomidorDeveloper так я с вашего GitHub и взял код, долго сравнивал со своим, так и не нашел отличия ) В итоге вставил ваш код в эти файлы, так как свой я редактировал в попытках найти причину. Видимо опять где то одна буква или запятая делала всю погоду) Чем плох Python это форматирование кода через табы и пробелы, в итоге даже неверный отступ в один пробел может сломать код.
Для этого есть редакторы кода с подсветкой синтаксиса.
В языках программирования, где нужно использовать разные скобочки вместо отступов, еще легче допустить ошибку.
@@U7116-k7d в пайчарме есть функция сравнить два файла или сравнить файл с буфером обмена, удобно, можно копировать чужой код в буфер и сравнивать со своим файлом
может кто подскажет, я как то поменял название таблицы ALTER TABLE books_shop_book RENAME TO books_shop; и потом django при отработке url выдает такую ошибку: django.db.utils.ProgrammingError: ОШИБКА: отношение "books_shop_book" не существует. Я так понял, что старое отношение осталось и как можно "безболезненно" поменять название таблицы ?) При чем если меняю название таблицы обратно на "books_shop_book", то все окей, django видит эту таблицу. Что то мне подсказывает, что лучше не менять название таблицы, судя по различным источникам, но все же хочется до истины докопаться. Думал кешируются где данные эти, почистил в __pycache__ все, что было связано с БД, но, увы, это результат не принесло. Видимо, где то хранится настройка..
Вы можете в момент создания модели указать db_table , и тогда можно будет его менять когда хотите и модель должна продолжать работать . Но если имя таблицы создано автоматически то лучше уже не не переименовывать
Мдя, с annotate у меня как то совсем грустно. вообще ни чо не понял, где вообще можно почитать как строить такую мудрёную конструкцию как на 16:17, тут получается типа match - case c условием, но как узнать что так можно было? от фонаря такую конструкцию не напишешь... Когда-то мне казалось что лямбда и рекурсия это что то запутанное, но теперь я понимаю что то был просто сахар, на уровне детского сада.
Я тоже это в голове не держу . Точно не напишу по памяти такой кейс с Case, When, then=1. Просто я примерно представляю возможности ORM и когда есть подобная задача то начинаю гуглить как через ORM это сделать.
Тут задача это главное. Без нее я бы и не подумал копаться в этом .
@@SeniorPomidorDeveloper Спасибо, немного упокоили ), а то я уже совсем скис )
добрый день
почитал документацию и нашел про Sum, написал:
books = Book.objects.all().annotate( annotated_likes=Count(Case(When(userbookrelation__like=True, then=1))), rating=Avg('userbookrelation__rate'), price_discount=Sum(F('price')-F('discount'), output_field=FloatField())).order_by('id')
но оно выдает лютую дичь, погуглил про F не нашел ответ на свой впорос. Что в данном случае делает функция F? и как правильно делается дз?
Не буду спойлерить правильный ответ. А F() нужен только для того чтобы обратится к определённому полю. Сам по себе он не несёт никакой функции .
а зачем суммировать в price_discount? и ещё, меня так напугали FloatField в отношении денежных расчётов, что я вместо них повсеместно применяю поле DecimalField
Decimal это правильно. Суммировать ? Вроде нужно из цены вычесть скидку ..
@@SeniorPomidorDeveloper я наткнулся на пример где Sum(F(..) /F(..)) и подумал если деление стоит, то может и минут прокатит
Может и прокатит :) Тут не угадаешь, нужно просто попробовать ! )
Супер
В вашей реализации лайки получаются неуникальными. Один пользователь может миллион раз лайкать одну книгу. А для того чтоб убрать лайк по факту нужно передавать "False". Причем когда несколько "True" от этого пользователя, то получится каша))
Всегда есть над чем работать
@@SeniorPomidorDeveloper
я решил эту проблему, добавив в модель мета класс с "UniqueConstraint(fields=['user', 'book'], name='unique-like')"
тесты проходит, больше одного лайка не ставит. надеюсь, что это хорошее решение)
Супер! Спасибо. Надо будет учесть в следующих курсах
@@SeniorPomidorDeveloper я немного дальше пошел. Сделал так, чтобы при отправке запроса лайк менялся на True или False автоматически, без отправки тела запроса. Во вью переопределил метод perform_update. Ппц как долго искал информацию, а решение оказалось очень простым))
def perform_update(self, serializer): fav_post = self.get_object() if fav_post.like: serializer.save(like=False) else: serializer.save(like=True)
Респект за уроки. Очень помогают в развитии. Ускорили меня раз в 150)
Супер! Можно ещё типа так упростить
serializer.save(like=not bool(fav_post.like))
Невоспронимаемая информация начиная с 7 видео, без разяснения ничего не понятно. Хотя до этого много очень полезного узнал.
Сочувствую. Посмотрите последний курс, Оптимизация. Там похожие вещи и будет понятнее, я думаю.
django.db.utils.IntegrityError: duplicate key value violates unique constraint "auth_user_username_key"
DETAIL: Key (username)=(user1) already exists.
Вот такая ошибка выходить после запуска теста, 17:40
Надо почистить тестовую базу наверно. Или перезапустить тест , по идее скрипт должен сам предложить ее почистить
аа, ок спасибо@@SeniorPomidorDeveloper
какие именнно таблциы нужно почистить ?
Вся тестовая база перед запуском тестов должна удаляться. Это другая база , обычно называется с префиксом test_* или типа того
не могу решить, что можно делать ? с гита пуллит ?
@@SeniorPomidorDeveloper
final_price = StoreBook.objects.filter(id=self.book1.id).annotate(discounted=Sum('price')-Sum('discount')).order_by('id')
Задание по окончанию урока:
пол часа сидел с этим заданием, по ходу выполнения многое узнал но не смог найти другого решения кроме этого. Не делая дополнительный запрос вычитать сумму из поля скидки из поле цены которые находятся в одной таблице(там было макс, мин, сум итд, но не было вычитание)
Честно говоря, я уже плохо помню это видео и задание. Но если оно помогло разобраться то я очень рад.
очень крутой урок
спасибо большое)