O básico do CreateView
Começando do básico, o exemplo abaixo já quebra um galho enorme quando de trata de formulários simples, como por exemplo o Contato de algum site.
forms.py
forms.py
# coding: utf-8 from django import forms from .models import Contato class ContatoForm(forms.ModelForm): nome = forms.CharField(widget=forms.TextInput(attrs={'class' : 'required',}), label="Nome") email = forms.EmailField(widget=forms.TextInput(attrs={'class' : 'required',}), label="E-mail") texto = forms.CharField(widget=forms.Textarea(attrs={'class' : 'required',}), label="Texto") class Meta: model = Contato fields = '__all__' def dados(self): return {'form':self.cleaned_data, 'data':datetime.now()}
Até aqui sem muitas dificuldades né? Vemos a definição da classe para gerenciamento do form, com alguns atributos. Com o fields = '__all__' não era necessário definir os atributos, mas isso é obrigatório caso queira adicionar uma classe, como por exemplo o required ou outro atributo qualquer, por exemplo um placeholder, etc.
views.py
# Create your views here. # coding: utf-8 from django.views.generic.edit import CreateView from .forms import ContatoForm from .models import Contato class ContatoView(CreateView): form_class = ContatoForm success_url = '/contato/sucesso/' template_name = 'contato/contato.html' model = Contato def get_context_data(self, **kwargs): kwargs.update({ 'menu':'contato', 'title': 'Contato', }) return kwargs
A view também é bem tranquila. Herdando as características da CreateView, exige apenas algumas informações para resolver o problema de inserção e validação dos campos de forma elegante:
- form_class: Classe do Form definida no forms.py
- success_url: URL para qual será redirecionado após o sucesso da inserção
- template_name: Html do form.
- model: Classe definida no models.py
contato.html
<form method="post" action=""> {% csrf_token %} {{ form.errors}} {{ form.as_p }} <input class="btn" type="submit" value="Enviar"> </form>
Acima temos um exemplo bem sucinto de como montar um form de forma bem automágica e podemos observar algumas coisas interessantes com o exemplo acima:
- A CreateView trabalha com o post para própria página (mesma url) por isso no action do form não tem nenhuma informação.
- A validação do CSRF já é nativa da CreateView, a unica coisa que precisa e incluir o token no html dentro da tag form.
- {{ form.errors }} vai gerar uma <ul> com a tag errorlist onde cada <li> será responsável por lista os erros de um campo específico e terá uma <ul> também com a classe errorlist uma lista de <li> para todos os erros deste respectivo campo.
Outra coisa legal de fazer é definir no html os campos separamente para um tratamento de erro e layout melhor apresentados. Isso pode ser feito assim:
<p>{{ form.email }}</p> <div class="error">{{ form.email.errors }}</div>
E o UpdatView?
Um formulário de contato não sofrerá update bem possivelmente. Mas apenas para ilustar usaremos o e mesmo exemplo.views.py
class ContatoUpdateView(UpdateView): form_class = ContatoForm model = Contato success_url = '/contato/sucesso/' template_name = 'contato/contato.html' def get_context_data(self, **kwargs): kwargs.update({ 'menu':'contato', 'title': 'Contato', 'update':True, }) return kwargs
Eu já mostrei como fazer para utilizar os inlines junto com a CreateView neste post: http://djangoweb.blogspot.com.br/2013/08/como-utilizar-os-inlines-no-front-para.html. Agora vamos a algo mais divertido.
Que tal criarmos um exemplo mais complexo, onde não terá apenas uma, mas sim várias classes inline, com o CreateView e UpdateView?
Multiplos inlines
forms.py
# coding: utf-8 from django.contrib.admin.widgets import FilteredSelectMultiple from django.forms.models import inlineformset_factory from django import forms from .models import * from util import util as U class ModeloForm(forms.ModelForm): class Meta: model = Modelo fields = '__all__' def dados(self): return {'form':self.cleaned_data, 'data':datetime.now()} class ModeloInline1Form(forms.ModelForm): class Meta: model = ModeloInline1 fields = '__all__' ModeloInline1FormSet = inlineformset_factory(Modelo, ModeloInline1, extra=0, min_num=1, form=ModeloInline1Form, fields='__all__') class ModeloInline2Form(forms.ModelForm): class Meta: model = ModeloInline1 fields = '__all__' ModeloInline2FormSet = inlineformset_factory(Modelo, ModeloInline2, extra=0, min_num=1, form=ModeloInline1Form, fields='__all__')
views.py
class ModeloCreateView(CreateView): form_class = ModeloForm success_url = '/home/' template_name = 'modelo/form.html' model = Modelo def get(self, request, *args, **kwargs): self.object = None form_class = self.get_form_class() form = self.get_form(form_class) inline1_form = ModeloInline1FormSet() inline2_form = ModeloInline2FormSet() return self.render_to_response(self.get_context_data(form=form, inline1_form = inline1_form , inline2_form = inline2_form )) def post(self, request, *args, **kwargs): self.object = None form_class = self.get_form_class() form = self.get_form(form_class) inline1_form = ModeloInline1FormSet(self.request.POST) inline2_form = ModeloInline1FormSet(self.request.POST) if (form.is_valid() and inline1_form .is_valid() and inline2_form .is_valid()): return self.form_valid(form, inline1_form , inline2_form ) else: return self.form_invalid(form, inline1_form , inline2_form ) def form_valid(self, form, inline1_form, inline2_form ): self.object = form.save() inline1_form.instance = self.object inline1_form.save() inline2_form.instance = self.object inline2_form.save() return HttpResponseRedirect(self.get_success_url()) def form_invalid(self, form, inline1_form, inline2_form ): return self.render_to_response( self.get_context_data( form=form, inline1_form=inline1_form, inline2_form=inline2_form) ) def get_context_data(self, **kwargs): kwargs.update({}) return kwargs class ModeloUpdateView(UpdateView): form_class = ModeloForm model = Modelo success_url = '/home/' template_name = 'modelo/form.html' def get(self, request, *args, **kwargs): self.object = self.get_object() form_class = self.get_form_class() form = self.get_form(form_class) # Render form inline1_form = ModeloInline1FormSet(instance=self.object) inline2_form = ModeloInline2FormSet(instance=self.object) return self.render_to_response(self.get_context_data(form=form,inline1_form=inline1_form,inline2_form=inline2_form)) def post(self, request, *args, **kwargs): self.object = self.get_object() form_class = self.get_form_class() form = self.get_form(form_class) inline1_form = ModeloInline1FormSet(self.request.POST, instance=self.object) inline2_form = ModeloInline2FormSet(self.request.POST, instance=self.object) if (form.is_valid() and inline1_form.is_valid() and inline2_form.is_valid()): return self.form_valid(form, inline1_form, inline2_form) else: return self.form_invalid(form, inline1_form, inline2_form) def form_valid(self, form, inline1_form , inline2_form ): self.object = form.save() inline1_form.instance = self.object inline1_form.save() inline2_form.instance = self.object inline2_form.save() return HttpResponseRedirect(self.get_success_url()) def form_invalid(self, form, inline1_form , inline2_form ): return self.render_to_response( self.get_context_data( form=form, inline1_form=inline1_form, inline2_form=inline2_form) ) def get_context_data(self, **kwargs): kwargs.update({ 'update':True, }) return kwargs
Nos exemplos acima, o formulário html pode ser o mesmo como no exemplo do Contato. Podemos também utilizar o recurso do inline visto no post cima citado para inserção de quantos inlines forem necessários com o mesmo recurso do admin para sempre adicionar mais um conforme necessidade.
Um jeito mais elegante de usar isso é usar um template vinculado ao js, com o plugin underscore disponível aqui: http://underscorejs.org/
<script type="text/javascript" src="/static/site/js/plugins/underscore/underscore-min.js"></script> <script type="text/html" id="modeloinline1-template"> <div class="bloco-modeloinline1 clearfix"> <div class="col-lg-12"> <div class="form-group"> <label for="">Exemplo de Textarea</label> <textarea rows="5" name="modeloinline1_set-<%= id %>-descricao" id="id_modeloinline1_set-<%= id %>-descricao" cols="40" class="form-control"></textarea> </div> </div> </div> </script> django.jQuery('.container-modeloinline1').on('click', '.btn-adicionar-modeloinline1', function(ev) { ev.preventDefault(); django.jQuery(this).parent().find('button').hide(); var count = django.jQuery('.container-modeloinline1').children().length; var tmplMarkup = django.jQuery('#modeloinline1-template').html(); var compiledTmpl = _.template(tmplMarkup, { id : count }); django.jQuery('.modeloinline1-form"').append(compiledTmpl); // update form count django.jQuery('#id_modeloinline1_set-TOTAL_FORMS').attr('value', count+1); }); </script> <form role="form" action="" method="post" class=''> {{ modeloinline1.management_form }} <div class="container-modeloinline1-form"> {% for form in inline1_form %} {% if update %} {{ form.id }} {{ form.avaliacao }} {% endif %} {% if form.errors %} <div class="alert alert-danger"> Por favor corriga os erros abaixo para prosseguir: {{ form.errors}} </div> {% endif %} </div> </form> <button class="btn btn-primary btn-sm btn-adicionar-modeloinline1" type="button">Adicionar outro Modeloinline1</button>
Nenhum comentário:
Postar um comentário