ドキュメントエンジニアリング海外ViewSets Django RESTフレームワーク

親愛なる同僚たち、あなたがそれをもっと簡単にしたいということは私たちの人生で起こりますが、それは初心者のようになります。 そして、興味深いことに、魂と引き換えにシンプルなソリューションを提供する多くの強力なツールがあります。 つまり、抽象化の価格は、その使用の美しさに不釣り合いです。 私にとって、このような不平等な交換の例は、Django Rest Framework 3.4.0、そのViewSetsメカニズム、および開発されたAPIに関する詳細なドキュメントを表示する必要性でした。



簡単なものから始めましょう。DRFで作業するための私のお気に入りの形式は、APIViewの子孫だけを書くことです。 一方では、これは繰り返しコードであり、他方では、予測可能で管理可能なユーザーケースを備えた完全に簡潔なソリューションです。 まず、95%の確率で、1つのエンドポイントに複数のシリアライザーをハングアップさせません。 次に、URLバインディングを微調整できます。 しかし、時間が経つにつれて、あなたは考え始めます:私はすべてを正しくしましたか? たぶん、RESTによって長年にわたってテストされた保守主義の概念から離れる時でしょうか? さらに、DRFにはViewSetsという非常に優れた抽象化レイヤーがあります。



ビューセットの概念は単純です。サービスモデルがあり、エンドポイントを構成したり、別のクラスでそれらを記述する必要はありません。 ビューを個別に登録したり、URLをバインドしたりする1つのクラス つまり これらは、青いリボンで縛られた箱に詰められた多くのパターンです。 タスクは比較的標準的でした:



1.カスタムユーザープロファイルがあります。

2.追加のフィールドがあります。

3.登録時に、RESTを使用して、手動で必要なフィールドと必要でないフィールドを決定します(DRFレベルでモデルフィールドをオーバーライドします)。

4.ログインが自動的に生成されます。

5.プロファイルには招待との接続があり、招待はこの招待を発行した組織に関連付けられています。



少し考えて、2つまたは3つのシリアライザーを作成することにしました。 絶対に間違いなく、作成時に別のシリアライザーがあります。 個別-表示中。 おそらく、しかし、3番目が必要であるという事実ではなく、更新時(変更時)に。 従来のRESTアプリケーションスキームは次のようになります。



serializers.py



class UserCreateSerializer(serializers.ModelSerializer): pass class UserViewSerializer(serializers.ModelSerializer): pass class UserUpdateSerializer(serializers.ModelSerializer): pass
      
      







views.py



 class UserCreateView(APIView): pass class UserDetailsView(APIView): pass class UserUpdateView(APIView): pass
      
      







少しリファクタリングした後、1つのAPIViewを取得できます。



views.py



 class UserApiView(APIView): def get(self, request, *args, **kwargs): return self.__list_view(request) if 'pk' not in self.kwargs else self.__detail_view(request) def post(self, request, *args, **kwargs): return self.__create_view(request) if 'pk' not in self.kwargs else self.__update_view(request)
      
      







ご覧のとおり、ViewSetは特に必要ありません。 リクエストトレースは正確に1行で発生しますが、関数get、post、put、およびその他の機能は利用可能です。 さらに、突然結果が気に入らなければ、エンドポイントの3つの別々のクラスの形式にいつでも戻ることができます。 この方法には、自動ドキュメンテーション( SwaggerまたはDRF Docs )用のアプリケーションをインストールすると、予測可能な結論が得られます:3つのエンドポイント、または説明した3つの方法で1つのエンドポイント。



ただし、ViewSetの抽象化に移りましょう。



