djangoアプリケーションまたはシックモデルでのコードの整理については問題ありません

翻訳者から
いつものように、djangoアプリケーションでコードを整理するための特定のアプローチに関する興味深い記事の無料翻訳。 役に立つでしょう:

  • そのような問題について考えていない人
  • 論理の組織化についてすでに独自の見解を持っているが、代替案の評価を気にしない人
  • すでに議論されたアプローチを使用している人々のために、彼らの考えを確認するために
  • 議論されたアプローチをもはや使用せず、反対意見を持っている人


それほど多くのコードはありません。この記事はほとんど議論の余地があります。 アングイ)





画像

厚いモデル。



イントロ



このフレームワークのdjangoチュートリアルと例のほとんどは、プロジェクトでコードを整理するという点で初心者にとって悪い例です。 大規模で長時間実行されるアプリケーションの場合、このような例から引き出されたコード構造は、開発プロセス中に些細でない問題や困難な状況を引き起こす可能性があります。 この記事では、コードを編成するためのめったに出会わない別のアプローチについて説明します。



DjangoのMVC = MTV +埋め込みC



MTVがdjangoでどのように機能するかを、たとえばRoR開発者に説明したことがありますか? 誰かがテンプレートはビューであり、ビューはコントローラーであると考えるかもしれません。 あまり好きではありません。 コントローラーは、リクエスト/レスポンスロジックを提供するdjango組み込みURLルーターです。 ビューは、適切なデータを適切なパターンで表すために必要です。 テンプレートとプレゼンテーションが一緒になって、フレームワークの「プレゼンテーション」レイヤーを構成します。



このようなMTVには多くの利点があります。その助けにより、一般的なアプリケーションだけでなく、簡単かつ迅速に作成できます。 ただし、データを処理および編集するためのロジックを保存する場所、コードを抽象化する場所、その場合は不明です。 いくつかの異なるアプローチを評価し、それらのアプリケーションの結果を見てみましょう。



ビューのロジック



ビューにすべてまたはほとんどのロジックを配置します。 さまざまなチュートリアルや初心者向けの最も一般的なアプローチ。 次のようになります。



def accept_quote(request, quote_id, template_name="accept-quote.html"): quote = Quote.objects.get(id=quote_id) form = AcceptQuoteForm() if request.METHOD == 'POST': form = AcceptQuoteForm(request.POST) if form.is_valid(): quote.accepted = True quote.commission_paid = False #   provider_credit_card = CreditCard.objects.get(user=quote.provider) braintree_result = braintree.Transaction.sale({ 'customer_id': provider_credit_card.token, 'amount': quote.commission_amount, }) if braintree_result.is_success: quote.commission_paid = True transaction = Transaction(card=provider_credit_card, trans_id = result.transaction.id) transaction.save() quote.transaction = transaction elif result.transaction: #  ,      celery logger.error(result.message) else: #  ,      celery logger.error('; '.join(result.errors.deep_errors)) quote.save() return redirect('accept-quote-success-page') data = { 'quote': quote, 'form': form, } return render(request, template_name, data)
      
      







それは非常にシンプルであるため、一見魅力的です。すべてのコードが1か所にあるため、脳に負担をかけたり、何かを抽象化する必要はありません。 しかし、これは一見しただけです。 このアプローチはうまく拡張できず、すぐに読みやすさが失われます。 500行のコードのパフォーマンスを見る幸運がありました。そこから、経験豊富な開発者でさえ頬骨を運転し、拳を握り締めました。 厚いビューはコードの重複と複雑さを招き、それらをテストおよびデバッグすることは難しく、その結果、簡単に壊れてしまいます。



フォームのロジック



djangoのフォームはオブジェクト指向であり、データを検証およびクリーンアップします。そのため、フォームはロジックの場所と見なすこともできます。



 def accept_quote(request, quote_id, template_name="accept-quote.html"): quote = Quote.objects.get(id=quote_id) form = AcceptQuoteForm() if request.METHOD == 'POST': form = AcceptQuoteForm(request.POST) if form.is_valid(): #     form.accept_quote() success = form.charge_commission() return redirect('accept-quote-success-page') data = { 'quote': quote, 'form': form, } return render(request, template_name, data)
      
      







