Djangoのカスタムフォームフィールド



こんにちは、Habr!

私はDjangoフレームワークの大ファンであり、すべてのプロジェクトをそれだけで記述しています。 今日は、独自のソリューションでフォームフィールドの標準ライブラリを拡張する方法について説明します。 この記事の目的は、ターンキーソリューションを提供することではなく、カスタムフィールドを作成する技術を明らかにすることです。

小さな余談。 かつて、私はその時に働いていた会社の知識ベースの作成に取り組んでいました。 ベースは、タグでタグ付けされた記事のコレクションでした。 タグエントリ要素には次の要件が課されました。



いくつかの検索の後、jQuery Tag-Itプラグインを見つけました ウィジェットの要件を完全に満たしていました。 このフィールドをDjangoにねじ込むだけです。





オートコンプリートデータソース



ウィジェットはモデルからデータを取得します。実際には、入力されたタグが保存されます

from django.db import models from unicodedata import category from django.utils.http import urlquote import re class Tag(models.Model): """ Model of Tags """ name = models.CharField(max_length=200, null=False, verbose_name="Tag name") slug = models.CharField(max_length=400, editable=False, verbose_name=u'Slug', unique=True, null=False) def __unicode__(self): return self.name @staticmethod def _generate_slug(value): slug = ''.join(ch for ch in value[:200] if category(ch)[0] != 'P') return urlquote(re.sub(r'([ ]+_)|(_[ ]+)|([ ]+)', '_', slug)) def save(self, *args, **kwargs): self.name = self.name.lower() self.slug = self._generate_slug(self.name) super(Tag, self).save(*args, **kwargs) @classmethod def get_or_create(cls, value): slug = cls._generate_slug(value.lower().strip()) if cls.objects.filter(slug=slug).exists(): return cls.objects.get(slug=slug) else: return cls.objects.create(name=value.lower().strip())
      
      





タグにはスペースやその他のゴミが含まれる可能性があるため、モデルにslugフィールドを導入します。このフィールドは、タグ名の単語間にスペースがいくつあるかに関係なく、コンテンツによってタグを明確に識別します。 また、クラスメソッドget_or_createを導入します。このメソッドは、slugフィールドでタグが見つかった場合にタグを返すか、そうでない場合は新しいタグを作成します。 さらに、新しいタグを作成する前に、一貫性を保つためにsaveメソッドで小文字にキャストします。



自動補完の表示



入力した文字で始まるタグのリストを返す小さなビューの概要を説明します。

Tag-Itプラグイン! 入力された文字列を用語変数に渡します。

 from models import Tag from django.http import HttpResponse import json def tag_autocomplete(request): """ url: /tag_autocomplete/""" value = request.GET['term'] available_tags = Tag.objects.filter(name__startswith=value.lower()) response = HttpResponse(json.dumps([unicode(tag) for tag in available_tags]), content_type="application/json") return response
      
      







ウィジェットとフォームフィールド



ウィジェットとフォームフィールドは、アプリケーションの場所(forms.py)で直接宣言できます。 他の場所で使用する予定はなかったので、そうしました。

Tag-Itプラグインが視覚化を行うため、非表示の入力フィールドからウィジェットを継承しました!..

 from django import forms class TagitWidget(forms.HiddenInput): """ Widget on the basis of Tag-It! http://aehlke.github.com/tag-it/""" class Media: js = (settings.STATIC_URL + 'js/tag-it.js', settings.STATIC_URL + 'js/tagit_widget.js',) css = {"all": (settings.STATIC_URL + 'css/jquery.tagit.css',)}
      
      





tag-it.jsおよびjquery.tagit.cssは、Tag-Itプラグインのファイルです!.. tagit_widget.jsの内容については、以下で説明します。



 class TagitField(forms.Field): """ Tag field """ widget = TagitWidget def __init__(self, tag_model, *args, **kwargs): self.tag_model = tag_model super(TagitField, self).__init__(*args, **kwargs) def to_python(self, value): tag_strings = value.split(',') return [self.tag_model.get_or_create(tag_string) for tag_string in tag_strings if len(tag_string) > 0] def validate(self, value): if len(value) == 0 and self.required: raise ValidationError(self.error_messages['required']) def prepare_value(self, value): if value is not None and hasattr(value, '__iter__'): return ','.join((unicode(tag) for tag in value)) return value def widget_attrs(self, widget): res = super(TagitField, self).widget_attrs(widget) or {} res["class"] = "tagit" return res
      
      





フォームフィールドの宣言で、ウィジェットを指定します。 通常のパラメーターに加えて、コンストラクターにタグモデルを渡します。タグモデルを使用して、タグ名のリストをto_pythonメソッドでタグオブジェクトのリストに変換します。 prepare_valueメソッドは逆変換を行います。 widget_attrsメソッドで、「class」属性を非表示フィールドに追加します。これにより、スクリプトはTag-Itプラグインを適用するために必要なフィールドを見つけます!..

スクリプト自体はtagit_widget.jsファイルにあり、次の形式になっています。

 $(document).ready(function() { $(".tagit").tagit({ allowSpaces: true, autocomplete: {delay: 0, minLength: 2, source: "/tag_autocomplete/" } }); });
      
      





追加のプラグインオプションはこちらにあります 。 ここでは、タグにスペース(allowSpaces)を含めることを許可し、入力後に遅延なしで自動補完を実行し(遅延)、入力した2番目の文字から(minLength)、ビューからオプションを取得する(ソース)としか言えません。



おわりに



フィールドを使用する準備ができました。 次のように適用できます。

 from models import Tag class SomeForm(forms.Form): tag = TagitField(Tag, label='Tags', required=True)
      
      





主なことは、テンプレート内のこのフォームから静的を接続することを忘れないことです

 <!doctype html> <html> <head> <title>Tag-It!</title> {{some_form.media}} </head> <body> <form action=""> {{some_form.as_p}} </form> </body> </html>
      
      





素敵なジャンゴコーディングが必要です。



All Articles