Найти - Пользователи
Полная версия: DRF
Начало » Django » DRF
1 2 3 4
vasi.che
Вот те раз! Вот те два Со спрингом то и я могу. На нем сейчас и крутится.
FishHook
Не потому что она плохая сама по себе, а потому что она не подходит для энтерпрайза. Как только вы начинаете думать над архитектурой, джанга начинает мешать и вы потихоньку запиливаете свой фреймворк. Лучше сразу взять что-нибудь минимальное типа фласка или гибкое типа пирамиды. А еще лучше подобнвми делами на питоне не заниматься
Одним из мотивов была переносимость на другой(ие) хосты. С джавой бывает сложно найти. А питон и postgresql как правило по умолчанию. Неужели в энтерпрайзе не используется?! Удивительно. Вроде сам Гугл python двигает. И тут на тебе! Очень интересное мнение. Как-то я прохлопал этот энтерпрайзный аспект.
FishHook
vasi.che
Одним из мотивов была переносимость на другой(ие) хосты.
какие могут проблемы с хостом? VDS/VPS и вперед, другого хостинга я вообще не понимаю.
vasi.che
FishHook
Память!
vasi.che
vasi.che
Да и просто хочется посмотреть , ессть ли жизнь в параллельных вселенных
vasi.che
FishHook
Созвучно Вашему мнению
https://habrahabr.ru/company/mailru/blog/328352/#comment_10214512
Slow
В общем случае в DRF, если вы хотите в рамках одного вьюсета использовать разные сериализаторы на вход и на выход, у вас нет другого выхода, кроме как городить что то типа

 class CustomSerializerClassesMixin():
    serializer_class = None
    request_serialiser_classes = {}  # {'action': SerializerClass}
    response_serializer_classes = {}
    def get_serializer_class(self):
        if hasattr(self, 'action') and self.action in self.request_serialiser_classes:
            return self.request_serialiser_classes[self.action]
        return self.serializer_class
    def get_response_serializer_class(self):
        if hasattr(self, 'action') and self.action in self.response_serializer_classes:
            return self.response_serializer_classes[self.action]
        return self.serializer_class
Но чтобы сие заработало, нужно еще и все миксины научить с этим работать (RetrieveModelMixin, UpdateModelMixin и прочие)

и затем это использовать в том числе для `actions` типа `list`, `retreive`, `update` и так далее, включая и определенные вами.

Валидаторы (и сериализацию) не нужно утаскивать в контроллер, как сделано в вашем примере; можно определить DocCreateBySerializer какой-нибудь с нужными полями, валидацией по полям и валидацией целикового объекта в методе validate(self) этого сериализатора, а затем использовать сериализатор в коде метода createby вьюсета.

Кроме того,большая проблема вас настигнет, если вы потом захотите это всё использовать при автоматической генерации документации (у них это сейчас в майлстонах, но будет не скоро, (у тома кристи, автора фреймворка, что то там нехорошее с ребенком и он сейчас даже пулл-реквесты толком не читает)).
Докген последней стабильной версии вообще никак не работает с респонзами, только с реквестами, и не поддерживает per-action.
vasi.che
Slow
В общем случае в DRF, если вы хотите в рамках одного вьюсета использовать разные сериализаторы на вход и на выход, у вас нет другого выхода, кроме как городить что то типа

Что-то как-то все замороченно. Я себе представлял нечто такое:
 # -*- coding: utf-8 -*-
"""-------------------------------------------------------------"""
class ABussinesLogic:
  """
    Абстрактный класс, родитель для конкретных фунций бизнес-логики
  """
  def __init__(self, validator, processor, serializator):
    self.validator = validator
    self.processor = processor
    self.serializator = serializator
  def isValid(self, data):
    return self.validator.isValid(data)
  def process(self, data):
    return self.processor.process(data) # Сама логика
  def serialize(self, data):
    return self.serializator.serialize(data)
"""-------------------------------------------------------------"""
class DocCreator(ABussinesLogic):
  """
  Конкретная функция бизнес-логики
  """
  def __init__():
    super(someValidator, someProcessor, someSerializator)
"""-------------------------------------------------------------"""
class Router:
  """
  Обработчи маршрутов и методов
  """
  """
    Конфиг для REST. Вид:
    route: {
      {
        method1: BussinesLogic1(),
        method2: BussinesLogic2()
      }
    }
  """
  config = {
      '/doc/createBy': {
          'POST': DocCreator(),
         },
     '/doc/': {
          'GET': DocPresenter()
      },
   .....
  }
  def applyRoute(route, method, requestData):
    logic = config[route][method]
    if logic.isValid(requestData):
      try:
        data = logic.process(requestData)
        serilizedData = logic.serialize(data)
        return Response(serilizedData)
      except Exception:
        return Response(500)
    else:
      Response('Not valid')
