web-dev-qa-db-ja.com

ModelFormのすべてのDjango日付フィールドのデフォルトウィジェットをどのように変更しますか?

典型的なモデルのセットを考えると:

# Application A
from Django.db import models
class TypicalModelA(models.Model):
    the_date = models.DateField()

 # Application B
from Django.db import models
class TypicalModelB(models.Model):
    another_date = models.DateField()

...

all DateFieldsのデフォルトウィジェットをカスタムMyDateWidgetに変更するにはどうすればよいですか?

アプリケーションに日付を入力するためのjQueryUI日付ピッカーを持たせたいので質問しています。

カスタムウィジェットを使用してDjango.db.models.DateFieldを拡張するカスタムフィールドを検討しました。これは、この種の全面的な変更を実装するための最良の方法ですか?このような変更には、特別なMyDateFieldをすべてのモデルに特別にインポートする必要があります。これは、労働集約的で、開発者エラーが発生しやすく(つまり、いくつかのモデルです。DateFieldは通過します)、私の考えでは、不必要な作業の重複のようです。一方、models.DateFieldの正規バージョンと見なされるものを変更するのは好きではありません。

考えと入力をいただければ幸いです。

41
Brian M. Hunt

ModelFormクラスでformfield_callbackという属性を宣言できます。これは、引数としてDjango model Fieldインスタンスを取り、それをフォームで表すためにフォームFieldインスタンスを返す関数である必要があります。

次に、渡されたモデルフィールドがDateFieldのインスタンスであるかどうかを確認し、インスタンスである場合は、カスタムフィールド/ウィジェットを返すだけです。そうでない場合、モデルフィールドにはformfieldという名前のメソッドがあり、これを呼び出してデフォルトのフォームフィールドを返すことができます。

だから、次のようなもの:

def make_custom_datefield(f):
    if isinstance(f, models.DateField):
        # return form field with your custom widget here...
    else:
        return f.formfield(**kwargs)

class SomeForm(forms.ModelForm)
    formfield_callback = make_custom_datefield

    class Meta:
        # normal modelform stuff here...
57
James Bennett

この記事 私は何度も助けてくれました。

その要点は、ModelFormの__init__メソッドをオーバーライドし、次にスーパークラスの__init__メソッドを呼び出し、フィールドを個別に調整することです。

class PollForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(PollForm, self).__init__(*args, **kwargs)
        self.fields['question'].widget = forms.Textarea()

    class Meta:
        model = Poll

このメソッドはVasilのメソッドよりも複雑に見えるかもしれませんが、フィールドを再宣言することで他の属性をリセットすることなく、フィールドの属性を正確にオーバーライドできるという追加の利点があります。

UPDATE:推奨されるアプローチは、各名前を厳密に入力せずにすべての日付フィールドを変更するように一般化できます。

from Django.forms import fields as formfields
from Django.contrib.admin import widgets

class PollForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(PollForm, self).__init__(*args, **kwargs)
        for field_name in self.fields:
            field = self.fields[field_name]
            if isinstance(field, formfields.DateField):
                field.widget = widgets.AdminDateWidget()

    class Meta:
        model = Poll

それはpython3Django 1.11で私のために働いた

7
Soviut

さて、デフォルトのフォームウィジェットを変更するためだけにカスタムモデルフィールドを作成することは、実際には明白な出発点ではありません。

Soviutの回答のように、独自のウィジェットを指定して、独自のフォームウィジェットを作成し、フォームのフィールドをオーバーライドできます。

より短い方法もあります:

class ArticleForm(ModelForm):
     pub_date = DateField(widget=MyDateWidget())

     class Meta:
         model = Article

フォームウィジェットの書き方の例があります。それはDjangoのフォームパッケージのどこかにあります。これは、3つのドロップダウンがある日付ピッカーです。

標準のHTML入力要素にJavaScriptを追加したいときに通常行うことは、そのままにして、後でJavaScriptでIDを参照して変更することです。 Djangoが生成する入力フィールドのIDの命名規則を簡単に理解できます。

フォームでウィジェットをオーバーライドするときに、ウィジェットのクラスを提供することもできます。次に、クラス名で jQuery を使用してそれらすべてをキャッチします。

6
Vasil

私はJQueryを使用しています。日付ピッカーに関連付けるフィールドの「id」を探して、JQueryと適切な表示形式でバインドするだけです。

models.py

class ObjectForm(ModelForm):
    class Meta:
        model = Object        
        fields = ['FieldName1','FieldName2']

ビューでレンダリングするページの上部:

<head>
    <link type="text/css" href="/media/css/ui-darkness/jquery-ui-1.8.2.custom.css" rel="Stylesheet" /> 
    <script type="text/javascript" src="/media/js/jquery-1.4.2.min.js"></script>
    <script type="text/javascript" src="/media/js/jquery-ui-1.8.2.custom.min.js"></script>
</head>
<script type="text/javascript">
 $(function() {
        $("#id_FieldName1").datepicker({ dateFormat: 'yy-mm-dd' });
        $("#id_FieldName2").datepicker({ dateFormat: 'yy-mm-dd' });
 });
</script>
...
{{form}}
3
MarC

カスタムウィジェットを定義し、ウィジェットの 内部メディアクラス を使用して、ウィジェットが機能するためにページに含める必要のあるJS(およびCSS?)ファイルを定義する必要があります。これを正しく行うと、ウィジェットを完全に自己完結型で再利用可能にすることができます。 Django-markitup を参照してください これを行う例MarkItUp!ユニバーサルマークアップエディター 用の再利用可能なウィジェットがあります)。

次に、formfield_callback(James Bennettの回答を参照)を使用して、そのウィジェットをフォーム内のすべてのDateFieldに簡単に適用します。

1
Carl Meyer

これに眉をひそめる人もいるかもしれませんが、日付ピッカーをカスタムウィジェットに置き換えるには、プロジェクトのモンキーパッチアプリを作成し、実行時にパッチDjango自体を作成します。これの利点は、サードパーティのアプリが同様に影響を受けるため、サードパーティのコードを変更することなく、エンドユーザーに統一されたインターフェイスを提示します。

from Django.forms.widgets import DateInput , DateTimeInput, TimeInput
from FOO.widgets import MyjQueryWidget

# be Nice and tell you are patching
logger.info("Patching 'DateInput.widget = MyjQueryWidget': Replaces Django DateInput to use my new super  'MyjQueryWidget'")

# be nicer and confirm signature of code we are patching and warn if it has changed - uncomment below to get the current hash fingerprint
# raise Exception(hashlib.md5(inspect.getsource(DateInput.widget)).hexdigest()) # uncommet to find latest hash
if not '<enter hexdigest fingerprint here>' == \
        hashlib.md5(inspect.getsource(DateInput.widget)).hexdigest():
    logger.warn("md5 signature of 'DateInput.widget' does not match Django 1.5. There is a slight chance patch "
                    "might be broken so please compare and update this monkeypatch.")

# be nicest and also update __doc__
DateInput.__doc__ = "*Monkeypatched by <app name>*: Replaced Django DateInput.widget with my new super  'MyjQueryWidget'" + DateInput.__doc__ 

DateInput.widget = MyjQueryWidget

上記は、プロジェクトの一部として使用しているhtml5monkeypatchから着想を得たものです。 patch_widgets.py および patch_fields.py をご覧ください。

1