views.py



 class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet): serializer_classes = { 'list': UserViewSerializer, 'get': UserViewSerializer, 'create': UserCreateSerializer, 'update': UserUpdateSerializer, 'set_password': UserEditSerializer, #   ? 'activate': UserEditSerializer } def list(self, request, *args, **kwargs): serializer_class = self.serializer_classes['list'] pass def create(self, request, *args, **kwargs): serializer_class = self.serializer_classes['create']
      
      







まず、豊富なコードが印象的です。 これは、1つのエンドポイントと3つのメソッドを持つバージョンよりもはるかに多くなります。 次に、かなり快適な宣言コードがあります。ちなみに、まもなくナイフでナイフを通り抜けます。



したがって、私たちの問題は、SwaggerおよびDRF Docsがこのビューで正しく機能しないことです。



Swaggerコードについては詳しく調べませんでしたが、次のようなエンドポイントメソッドを受け取ったと言っても、罪を犯さないと思います。



1. urlpatternを取得する

2.エンドポイント= urlpattern.callback

3.メソッド= endpoint.available_methods



インスタンスを作成せずに、または要求引数を受け取るas_viewメソッドにアクセスせずに、コールバックが要求されることに注意してください。 理論をテストしてみましょう。



views.py



 class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet): serializer_classes = { 'list': UserViewSerializer, 'get': UserViewSerializer, 'create': UserCreateSerializer, 'update': UserUpdateSerializer, 'set_password': UserEditSerializer, #   ? 'activate': UserEditSerializer } def get_serializer_class(self): logger.warn(self.request) logger.warn(self.actions) return UserViewSerializer # Actions here...
      
      







UserViewSetオブジェクトにrequest属性がないという情報を含む500エラーが表示されます。 問題のある行を削除すると、2番目のエラーが発生します。このオブジェクトにはactions属性がありません。 これは、ViewSetMixinがリクエストの存在下でアクションを公開するためです。ただし、クラスプロパティの形式で利用可能なアクションのリストを作成する方が論理的です(mixinを継承する場合、標準アクションは名前とトリガー条件によって修正されます)。



しかし今、私たちは私の祖母が泥を持っているとどうなるか興味がありません(私が間違っていなければ、ダールの辞書)。 文書化できないインターフェースがあります。 ここに悔しさがある!



Swaggerのインターフェースを文書化できませんでした。 問題を解決するための松葉杖は、前のスニペットで見たget_serializer_class()メソッドにあります。 SwaggerとDRF Docsの両方がこれを使用して現在のシリアライザーを取得します。 コードは次のようになります。



views.py



 class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet): serializer_classes = { 'list': UserViewSerializer, 'get': UserViewSerializer, 'create': UserCreateSerializer, 'update': UserUpdateSerializer, 'set_password': UserEditSerializer, #   ? 'activate': UserEditSerializer } def get_serializer_class(self): return self.serializer_classes.get(self.action, UserViewSerializer) # Actions here...
      
      







ただし、get_serializer_classがトリガーされた時点では、self.actionは属性として存在していません。 これにより500エラーが発生し、このケースの使用は許可されません。 両方のソリューション(Swagger、DRF Docs)を研究して、私は後者に落ち着きました。 そして、別の問題が発生しました:



- 今日は2016年7月27日で、masterブランチのDRF Docsコードは、pypiまたはGITリポジトリのダウンロードによってインストールされるDRF Docsコードとは異なります。



これがグリッチかどうかはわかりませんが、明らかに、gitはリリース0.0.11とマークされたコードを提供し、開発者はリリースなしでウィザードを更新する大胆さを持っていました。 失敗!



これまでのところ、この問題は松葉杖によって解決されています-パッケージ内のapi_endpoint.pyの置換。 これはオプションではないことを完全に理解しています。 ここで、コードを開発する2つの方法があります。開発者が新しいリリースをリリースするまで待つか、APIViewから継承するオプションに戻るかです。 今日、これを行う時間と労力はありません。 このファイルのコード(ウィザードで機能している)を処理すると、2つの興味深いフラグメントに出会いました。 これが最初のものです:



api_endpoint.py



  def __get_serializer_class__(self): if hasattr(self.callback.cls, 'serializer_class'): return self.callback.cls.serializer_class if hasattr(self.callback.cls, 'get_serializer_class'): return self.callback.cls.get_serializer_class(self.pattern.callback.cls())
      
      







実際、ViewSet実装には常にプロパティserializer_class = Noneが含まれます。 シリアライザの動的変更の優先度を調査するために、チェックを交換することは論理的です。



第二のポイント:



api_endpoint.py



  view_methods = [force_str(m).upper() for m in self.callback.cls.http_method_names if hasattr(self.callback.cls, m)] return viewset_methods + view_methods
      
      







ここで、これら2行の間にストッパーを挿入してself.callback.actionsを取得しようとすると、仕事に必要な辞書が取得されます。 もちろん、ここでは、開発に接続してアクションを文書化するための個別のロジックを追加することができました...しかし、私たちはそれを何も必要としません。 今、私はDRF Docsの開発者から最初の問題(serializer_class = None)の問題の採用を待ち、早期のリリースを望んでいます。 発生しない場合は、APIViewを使用してオプションに戻ります。 シリアライザーを取得する方法については、次のようになります。



views.py



 class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet): serializer_classes = { 'list': UserViewSerializer, 'get': UserViewSerializer, 'create': UserCreateSerializer, 'update': UserUpdateSerializer, 'set_password': UserEditSerializer, #   ? 'activate': UserEditSerializer } def get_serializer_class(self): if not hasattr(self, 'action'): action = 'create' if 'POST' in self.allowed_methods else 'list' else: action = self.action return self.serializer_classes.get(action, UserViewSerializer) # Actions here...
      
      







更新メソッドが追加時に問題を引き起こさないことが望まれます。 別の小さなコメント:まだpostメソッドを追加する必要がありました:



views.py



 class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet): #... def post(self, *args, **kwargs): return super().post(*args, **kwargs) # Actions here...
      
      







これがないと、DRF Docsはallowed_methodsを取得できず、Swaggerに問題がありました。



だから、親愛なる同僚たち、高レベルのフレームワーク抽象化に取り組むとき、私はアーキテクチャの問題に出くわしました。 つまり、「自分を責める」という単純な結論に要約されます。 ただし、ViewSetsは便利で公式のツールであるため、この質問はもちろん議論の余地があります。 ただし、クラスにアクションを登録する問題は解決されていないことが肉眼で確認できます。 したがって、ドキュメント開発者は通常のアクションを処理することに抵抗を感じます。 状況の結果は単純です。現在、モデルのプレゼンテーションテンプレートよりも個別のViews APIを使用する方が簡単です。 少なくとも、RESTを作成できる最も有名なRESTエンジンまたはフレームワークでは、そのような抽象化はほとんど見られません。 そして、非常に大きな質問:彼らはまったく必要ですか?



All Articles