Pull to refresh

Comments 16

> Во-первых, бросается в глаза обилие кода. Его гораздо больше, чем в варианте с одним эндпоинтом и тремя методами.

Ага, а сколько кода (причем дублируемого) будет в вашем __list_view и __detail_view? А в примере с UserViewSet — это полный код. Осталось только указать queryset и другие атрибуты. Никаких "# Actions here..." там больше нет, всё в миксинах.

> Как видите, особой надобности во ViewSet нету. Трэйс запроса происходит ровно одной строчкой, но нам доступны функции get, post, put и иже с ними.

Что значит надобности? Зависит от решаемой задачи. ViewSet и дженерики хороши в двух случаях: когда всё просто и когда всё сложно.
Когда всё просто — это наследовался от ModelViewSet, указал queryset, serializer_class и endpoint готов.
Когда всё сложно — это ViewSet с различными (в том числе кастомными) миксинами, которые можно применять во всех ViewSet проекта и расширять/изменять их по мере необходимости через методы (типа perform_update у UpdateModelMixin). В итоге имеем правильную и красивую архитектуру приложения без своих костылей.

На счёт Swagger — да, я все эти проблемы побороть не смог. Да что там, даже ApiRoot (тот, который для рендеринга карты урлов на главной) ломается. Но если исследовать проблему чуть глубже — становится понятно, что оно и не может работать. Нужно использовать другой путь, а это обычное дело в разработке.

Собственно отсюда и выводы. Если подумать, то сам ViewSet — это лишний сахар. С точки зрения какого-нибудь JS, безусловно, проще создать кучу объектов, у которых весь функционал будет записываться декларативно. А если взглянуть на стиль вьюсетов внимательнее, то можно найти очень много общего с JQuery.
А Django это лишний сахар над wsgi?
JS то тут причем? Насколько я пониманию, если взглянуть на стиль вьюсетов внимательнее, то можно найти много общего с принципом SOLID. Тоесть, не нужно придумывать свою соответствующую структуру.
При чём тут wsgi? Джанга в первых версиях связывалась с серваком по абсолютно иному протоколу.

А что до вьюсетов, то они нарушают принцип SOLID где только можно. Начиная с первого: SRP.
Это как у протестантов? Если нечего ответить оппоненту, скажи: «изучайте Писание».
werevolff спасибо за статью, воткнулся сейчас в эту проблему с документацией, в итоге переделал с viewset на views, в итоге оказалось кода меньше.

По поводу Django-писания, матчасть стоит подтянуть. Я, например, писал об этом в своей статье:
Django-разработчик, помни: все что ты делаешь — это настройка WSGI-приложения.
Посмотри документацию о превращении входящего запроса на сервер в исходящий ответ.
Да, а по поводу победы над этой темой я дал вполне явный намёк: в DRF Docs при формировании методов доступен словарь actions. Можно сделать форк и проработать создание эндпоинтов, включив туда actions вместо методов на ViewSets. Возможно, что овнер документатора заинтересуется таким подходом и согласится вмержить себе решение. Тем более, что у него там просто всё выводится на реакте.

Ну а я этим точно не стану заниматься: сроки поджимают.

Ещё хотел бы добавить, что методы класса post/get/put и т.д. создавали красивую экосистему, в которой надстройки в виде actions разумно доставались программисту проекта. ViewSets в эту экосистему слабо вписываются. Тут надо либо их выделять в полноценную концепцию со всеми методами получения и определения, либо удалять нафиг из системы. Полагаю, что так оно скоро и будет. Опенсорсные проекты часто избавляются от непопулярных узлов, либо перерабатывают их концепцию.

DRF — боль моя. То, что для обычного CRUD обычно нужна пачка сериалайзеров — C, R две штуки для U (patch и post) + еще, например, комплект для суперюзера, по-моему автору в голову даже не приходило. 2/3 файла в результате состоит из унылой копипасты.


Отдельная песня — DRF-Swagger (он уже научился догадываться, что на выходе может быть serializer_class[] ?). До удобоваримости пришлось очень сильно дорабатывать кувалдой и какой-то матерью. (input_serializer_class, many=True в yaml и прочее).


Особенно порадовался, как автор в 3.4 (видимо обчитавшись xkcd) ВНЕЗАПНО стал пилить автодокументирование в 15м стандарте (wtf is coreapi?).


А на тему статьи — ViewSets позволяют офигенно наглядно бутстрапить CRUD-образную обвязку любой сущности, выкидывая из CRUD ненужные буквы, и с этой задачей справляются на ура. Че еще от них надо-то?

