Как изменить виджет по умолчанию для всех полей даты Django в ModelForm? - PullRequest
39 голосов
/ 19 марта 2009

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

# 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 справится), и, на мой взгляд, кажется ненужным дублированием усилий. С другой стороны, я не люблю изменять то, что можно считать канонической версией моделей. DateField.

Мысли и вклад приветствуются.

Ответы [ 6 ]

56 голосов
/ 19 марта 2009

Вы можете объявить атрибут вашего класса 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...
6 голосов
/ 19 марта 2009

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

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

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

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

     class Meta:
         model = Article

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

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

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

6 голосов
/ 19 марта 2009

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

В основе этого лежит переопределение метода __init__ ModelForm, затем вызов метода __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

3 голосов
/ 24 июля 2010

Я использую 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}}
1 голос
/ 04 июня 2013

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

1 голос
/ 19 марта 2009

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

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

...