Definir classe css no Forms do django

192

Suponha que eu tenho um formulário

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

Existe uma maneira de definir classes css em cada campo para que eu possa usar o jQuery com base na classe na minha página renderizada?

Eu esperava não ter que criar manualmente o formulário.

ashchristopher
fonte
9
uau, todo mundo vota isso? docs.djangoproject.com/en/dev/ref/forms/widgets/…
Skylar Saveland
10
@skyl Eu consideraria isso uma indicação de que não é fácil encontrar nos documentos do django. Eu naveguei e fiz várias pesquisas no Google também e não consegui encontrar isso, por isso estou feliz com a pergunta e ele recebe meu voto positivo.
Nils
Eu sei que é um thread antigo, mas nos campos de formulário do Django agora temos uma classe id_fieldname.
Ratsimihah
1
Veja esta resposta sobre como definir classes para um campo de formulário dentro de um modelo, respeitando aulas de campo existentes
dimyG

Respostas:

182

Ainda outra solução que não requer alterações no código python e, portanto, é melhor para designers e alterações pontuais na apresentação: django-widget-tweaks . Espero que alguém ache útil.

Mikhail Korobov
fonte
61
A única solução sã, devo dizer. Obrigado!. O código Python, e especialmente na definição do formulário, é o último lugar para colocar coisas no estilo - elas definitivamente pertencem aos modelos.
Boris Chervenkov
5
Esta é uma ótima biblioteca! É uma pena que esta resposta esteja enterrada no fundo.
31412 Chris Cornasse
1
Ótimo para designers! :-)
hobbes3
1
Excelente! Eu desenvolvi alguns filtros para fazer essas coisas, mas esse projeto é muito mais poderoso. Obrigado!
21864 msbrogli
1
na verdade, ele funciona para o Jinja2 também :-) Alterei a ordem do filtro seguro e adicionei parênteses em vez de dois pontos {{myform.email | add_class ("css_class_1 css_class_2") | safe}} obrigado por escrever isso. deve fazer parte do Django.
David Dehghan
92

Respondeu minha própria pergunta. Suspiro

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

Não percebi que foi passado para o construtor de widgets.

ashchristopher
fonte
2
isso implica que não pode ser usado com a classe ModelForm?
Xster
2
Não, você apenas precisaria definir explicitamente o campo do formulário no modelo para poder definir o widget. Ou use a solução listada abaixo para evitar ter que mexer com o campo do formulário.
Tom
78

Aqui está outra solução para adicionar definições de classe aos widgets depois de declarar os campos na classe.

def __init__(self, *args, **kwargs):
    super(SampleClass, self).__init__(*args, **kwargs)
    self.fields['name'].widget.attrs['class'] = 'my_class'
ashchristopher
fonte
4
Para ModelForms, isso geralmente é melhor, pois você não precisa estar ciente do campo de formulário padrão que está sendo usado e pode definir dinamicamente diferentes classes com base nas condições de tempo de execução. Mais limpo do que meta de codificação hacks ...
Daniel Naab
1
Isso pressupõe que você deseja uma entrada para cada campo de um formulário, o que geralmente não é o caso. Um formulário não está necessariamente vinculado a um modelo - pode estar vinculado a muitos modelos. Caso os campos do modelo e do formulário tenham um relacionamento 1: 1, sim, o ModelForm é uma escolha muito melhor.
31511 ashshristopher #
44

Use django-widget-tweaks , é fácil de usar e funciona muito bem.

Caso contrário, isso pode ser feito usando um filtro de modelo personalizado.

Considerando que você processa seu formulário desta maneira:

<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 é uma instância do BoundField que possui o método as_widget .

você pode criar um filtro personalizado "addcss" em "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})

E depois aplique seu filtro:

{% 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>

O form.subjects será renderizado com a classe css "MyClass".

Espero que esta ajuda.

EDIT 1

  • Atualize o filtro de acordo com a resposta de dimyG

  • Adicionar link django-widget-tweak

EDIT 2

  • Atualizar filtro de acordo com o comentário de Bhyd
Charlesthk
fonte
3
Isso é ótimo! É SECO e separa a camada de exibição da camada de controle. +1 de mim!
alekwisnia
1
No entanto, agora eu notei uma desvantagem. Se eu definir classe no widget attrs, elas serão substituídas por esse filtro 'addcss'. Você tem alguma idéia de como mesclar isso?
precisa saber é o seguinte
1
Quero dizer, como_widget () substitui attrs. Como garantir que ele use os atributos existentes e os estenda com um novo?
alekwisnia
Veja esta resposta sobre como respeitar aulas de campo existentes
dimyG
get('class', None).split(' ')falhará se a tag não tiver o atributo de classe, como Noneé retornado. Mudá-lo para get('class', '').split(' ')obras
dtasev
32

Expandindo o método apontado em docs.djangoproject.com:

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

Eu pensei que era problemático saber o tipo de widget nativo para cada campo e achei engraçado substituir o padrão apenas para colocar um nome de classe em um campo de formulário. Isso parece funcionar para mim:

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'

Isso me parece um pouco mais limpo.

Dave
fonte
Isso é precisamente o que outros disseram, eras atrás, exceto que você está fazendo isso com 'tamanho' em vez de 'classe' que foi solicitado.
Chris Morgan
3
@ Chris Morgan Na verdade, ele é o primeiro a sugerir o uso de "comment.widget.attrs" na própria declaração do formulário (em vez de mexer com init ou fazê-lo na exibição).
DarwinSurvivor
29

Se você deseja que todos os campos do formulário herdem uma determinada classe, basta definir uma classe pai, que herda forms.ModelForme, em seguida, herda dela

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

Isso me ajudou a adicionar a 'form-control'classe a todos os campos em todos os formulários do meu aplicativo automaticamente, sem adicionar replicação de código.

Oleg Belousov
fonte
1
por favor comece nomes de classe com maiúscula (também, BaseForm parece muito melhor nome para ele)
Alvaro
16

Aqui está uma maneira simples de alterar a exibição. adicione abaixo na visualização antes de passá-lo para o modelo.

form = MyForm(instance = instance.obj)
form.fields['email'].widget.attrs = {'class':'here_class_name'}
Homem Aranha
fonte
14

Basta adicionar as classes ao seu formulário da seguinte maneira.

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
fonte
5

Aqui está uma variação acima, que dará a todos os campos a mesma classe (por exemplo, jquery cantos arredondados).

  # 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
fonte
5

Você pode tentar isso ..

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

Você pode ver mais informações em: Widgets do Django

Andres Quiroga
fonte
2

Caso você queira adicionar uma classe ao campo de um formulário em um modelo (não em view.py ou form.py), por exemplo, nos casos em que deseja modificar aplicativos de terceiros sem substituir suas visualizações, um filtro de modelo conforme descrito em Charlesthk resposta é muito conveniente. Mas nesta resposta o filtro de modelo substitui todas as classes existentes que o campo possa ter.

Tentei adicionar isso como uma edição, mas foi sugerido que fosse escrito como uma nova resposta.

Então, aqui está uma tag de modelo que respeita as classes existentes do campo:

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
fonte
Obrigado por sua sugestão. Eu editei minha resposta de acordo com uma abordagem mais "pitônica".
Charlesthk
grande !!, que é o que eu estava procurando
ugali macia
1

Como se vê, você pode fazer isso no construtor de formulários ( função init ) ou após o início da classe do formulário. Às vezes, isso é necessário se você não estiver escrevendo seu próprio formulário e esse formulário vier de outro lugar -

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
fonte
1

Você também pode usar o Django Crispy Forms , é uma ótima ferramenta para definir formulários, caso você queira usar alguma estrutura CSS como Bootstrap ou Foundation. E é fácil especificar classes para seus campos de formulário lá.

Sua classe de formulário gostaria disso então:

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'),
        )
    )
Dmitrii Mikhailov
fonte