Полагаю, надо чтобы они CRUD реализовывали полноценно и явно. Судя по коду документаторов, вьюсет без request'а не знает какую операцию будет выполнять. Поэтому, документаторы логично описывают такой клас как единый и неделимый CRUD (без методов вообще). Вот ведь доупрощались. Хотели реализовать концепцию одной сущностью, а получили эндпоинт, которым не знаешь как пользоваться.
По всей видимости, ощущение «неуклюжести» при использовании ViewSet'ов преследует многих, кто пытается ими пользоваться? ViewSet'ы, на мой взгляд, прекрасно подходят к случаю «один эндпоинт — одна модель, без отношений с другими». И совсем не подходят для сложных моделей с дополнительными action'ами. Выбор сериализатора на основании действия? Можно и так — делай соответствующий mixin и пользуйся им на здоровье. Выбор queryset'а? Ну, например, чтобы в list не тащить связанные модели, а вот в retrieve уже стянуть всё за один SQL-запрос — да тоже на здоровье.

Получаются они примерно такими - если кому интересно
# http://stackoverflow.com/questions/22616973/django-rest-framework-use-different-serializers-in-the-same-modelviewset
class MultipleSerializerViewSetMixin:

    def get_serializer_class(self):
        try:
            return self.action_serializer_classes[self.action]
        except (AttributeError, KeyError):
            return super().get_serializer_class()


class MultipleQuerysetViewSetMixin:

    def get_queryset(self):
        try:
            return self.action_querysets[self.action]
        except (AttributeError, KeyError):
            return super().get_queryset()


Теперь хочу фильтров! Разных! Тут становится понятно, что никакого «get_filters» нам не предоставлено — ну хорошо, переопределим filter_queryset, может даже полностью повторив его код — пускай… И где-то тут и возникает неловкость — как будто тебе дали рог изобилия, но забыли приложить к нему инструкцию и всё, что остаётся — лупить им по стенам и собирать вываливающиеся крошки.

Теперь что касается использования сразу get, post и т.д. Это в большинстве случаев скорее приятно, чем продуктивно. Знание того, что именно заставляет программу работать — оно прекрасно само по себе. Но постоянно переписывать одни и те же строчки для каких-то простых моделек?… Получение данных из request, валидация сериализатора, сохранение — и так 500 раз… А если я хочу в одну транзакцию с сохранением модели включить ещё что-то? Например, запись в журнал об этом изменении? Получается действительно много… даже Много повторяющегося кода. Причём повторяющего зачастую то, что в generic'ах уже кем-то написано, протестировано и упаковано — бери да пользуйся.

Так что по мне вариант — generic'и для сложных моделей, viewset'ы — для простых в две строчки. Есть конечно и свои нюансы — хочешь использовать меньше URL'ов и больше HTTP-методов — всё равно изволь выбирать сериализаторы… Но хотя бы не из простыни на 15 вариантов. Ну а get, post и остальные низкоуровневые методы — это для тех случаев, когда исполняемая логика не укладывается в обычный CRUD.
Вот тут абсолютно согласен: ViewSet — сырой продукт для плоских моделей. Хотя бы потому, что он неуклюже пытается подменить HTTP методы модельным CRUD.

Не согласен с тем, что методы генериков post/get/put — это низкоуровневое программирование. Скорее, наоборот. REST Framework не надо относить к пространству моделей. Это очевидный функционал формы. Отсюда следует, что отдельная форма — это отдельный класс сериалайзера. А представление, которое разруливает обращение к форме, должно работать с post/get/put а не с create/update/deflorate и т.д. Потому что это вьюха. Она обязана реализовывать функционал HTTP протокола, а не модели/формы для модели.
UFO just landed and posted this here
Только вот во 2-й ветке Django REST Swagger:
Deprecated:

YAML docstrings

И работает он теперь через CoreAPI и схемы. DRF предоставляет возможность ручного определения схем. И всё бы ничего, но повторять те же самые поля ещё раз?..

Возможность рисовать схему самому, но при этом таскать описания полей из имеющихся сериализаторов — это был бы не самый лучший, но выход. Можно попробовать покопаться в исходниках — rest_framework.schemas.SchemaGenerator и его get_path_fields, get_serializer_fields, get_pagination_fields и get_filter_fields, возможно, смогут помочь.
UFO just landed and posted this here
В последнем проекте плюнул на эти костыли для Swagger-a и перешел на RAML + raml2html
В итоге, клиент остался доволен документацией, в документации нет привязки к конкретной технологии, зато все описание выглядит прилично.
Как бонус, можно отдельно задокументировать большую часть ендпоинтов перед началом разработки (с свойствами, доступом и т.п) и дальше пилить серверную часть и клиента независимо друг от друга (сервер покрывался unit + integration тестами, клиент просто мокапил все запросы соответственно документации).

Все время пытался этот способ обойти т.к. боялся проблем из-за расхождения документации и реальности, но как показала практика это не проблема вообще.
Sign up to leave a comment.

Articles