すでに良い。 問題は、支払い承認フォームでクレジットカード手数料も処理していることです。 ネコミルフォ。 この関数を他の場所で使用したい場合はどうしますか? もちろん、私たちは賢く、必要な不純物をエンコードできますが、コンソール、セロリ、または他の外部アプリケーションでこのロジックが必要な場合はどうでしょうか? モデルを使用するためにフォームをインスタンス化する決定は正しくありません。



クラスベースビューのコード



このアプローチは以前のものと非常に似ています-同じ利点、同じ欠点。 コンソールまたは外部アプリケーションからロジックにアクセスすることはできません。 さらに、プロジェクトでのビューの継承のスキームは複雑です。



utils.py



別のシンプルで魅力的なアプローチは、サブミッションからすべてのサブコードを抽象化し、それをユーティリティ関数として別のファイルに配置することです。 すべての問題(多くの人が最終的に選択する)に対する迅速な解決策のように思えますが、少し考えてみましょう。



 def accept_quote(request, quote_id, template_name="accept-quote.html"): quote = Quote.objects.get(id=quote_id) form = AcceptQuoteForm() if request.METHOD == 'POST': form = AcceptQuoteForm(request.POST) if form.is_valid(): #    utility- accept_quote_and_charge(quote) return redirect('accept-quote-success-page') data = { 'quote': quote, 'form': form, } return render(request, template_name, data)
      
      







最初の問題は、そのような関数の場所が明らかではない可能性があり、特定のモデルに関連するコードがいくつかのパッケージに広がる可能性があることです。 2番目の問題は、プロジェクトが大きくなり、新しい人や他の人が作業を開始すると、どの機能が存在するかが明確にならないことです。 その結果、開発時間が長くなり、開発者を悩ませ、コードの重複を引き起こす可能性があります。 特定のことはコードレビューで把握できますが、これは事後解決策です。



ソリューション:脂肪モデルと脂肪マネージャー



モデルとそのマネージャーは、特にそのようなコードが論理的または機能的にORMの機能に結び付けられている場合、データの処理と更新を目的としたコードをカプセル化する理想的な場所です。 本質的に、独自のメソッドを使用してモデルAPIを拡張します。



 def accept_quote(request, quote_id, template_name="accept-quote.html"): quote = Quote.objects.get(id=quote_id) form = AcceptQuoteForm() if request.METHOD == 'POST': form = AcceptQuoteForm(request.POST) if form.is_valid(): #      quote.accept() return redirect('accept-quote-success-page') data = { 'quote': quote, 'form': form, } return render(request, template_name, data)
      
      







私の好みでは、このようなソリューションが最も正しいです。 クレジットカードを処理するためのコードはエレガントにカプセル化され、ロジックはそれに関連する場所にあり、必要な機能は簡単に見つけて(再)使用できます。



要約:一般的なアルゴリズム



コードbleを書く場所@ t ロジックがリクエストオブジェクトに関連付けられている場合は、おそらくビュー内の場所です。 それ以外の場合は、次のオプションの順序を考慮してください。



どのオプションも適合しない場合は、別のユーティリティ関数に抽象化することを検討する価値があります。



TL; DR



モデルのロジックは、 髪の毛だけなく 、djangoアプリケーションを改善します



ボーナス



元のトピックへのコメントでは、記事のトピックに近い2つの興味深いアプリケーションへのリンクをスリップしました。



github.com/kmmbvnr/django-fsm-django モデルのステートマシンサポート (説明から)。 モデルにFSMFieldフィールドを設定し、 受信者の精神のデコレータを使用して、事前定義された状態の変化を追跡します。



github.com/adamhaney/django-ondelta- モデルのフィールドの変更を処理できるdjango-modelsの混合物。 独自のclean _ *モデルメソッドのスタイルでAPIを提供します 。 説明どおりです。



そこでは、ビジネスロジックに関連するすべてのコードを別のモジュールに抽象化する別のアプローチが提案されました。 たとえば、価格アプリケーションでは、処理モジュールで価格処理を担当するすべてのコードを選択します。 utils.pyアプローチと同様に、すべてではなくビジネスロジックを抽象化する点が異なります。



私自身のプロジェクトでは、通常、記事の著者のアプローチを使用し、次のロジックに従います。



話し合いますか?



All Articles