Pythonを使用してHTMLフォームを処理します。

私が初めてdjangoを使い始めたとき、ORMの後の最も楽しい瞬間はdjango.formsパッケージでした。 djangoは過去のものになりました-Werkzeug + SqlAlchemy + Jinja2スタックを使用し、SqlAlchemyではなく非リレーショナルデータストアを試すこともあります。 しかし、django.formsに代わるものは見つかりませんでした。 したがって、私はすぐに自分の何かをスケッチすることにしました。



その結果、私は次の説明に至りました。 入力では、dictタイプで表されるデータがあり、この辞書のキーは文字列で、値は文字列または同じ構造の他の辞書です。 例:



データ= {
     「key1」:「value1」
     「key2」:{
         「key3」:「value3」
     }
 }




さらに、このデータに関していくつかの仮定があります-スキームと呼ばれるルールのセットです。 ここで、データディクショナリのすべてのフィールドを調べて、その値が正しいかどうかを確認し、必要なタイプに導く方法が必要です。 すべてがシンプルです!



これは、非常に理解しやすい実装要件を意味します。



*スキームを記述する簡単な方法-明確で便利な、つまり宣言的であること。

*コードの再利用-同じスキームを10回記述するのはかなり面倒です。

*ネストされたデータ構造のスキーマの定義-これが必要になる場合があります。



実装の基本原則

基本原則



次の例外を除き、データ検証エラーが記述されることが想定されています。



クラスSchemaValidationError(TypeError):
    def __init __(自己、エラー):
        self.error =エラー




データ検証は、実際にはデータ型の分析であるため、標準のTypeError例外を継承することが適切であると思います。



スキームは、フィールドを記述するオブジェクトとなる属性を持つクラスの形式で設定されます。 ネストされた構造を記述したいので、使用できる属性は文字列フィールドオブジェクトと他のスキームの両方です。 最初のステップは次のとおりです。



クラスSchemaElement(オブジェクト):
     u "" "
    回路要素の抽象クラス。
     「」「
     def validate(self、data):
         NotImplementedError()を発生させます

クラスField(SchemaElement):
    u "" "
    Fieldクラスは、文字列フィールドを記述します。
    「」「
    def validate(self、data):
        SchemaValidationError(「無効な値」)を発生させます

クラススキーマ(SchemaElement):
    u "" "
    Schemaクラスは検証スキームを記述します。
    「」「
    def validate(self、data):
        #データ検証コードデータ
       データを返す




回路要素はフィールドまたは別の回路のいずれかであるため、一般的なSchemaElementクラスからフィールドとスキーマを継承しました。 これは複合設計パターンであり、階層データ型の記述に最適です。



SchemaElementは、検証用の抽象インターフェイス、validateメソッドも定義します。 実際、このインターフェイスに続いて、検証の観点からFieldオブジェクトとSchemaオブジェクトを区別することはできません。私たちにとっては、まったく同じです。



Fieldクラスの子孫は、スキーマのフィールドの記述、つまり文字列値の処理に使用されます。 特定のフィールドにデータ検証アルゴリズムを実装するには、validateメソッドをオーバーライドする必要があります。このメソッドは、エラーが発生した場合に正しいデータデータと縮小データデータを返すか、SchemaValidationError例外をスローします。 デフォルトの実装では常に例外がスローされます。



Schemaクラスは、フィールドと他のスキームで構成される構造を記述するために使用されます。 検証メソッドのコードは少し後で表示されます。

回路の宣言的説明



すでに述べたように、最も成功したタスクは、属性が他のFieldおよびSchemaオブジェクトであるクラスの形式でスキームを定義することです。 これは宣言的記述と呼ばれます。 これを実装するには、Schemaコンテナクラスのメタクラスが必要です。



クラスSchemaMeta(タイプ):
    def __new __(mcs、name、bases、attrs):
       名前でない場合== "スキーマ":
           フィールド= {}
           逆向きのベース(ベース)の場合:
                issubclass(ベース、スキーマ)であり、ベースがスキーマではない場合:
                    fields.update(ベース.__ fields__)
            field_nameの場合、attrs.items()のフィールド:
                isinstance(フィールド、SchemaElement)の場合:
                   フィールド[フィールド名] = attrs [フィールド名]
            attrs ["__ fields__"] =フィールド
        cls = type .__ new __(mcs、name、bases、attrs)
        clsを返す

    def __contains __(cls、value):
        cls .__ fields__の戻り値

    def __iter __(cls):
        return cls .__ fields __。items().__ iter __()




このメタクラスを使用する主な理由は、スキーマのすべてのフィールドをグループ化し、__ fields__属性に配置することです。 __fields__には不要なガベージが含まれていないため、毎回__dict__を回っているように、フィールドを処理したり、構造をイントロスペクトするときに便利です。



