Django: как глобально переопределить textarea с помощью пользовательского класса?
Я пытаюсь переопределить виджеты (например, textarea) глобально в Django.
Для этого я определил собственный django/form/widgets/textarea.html
файл.
Сначала я хотел изменить количество строк по умолчанию. Файл textarea.html
будет выглядеть следующим образом:
<textarea
rows="3"
name="{{ widget.name }}"
{% include "django/forms/widgets/attrs.html" %}
>{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
Приведенный выше пример работает идеально.
Но что, если я хочу добавить пользовательский класс сейчас?
<textarea
rows="3"
class="my-custom-class"
name="{{ widget.name }}"
{% include "django/forms/widgets/attrs.html" %}
>{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
Это не будет работать так, как ожидается, потому что attrs.html
должен обрабатывать дополнительные атрибуты, такие как class
. Следовательно, определение атрибута класса непосредственно в textarea нарушит это поведение и сотрет существующие классы.
Каков "чистый" способ глобального добавления дополнительных классов?
Я хотел бы сделать это в HTML, а не в Python. Например, я не хочу определять новый виджет, который будет наследоваться от базового виджета, когда я определяю свои формы, поскольку я считаю, что это чистая тема разметки и не должна вмешиваться в определение формы.
Спасибо.
С помощью этого вопроса я нашел решение. Не стесняйтесь улучшить его, если вы считаете, что у вас есть лучшее решение.
TL/DR: мы создаем пользовательский фильтр для определения типа виджета, сопоставления с ним классов, а затем используем django-widget-tweaks для внедрения класса в поле, не изменяя существующие.
Теперь я могу настраивать все свои классы виджетов из одного места, без наследования Python и без переопределения html-файлов виджетов Django.
Тег шаблона
from django import template
register = template.Library()
BASIC_INPUT = "border border-indigo-300 px-2.5 py-1.5 rounded-md focus:outline-none"
mapping = {
"Select": BASIC_INPUT,
"TextInput": BASIC_INPUT,
"EmailInput": BASIC_INPUT,
"RegionalPhoneNumberWidget": BASIC_INPUT,
"ModelSelect2": "", # let the default markup
# add all widgets you could use, or use a default one and override the others.
}
@register.filter("get_field_classes")
def get_field_classes(field):
widget_class_name = field.field.widget.__class__.__name__
try:
return mapping[widget_class_name]
except KeyError:
# you could use default classes rather than raising an error if you prefer
raise ValueError(f"Classes related to {widget_class_name} are not defined yet")
Затем в шаблоне формы по умолчанию (например: django/forms/default.html
), при просмотре полей, с помощью пакета django-widget-tweaks:
{% load widget_tweaks %}
{% load get_field_classes %}
{% for field in form.visible_fields %}
{# .... #}
<div>
{% with field_classes=field|get_field_classes %}
{{ field|add_class:field_classes }}
{% endwith %}
</div>
{% endfor %}