web-dev-qa-db-ja.com

DjangoフォームはMVCに違反していますか?

私は、長年のSpring MVCから来たDjangoで作業を開始したばかりで、フォームの実装は少しおかしくなっています。慣れていない場合は、 Djangoフォーム は、フィールドを定義するフォームモデルクラスから始まります。 Springも同様に、フォームバッキングオブジェクトから始まります。ただし、SpringがJSP内のバッキングオブジェクトにフォーム要素をバインドするための taglib を提供する場合、Djangoはモデルに直接関連付けられたフォームウィジェットを備えています。 CSSを適用したり、完全にカスタムウィジェットを新しいクラスとして定義したりするために、フィールドにスタイル属性を追加できるデフォルトのウィジェットがあります。それはすべてpythonコードに含まれています。それは私には不自然に思えます。最初に、ビューに関する情報をモデルに直接配置し、次に、モデルを特定のビューにバインドします。何か不足していますか?

編集:要求に応じていくつかのサンプルコード。

Django:

# Class defines the data associated with this form
class CommentForm(forms.Form):
    # name is CharField and the argument tells Django to use a <input type="text">
    # and add the CSS class "special" as an attribute. The kind of thing that should
    # go in a template
    name = forms.CharField(
                widget=forms.TextInput(attrs={'class':'special'}))
    url = forms.URLField()
    # Again, comment is <input type="text" size="40" /> even though input box size
    # is a visual design constraint and not tied to the data model
    comment = forms.CharField(
               widget=forms.TextInput(attrs={'size':'40'}))

春のMVC:

public class User {
    // Form class in this case is a POJO, passed to the template in the controller
    private String firstName;
    private String lastName;
    get/setWhatever() {}
}

<!-- JSP code references an instance of type User with custom tags -->
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!-- "user" is the name assigned to a User instance -->
<form:form commandName="user">
      <table>
          <tr>
              <td>First Name:</td>
              <!-- "path" attribute sets the name field and binds to object on backend -->
              <td><form:input path="firstName" class="special" /></td>
          </tr>
          <tr>
              <td>Last Name:</td>
              <td><form:input path="lastName" size="40" /></td>
          </tr>
          <tr>
              <td colspan="2">
                  <input type="submit" value="Save Changes" />
              </td>
          </tr>
      </table>
  </form:form>
16
jiggy

はい、DjangoフォームはMVCの観点からみて混乱しています。大きなMMOスーパーヒーローゲームで作業していて、ヒーローモデルを作成しているとしましょう。 :

class Hero(models.Model):
    can_fly = models.BooleanField(default=False)
    has_laser = models.BooleanField(default=False)
    has_shark_repellent = models.BooleanField(default=False)

MMOプレイヤーがヒーローのスーパーパワーを入力できるように、フォームを作成するように求められます。

class HeroForm(forms.ModelForm):
    class Meta:
        model = Hero

サメ忌避剤は非常に強力な武器であるため、上司から制限を求められました。ヒーローがサメ忌避剤を持っている場合、彼は飛ぶことができません。ほとんどの人は、このビジネスルールをcleanの形式で追加し、1日と呼びます。

class HeroForm(forms.ModelForm):
    class Meta:
        model = Hero

    def clean(self):
        cleaned_data = super(HeroForm, self).clean()
        if cleaned_data['has_shark_repellent'] and cleaned_data['can_fly']:
            raise ValidationError("You cannot fly and repel sharks!")

このパターンはクールに見え、小規模なプロジェクトで機能する可能性がありますが、私の経験では、これを複数の開発者がいる大規模なプロジェクトで維持するのは非常に困難です。問題は、フォームがMVCのviewの一部であることです。したがって、次の場合は常にそのビジネスルールを覚えておく必要があります。

  • ヒーローモデルを扱う別のフォームを作成します。
  • 別のゲームからヒーローをインポートするスクリプトを記述します。
  • ゲームのメカニズム中に手動でモデルインスタンスを変更します。
  • 等.

ここでの私のポイントは、forms.pyはすべてフォームのレイアウトとプレゼンテーションに関するものであり、スパゲッティコードをいじることを楽しんでいない限り、そのファイルにビジネスロジックを追加しないでください。

ヒーローの問題を処理する最良の方法は、 モデルのクリーンメソッド に加えてカスタム信号を使用することです。モデルクリーンはフォームクリーンと同様に機能しますが、モデル自体に格納されます。HeroFormがクリーンになると、自動的にヒーロークリーンメソッドが呼び出されます。別の開発者がヒーロー用に別のフォームを作成する場合、彼は無料で忌避剤/飛行検証を取得するため、これは良い習慣です。

クリーンの問題は、モデルがフォームによって変更されたときにのみ呼び出されることです。手動でsave()すると呼び出されず、データベースに無効なヒーローが含まれる可能性があります。この問題に対処するには、このリスナーをプロジェクトに追加します。

