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 %}
Вернуться на верх