Как изменить виджет по умолчанию для всех полей даты Django в ModelForm?

Учитывая набор типичных моделей:

# 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()

...

Как можно изменить виджет по умолчанию для всех DateFields на пользовательский MyDateWidget?

Я прошу, потому что я хочу, чтобы мое приложение получало датпикер jQueryUI для ввода дат.

Я рассмотрел настраиваемое поле, которое расширяет django.db.models.DateField с помощью моего настраиваемого виджета. Является ли это лучшим способом реализации такого рода изменений? Для такого изменения потребуется специально импортировать специальный MyDateField в каждую модель, которая является трудоемкой, подверженной ошибкам разработчиков (т.е. Несколько моделей .DateField будет проходить), и, на мой взгляд, это похоже на ненужное дублирование усилий. С другой стороны, мне не нравится изменять то, что можно считать канонической версией models.DateField.

Мысли и вводят оценку.

Ответ 1

Вы можете объявить атрибут в своем классе ModelForm под названием formfield_callback. Это должна быть функция, которая принимает экземпляр Field модели Django в качестве аргумента и возвращает экземпляр формы 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()

class SomeForm(forms.ModelForm)
    formfield_callback = make_custom_datefield

    class Meta:
        # normal modelform stuff here...

Ответ 2

Эта статья помогла мне много раз.

Мясо его включает в себя переопределение метода 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's, но он предлагает дополнительное преимущество, позволяющее точно переопределить любой атрибут в поле без сброса каких-либо других атрибутов путем повторного объявления его.

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

Это работало для меня на python3 и django 1.11

Ответ 3

Ну, создание настраиваемого поля модели, чтобы изменить его виджет формы по умолчанию, на самом деле не является очевидным местом для начала.

Вы можете создать свой собственный виджет формы и переопределить поле в форме, указав свой собственный виджет, как в ответе Soviut.

Там также более короткий путь:

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

     class Meta:
         model = Article

Есть пример того, как писать виджеты форм, это где-то в пакете форм Django. Это датапикер с 3 выпадающими меню.

Что я обычно делаю, когда просто хочу добавить JavaScript в стандартный элемент ввода HTML, оставьте его так, как он есть, и измените его, указав его позже с помощью JavaScript. Вы можете легко поймать соглашение об именах для идентификаторов полей ввода Django.

Вы также можете просто предоставить класс для виджета, когда вы переопределите его в форме. Затем перехватите их всех jQuery по имени класса.

Ответ 4

Я использую JQuery. Вам нужно только искать "идентификатор" полей, которые вы хотите связать с выбором даты, и связать их с 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}}

Ответ 5

Вы хотите определить пользовательский виджет и использовать виджет внутренний класс мультимедиа для определения файлов JS (и CSS?), которые должны быть включены в страницу для работы виджета. Если вы сделаете это правильно, вы можете сделать свой виджет полностью автономным и многоразовым. См. django-markitup для одного пример этого (он имеет многоразовый виджет для MarkItUp! универсальный редактор разметки).

Затем используйте formfield_callback (см. ответ Джеймса Беннетта), чтобы легко применить этот виджет ко всем DateField в форме.

Ответ 6

Некоторые могут нахмуриться, но, заменив сборщик дат вашим специальным виджетами, я бы уничтожал приложение monkeypatch для вашего проекта и исправлял сам 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.