from Django.db.models.signals import pre_save

def call_clean(sender, instance, **kwargs):
    instance.clean()
pre_save.connect(call_clean, dispatch_uid='whata')

これにより、すべてのモデルのsave()呼び出しごとにcleanメソッドが呼び出されます。

21
Cesar Canassa

スタック全体を混合しています。いくつかのレイヤーが関係しています。

  • a Djangoモデルはデータ構造を定義します。

  • a Django=フォームは、HTMLフォーム、フィールド検証、Python/HTML値の変換を定義するためのショートカットです。厳密に必要なわけではありませんが、多くの場合便利です。

  • a Django ModelFormは、別のショートカットです。簡単に言うと、モデル定義からフィールドを取得するFormサブクラスです。フォームを使用してデータベースにデータを入力する一般的な場合に便利な方法です。

そして最後に:

  • DjangoアーキテクトはMVC構造に厳密に準拠していません。時にはMTV(モデルテンプレートビュー)と呼んでいます。コントローラーなどはなく、テンプレート(プレゼンテーションのみ、ロジックなし)とビュー(ユーザー向けロジックのみ、HTMLなし)の間の分割は、モデルの分離と同じくらい重要です。

一部の人々はそれを異端と見なします。ただし、MVCはもともとGUIアプリケーション用に定義されたものであり、Webアプリにはかなり扱いにくいものであることを覚えておくことが重要です。

8
Javier

他の答えは言及された特定の問題を回避するように見えるので、私はこの古い質問に答えています。

Djangoフォームを使用すると、簡単に小さなコードを記述し、適切なデフォルトでフォームを作成できます。任意の量のカスタマイズは非常に迅速に「より多くのコード」と「より多くの作業」につながり、フォームシステムの主な利点をいくぶん無効にします

Django-widget-tweaks のようなテンプレートライブラリにより、フォームのカスタマイズがはるかに簡単になります。うまくいけば、このようなフィールドのカスタマイズは、最終的にはVanilla Djangoインストールで簡単になります。

Django-widget-tweaksを使用した例:

{% load widget_tweaks %}
<form>
  <table>
      <tr>
          <td>Name:</td>
          <td>{% render_field form.name class="special" %}</td>
      </tr>
      <tr>
          <td>Comment:</td>
          <td>{% render_field form.comment size="40" %}</td>
      </tr>
      <tr>
          <td colspan="2">
              <input type="submit" value="Save Changes" />
          </td>
      </tr>
  </table>
4
Trey Hunner

(私はイタリックを使用して、これをより読みやすくするためにMVCの概念を示しています。)

いいえ、私の意見では、それらはMVCを壊しません。 Django Models/Formsを使用する場合、MVCスタック全体をModelとして使用していると考えてください:

  1. Django.db.models.ModelはベースModelです(データとビジネスロジックを保持します)。
  2. Django.forms.ModelFormは、Django.db.models.Modelと対話するためのControllerを提供します。
  3. Django.forms.FormDjango.forms.ModelFormによる継承を通じて提供される)は、対話するViewです。
    • ModelFormFormであるため、これにより状況がぼやけます。したがって、2つのレイヤーは密結合されます。私の意見では、これはコードを簡潔にするため、およびDjango開発者のコ​​ード内でコードを再利用するために行われました。

このようにして、Django.forms.ModelForm(データとビジネスロジックを含む)はModel自体になります。 (MVC)VCとして参照できます。これは、OOPでかなり一般的な実装です。

たとえば、DjangoのDjango.db.models.Modelクラスを見てみましょう。 Django.db.models.Modelオブジェクトを見ると、既にMVCの完全な実装であるにもかかわらずModelが表示されます。 MySQLがバックエンドデータベースであると仮定します。

  • MySQLdbModelです(データストレージレイヤー、およびデータとの対話/検証方法に関するビジネスロジック)。
  • Django.db.models.queryControllerです(-Viewからの入力を処理し、それをModelに変換します)。
  • Django.db.models.Modelビュー(ユーザーが操作するもの)です。
    • この場合、開発者(あなたと私)が「ユーザー」です。

この相互作用は、yourproject.forms.YourFormDjango.forms.ModelFormから継承)オブジェクトを操作するときの「クライアント側の開発者」と同じです。

  • Django.db.models.Modelの操作方法を知る必要があるため、yourproject.forms.YourFormの操作方法も知っている必要があります(そのModel)。
  • MySQLdbについて知る必要がないため、「クライアント側の開発者」はyourproject.models.YourModelについて何も知る必要はありません。
  • どちらの場合も、実際にControllerがどのように実装されているかについて心配する必要はほとんどありません。
1
Jack M.