Schemaという名前のクラスを作成する場合、メタクラスはそれを処理しません。Schemaを継承する別のクラスである場合は、最初に__fields__のスーパークラスのすべてのフィールドを右から左に収集してから、現在のクラスのフィールドをそこに追加します。



また、指定された名前のフィールドがスキーム内に含まれているかどうかを確認する__contains__メソッドと、スキームを持つクラスを反復可能にする__iter__メソッドも追加しました。 これらのメソッドをメタクラスで定義したため、クラスメソッドを取得します。これは、オブジェクトメソッドでclassmethodデコレータを使用するのと同等です。



ここで、__ metaclass__属性をSchemaクラスに追加します。



クラススキーマ(SchemaElement):
     ...
     __metaclass__ = SchemaMeta
     ...




次のようにスキームをすでに定義できます。



 >>>クラスMySchema(スキーマ):
 ... my_field =フィールド()

 >>> AnotherSchemaクラス(MySchema):
 ... another_field =フィールド()

 >>> MySchemaの「my_field」
本当
 >>> AnotherSchemaの "another_field"
本当
 >>> AnotherSchemaの「my_field」
本当




スキーマの継承は機能します-my_field属性はAnotherSchemaスキーマにも現れました。 階層データ構造を検証するためのスキーマを作成するには、スキーマ属性を持つ別のスキーマを追加するだけです。



 >>>クラスCompositeSchema(スキーマ):
         sub_schema = MySchema()
         my_field =フィールド()

 >>> CompositeSchemaの「my_field」
本当
 >>> CompositeSchemaの「sub_schema」
本当
 >>> CompositeSchema.sub_schemaの「my_field」
本当




データ検証



検証はvalidateメソッドによって実行され、Fieldクラスのオブジェクト自体がそれをオーバーライドする必要があります、私がここで与えるSchemaクラスのvalidateメソッドの実装:



クラススキーマ(SchemaElement):
    ...
    def validate(self、data):
       エラー= {}
        field_nameには、self .__ fields __。items()のフィールド:
           試してください:
                data [field_name] = field.validate(data.get(field_name、None))
            SchemaValidationErrorを除き、エラー:
               エラー[フィールド名] = error.error
       エラーの場合:
            SchemaValidationError(エラー)を発生させます
       データを返す
    ...




まず、スキーマの有効な各フィールドは、データディクショナリからの目的のパラメーターを使用してvalidateメソッドを呼び出します。 エラーがある場合、それはキャッチされ、エラーディクショナリに保存されます。 すべてのフィールドを調べた後、エラーディクショナリがチェックされ、空でない場合、このディクショナリをパラメータとしてSchemaValidationError例外がスローされます。 これにより、階層の最下位レベルからすべてのエラーを収集できます。



これで、いくつかの基本的なフィールドとスキームを定義して、実際にデータ検証を試すことができます。



クラスNotEmptyField(フィールド):
     u "" "
    空にできないフィールドを記述するクラス。
     「」「
     def validate(self、data):
         「フィールド検証」を印刷
        データでない場合:
             SchemaValidationError(「空のフィールド」)を発生させます

クラスCustomSchema(スキーマ):
     not_empty_field = NotEmptyField()

     def validate(self、data):
         「スキーマフィールドの検証」を出力します
         data = super(CustomSchema、self).validate(data)
         「スキーマレベル検証コード」を印刷
        データを返す




validateメソッド内で、スーパークラスのvalidateメソッドを必ず呼び出す必要があります。 また、データを返すか、SchemaValidationError例外をスローする必要があります。 フォームをチェックします:



 >>> schema = CustomSchema()
 >>>試してください:
 ... schema.validate({"not_empty_field": "some value"})
 ... SchemaValidationErrorを除く、e:
 ...エラー= e.error
スキーマフィールドの検証
フィールド検証
スキーマレベル検証コード
 >>> schema.errors
 {}




次に、検証用の無効なデータを提供してみましょう。



 >>>試してください:
 ... schema.validate({"not_empty_field": ""})
 ... SchemaValidationErrorを除く、e:
 ...エラー= e.error
まず、スキーマフィールドを検証します
フィールド検証
 >>>エラー
 {「not_empty_field」:「空のフィールド」}




予想どおり、データ検証は失敗しました。

おわりに



そのため、データ検証用の小さいながらも強力なライブラリがあります。 もちろん、必要なフィールド(フィールドを継承するクラス)を補充する必要があります。 ところで、それは非常にコンパクトであることが判明しました-130行以下です。 ソースコードを取得したい場合は、私に書くことができます。



All Articles