Как изменить виджет по умолчанию для всех полей даты 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()

...

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

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

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

мысли и ввод ценятся.

6 ответов


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

class SomeForm(forms.ModelForm)
    formfield_callback = make_custom_datefield

    class Meta:
        # normal modelform stuff here...

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

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

существует также более короткий путь:

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

     class Meta:
         model = Article

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

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

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


в этой статье помогал мне много раз.

мясо это предполагает переопределение 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

этот метод может показаться более сложным, чем Василь, но он предлагает дополнительное преимущество в том, что он может точно переопределить любой атрибут в поле без сброса каких-либо других атрибутов путем повторного объявления он.

обновление: предлагаемый подход можно обобщить, чтобы изменить все поля даты без ввода каждого имени строго:

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


Я использую 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}}

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

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


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