Определить класс CSS в формах Django

192

Предположим, у меня есть форма

class SampleClass(forms.Form):
    name = forms.CharField(max_length=30)
    age = forms.IntegerField()
    django_hacker = forms.BooleanField(required=False)

Есть ли способ для меня определить классы CSS для каждого поля, чтобы я мог использовать jQuery на основе класса в моей отрисованной странице?

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

ashchristopher
источник
9
Вау, все проголосовали за это? docs.djangoproject.com/en/dev/ref/forms/widgets/…
Skylar Saveland
10
@skyl Я бы воспринял это как указание на то, что его нелегко найти в документации Django. Я просмотрел и сделал несколько поисков в Google тоже и не смог найти это, так что я рад за вопрос, и он получает мой upvote.
Нильс
Я знаю, что это старый поток, но в полях формы Django теперь есть класс id_fieldname.
Рацимихах
1
Посмотрите этот ответ о том, как определить классы для поля формы внутри шаблона при соблюдении существующих классов полей
dimyG

Ответы:

182

Еще одно решение, которое не требует изменений в коде Python и поэтому лучше для дизайнеров и одноразовых презентационных изменений: django-widget-tweaks . Надеюсь, кто-нибудь найдет это полезным.

Михаил Коробов
источник
61
Только вменяемое решение, я должен сказать. Спасибо!. Python-код, особенно в определении формы, является последним местом, где можно разместить элементы для стилизации - они определенно принадлежат шаблонам.
Борис Червенков
5
Это отличная библиотека! Обидно, этот ответ похоронен внизу.
Крис Лакассе
1
Отлично подходит для дизайнеров! :-)
hobbes3
1
Превосходно! Для этого я разработал несколько фильтров, но этот проект гораздо более мощный. Спасибо!
msbrogli
1
на самом деле это работает и для Jinja2 :-) Я изменил порядок безопасного файла и добавил скобки вместо двоеточия {{myform.email | add_class ("css_class_1 css_class_2") | safe}} спасибо за написание этого. это должно быть частью Джанго.
Дэвид Дехган
92

Ответил на мой собственный вопрос. Вздох

http://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.Widget.attrs

Я не понял, что это было передано в конструктор виджетов.

ashchristopher
источник
2
Означает ли это, что он не может быть использован с классом ModelForm?
xster
2
Нет, вам просто нужно явно определить поле формы в форме модели, чтобы вы могли определить виджет. Или используйте решение, перечисленное ниже, чтобы избежать необходимости вносить изменения в поле формы.
Том
78

Вот еще одно решение для добавления определений классов к виджетам после объявления полей в классе.

def __init__(self, *args, **kwargs):
    super(SampleClass, self).__init__(*args, **kwargs)
    self.fields['name'].widget.attrs['class'] = 'my_class'
ashchristopher
источник
4
Для ModelForms это часто лучше, так как вам не нужно знать об используемом поле формы по умолчанию, и вы можете динамически устанавливать различные классы в зависимости от условий выполнения. Чище, чем мета-кодирование хаков ...
Даниэль Нааб
1
Это предполагает, что вы хотите ввод для каждого поля в форме, что часто не так. Форма не обязательно связана с моделью - она ​​может быть связана со многими моделями. В случае, когда поля модели и поля формы имеют отношение 1: 1, тогда да, ModelForm - гораздо лучший выбор.
Ашхристофер
44

Используйте django-widget-tweaks , он прост в использовании и работает довольно хорошо.

В противном случае это можно сделать с помощью пользовательского шаблона фильтра.

Учитывая, что вы делаете свою форму таким образом:

<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject }}
    </div>
</form>

form.subject является экземпляром BoundField, который имеет метод as_widget .

Вы можете создать собственный фильтр "addcss" в "my_app / templatetags / myfilters.py"

from django import template

register = template.Library()

@register.filter(name='addcss')
def addcss(value, arg):
    css_classes = value.field.widget.attrs.get('class', '').split(' ')
    if css_classes and arg not in css_classes:
        css_classes = '%s %s' % (css_classes, arg)
    return value.as_widget(attrs={'class': css_classes})

А затем примените свой фильтр:

{% load myfilters %}
<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject|addcss:'MyClass' }}
    </div>
</form>

Form.subjects будет отображаться с классом «MyClass».

Надеюсь, это поможет.

РЕДАКТИРОВАТЬ 1

  • Обновить фильтр в соответствии с ответом dimyG

  • Добавить ссылку на django-widget-tweak

РЕДАКТИРОВАТЬ 2

  • Обновить фильтр в соответствии с комментариями Bhyd
Charlesthk
источник
3
Это круто! Это СУХОЙ, и это отделяет слой дисплея от контрольного слоя. +1 от меня!
Alekwisnia
1
Однако сейчас я заметил один недостаток. Если я определяю класс в атрибутах виджетов, то они переопределяются этим фильтром 'addcss'. У вас есть идеи, как это объединить?
alekwisnia
1
Я имею в виду, as_widget () переопределяет attrs. Как гарантировать, что он использует существующие атрибуты и расширяет их новыми?
Alekwisnia
Смотрите этот ответ о том, как уважать существующие классы полей
dimyG
get('class', None).split(' ')потерпит неудачу , если тег никак не имеет атрибута класса, как Noneвозвращаются. get('class', '').split(' ')
Меняем
32

Расширяя метод, указанный на docs.djangoproject.com:

class MyForm(forms.Form): 
    comment = forms.CharField(
            widget=forms.TextInput(attrs={'size':'40'}))

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

class MyForm(forms.Form): 
    #This instantiates the field w/ the default widget
    comment = forms.CharField()

    #We only override the part we care about
    comment.widget.attrs['size'] = '40'

Это кажется немного чище для меня.

Дейв
источник
Это именно то, что говорили другие много лет назад, за исключением того, что вы делаете это с помощью «размера», а не «класса», который был запрошен.
Крис Морган
3
@Chris Morgan На самом деле, он первый, кто предложил использовать «comment.widget.attrs» в самой декларации формы (вместо того, чтобы связываться с init или делать это в представлении).
DarwinSurvivor
29

Если вы хотите, чтобы все поля в форме наследовали определенный класс, вы просто определяете родительский класс, который наследует forms.ModelForm, а затем наследует от него.

class BaseForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(BaseForm, self).__init__(*args, **kwargs)
        for field_name, field in self.fields.items():
            field.widget.attrs['class'] = 'someClass'


class WhateverForm(BaseForm):
    class Meta:
        model = SomeModel

Это помогло мне 'form-control'автоматически добавить класс во все поля всех форм моего приложения, не добавляя репликацию кода.

Олег Белоусов
источник
1
пожалуйста, начинайте имена классов с заглавных букв (кроме того, BaseForm кажется гораздо более подходящим названием для этого)
Alvaro
16

Вот простой способ изменить вид. добавьте ниже в поле зрения перед передачей в шаблон.

form = MyForm(instance = instance.obj)
form.fields['email'].widget.attrs = {'class':'here_class_name'}
Человек-паук
источник
14

Просто добавьте классы в форму следующим образом.

class UserLoginForm(forms.Form):
    username = forms.CharField(widget=forms.TextInput(
        attrs={
        'class':'form-control',
        'placeholder':'Username'
        }
    ))
    password = forms.CharField(widget=forms.PasswordInput(
        attrs={
        'class':'form-control',
        'placeholder':'Password'
        }
    ))
paulc
источник
5

Вот вариант выше, который даст всем полям один и тот же класс (например, jquery nice закругленные углы).

  # Simple way to assign css class to every field
  def __init__(self, *args, **kwargs):
    super(TranslatedPageForm, self).__init__(*args, **kwargs)
    for myField in self.fields:
      self.fields[myField].widget.attrs['class'] = 'ui-state-default ui-corner-all'
timlinux
источник
5

Вы можете попробовать это ..

class SampleClass(forms.Form):
  name = forms.CharField(max_length=30)
  name.widget.attrs.update({'class': 'your-class'})
...

Вы можете увидеть больше информации в: Django Widgets

Андрес Кирога
источник
2

Если вы хотите добавить класс в поле формы в шаблоне (не в view.py или form.py), например, в случаях, когда вы хотите изменить сторонние приложения без переопределения их представлений, тогда фильтр шаблона, как описано в Charlesthk ответить очень удобно. Но в этом ответе фильтр шаблона переопределяет любые существующие классы, которые может иметь поле.

Я попытался добавить это как редактирование, но это было предложено написать как новый ответ.

Итак, вот тег шаблона, который учитывает существующие классы поля:

from django import template

register = template.Library()


@register.filter(name='addclass')
def addclass(field, given_class):
    existing_classes = field.field.widget.attrs.get('class', None)
    if existing_classes:
        if existing_classes.find(given_class) == -1:
            # if the given class doesn't exist in the existing classes
            classes = existing_classes + ' ' + given_class
        else:
            classes = existing_classes
    else:
        classes = given_class
    return field.as_widget(attrs={"class": classes})
dimyG
источник
Спасибо за ваше предложение. Я отредактировал свой ответ соответственно с более "питоническим" подходом.
Charlesthk
отлично !!, вот что я искал
угали софт
1

Оказывается, вы можете сделать это в конструкторе формы ( функция init ) или после того, как класс формы был инициирован. Это иногда требуется, если вы не пишете свою собственную форму, и эта форма приходит откуда-то еще -

def some_view(request):
    add_css_to_fields = ['list','of','fields']
    if request.method == 'POST':
        form = SomeForm(request.POST)
        if form.is_valid():
            return HttpResponseRedirect('/thanks/')
    else:
        form = SomeForm()

    for key in form.fields.keys():
        if key in add_css_to_fields:
            field = form.fields[key]
            css_addition = 'css_addition '
            css = field.widget.attrs.get('class', '')
            field.widget.attrs['class'] = css_addition + css_classes

    return render(request, 'template_name.html', {'form': form})
Niki.py
источник
1

Вы также можете использовать Django Crispy Forms , это отличный инструмент для определения форм на случай, если вы захотите использовать некоторые CSS-фреймворки, такие как Bootstrap или Foundation. И там легко указать классы для полей формы.

Ваш класс формы хотел бы это тогда:

from django import forms

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div, Submit, Field
from crispy_forms.bootstrap import FormActions

class SampleClass(forms.Form):
    name = forms.CharField(max_length=30)
    age = forms.IntegerField()
    django_hacker = forms.BooleanField(required=False)

    helper = FormHelper()
    helper.form_class = 'your-form-class'
    helper.layout = Layout(
        Field('name', css_class='name-class'),
        Field('age', css_class='age-class'),
        Field('django_hacker', css-class='hacker-class'),
        FormActions(
            Submit('save_changes', 'Save changes'),
        )
    )
Дмитрий Михайлов
источник