Slow
vasi.che
Что-то как-то все замороченно. Я себе представлял нечто такое:
Тоже вполне себе реализуемо. Хотя и до конца не понятно, зачем вам тогда вообще рест фреймворк (да и джанго)
Если пользоваться терминами рест фреймворка, то ваши сущности `validator`, `processor` и `serializator` вполне себе мапятся в `InputSerializer`, `viewset.action` и `OutputSerializer`. Для большинства кейсов сериализатор будет вообще общим.

Кроме того, рест фреймворк позволяет очень быстро реализовать тупейший CRUD+L с возможностью переопределения конкретных действий и/или дополнения парадигмы CRUD, создания каких-либо rest-rpc или чего-либо другого.

Он создавался больше с целью максимально быстро принимать/отдавать имеющиеся в орм модели без лишних затрат на разработку.

В общем случае, если у вас есть какая-то модель, и вам нужно их просто персистить/читать, вы получаете и сериализаторы, и сам вьюсет практически бесплатно, поскольку он умеет строить их автоматически на основе моделей.

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

PS. СamelCase в питонячьих гаедлайнах применяется только в именовании класса, в именовании методов и переменных рекомендуется snake_case.
PPS. Чтобы замапить кемелкейс сериализатора в аттрибут модели, можно использовать kwarg `source`, например:
 class SomeSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.SomeModel
        fields = (
            'id',
            'attribute',
            'oneElseAttribute',       
        )
    oneElseAttribute = serializers.CharField(source='one_else_attribute', validators=[val1, val2])
    def validate(self):
        '''
            Object level validation, must raise ValidationError on failure
        '''
        pass
`oneElseAttribute` при сериализации на вход будет порождать ключ `one_else_attribute`, а на выход - искать ключ/аттрибут отъекта (в зависимости от того, чем вы инстанциируете сериализатор) `one_else_attribute`, а в выходном json будет `oneElseAttribute`.

Когда вы вызовете is_valid() экземпляра этого сериализатора, он прогонит сначала определенные на модели валидаторы для полей `id` и `attribute`, затем ваши `val1` и `val2` для `one_else_attribute`, после чего запустит валидацию объекта, которую вы (возможно) опишете в методе `validate` сериализатора (или не запустит, если это partial_update объекта (HTTP PATCH), а после вызова `.is_valid` у вас будет доступным `.validated_data`, которая по сути OrderedDict с ключами, которые у вас определены на модели.

Это несколько отличается от того, чего хотите вы, вы исходите от действия, а рест фреймворк в первую очередь ориентируется на модель. Но реализовать можно и ваш подход, если несколько переопределить поведение рест_фреймворка.
vasi.che
Вобщем, результаты поиска ответов у зарубежных и отечественных коллег. С мнением FishHook многие согласны. Вот боле-мене развернутый ответ Large web apps in Python: A good architecture. Ответ на вопрос размещения бизнес логики развернут. Не view, ни model не подходят для этого дела, надо выносить в отдельный слой. Похожих мнений достаточно.
ZAN
vasi.che
Slow довольно развернуто ответил. Со своей стороны добавлю несколько комментариев касательно pure Django vs DRF.

Прежде всего, если используется DRF, то часть джанги, которая связана формами, темплайтами и non-DRF вьюхами, очень часто вообще не используется. Т.е. по сути это немного другой стек - бэкенд реализует REST API, а фронтенд пишется на JavaScript, в то время, как pure Django - это HTML, который генерируется на сервере.
DRF при этом позволяет очень быстро и просто сделать API, при условии если объекты вашего приложения хорошо соответствуют моделям в Django. Чем больше расхождений, тем больше приходится подкручивать в коде, и тем больше сопротивления придется преодолевать.
Что хорошего в DRF - он написан с учетом best practice in REST. Если ваше API соответствует ванильному REST, то написать его будет быстро и легко, если нет, то будет, как в ситуации выше. Но нужно учитывать, что REST сам по себе не является серебряной пулей и обладает рядом недостатков, которые неизбежно наследует DRF, как фреймворк. К примеру, что ванильный REST не позволяет одним запросом вытягивать несколько объектов разного типа, в этом случае лучше смотреть в сторону GraphQL. Либо дилемма тяжелых запросов vs много слишком частных апишек (опять-таки решается GraphQL, либо кастомными сериализаторами, которые будут возвращать только нужные поля, запрошенные в GET параметрах).
Что еще хорошего в DRF - он довольно хорошо сегментирован на абстракции, и переопределить можно практически все, что хочешь (но не ORM). Дополнительно т.к. он использует джангу, то вы бесплатно получается такие плюшки, как админку, возможность наклепать Web странички на темплейтах (если оно вам надо, конечно) и вообще весь джанговский стек.
Что плохого в DRF - он испольует джангу, поэтому он МЕДЛЕННЫЙ, а также обладает другими недостатками джанги (снова ORM) и остальное. Т.е. другими словами - если нужно запилить небольшой проект, то DRF - это довольно круто, а для тяжелого не очень.
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