DJANGO - FORMULARIOS ASOCIADOS A MODELOS

En esta sesión demostramos el uso de la clase ModelForm para definir formularios directamente asociados a modelos de Django, y cómo aprovecharlos en conjunto con las vistas genéricas basadas en clases, para implementar funcionalidades comunes en muchas aplicaciones web con un mínimo de esfuerzo de programación.

Cargando video...

NOTA: Solo puedes ver una versión limitada del video a baja resolución, si quieres ver la versión completa por favor regístrate y obtén alguno de nuestros planes!

Descripción del Vídeo

Con Django, podemos usar la clase ModelForm para configurar de manera sencilla formularios asociados a nuestros modelos de base de datos.

Al combinar esta funcionalidad con las clases de vistas genéricas, podemos generar formularios y vistas completas con muy poco esfuerzo de desarrollo.

En este ejemplo, ilustraremos el uso de formularios asociados a modelos con vistas genéricas, para agregar en la propia aplicación la funcionalidad de agregar/editar preguntas y opciones (aunque ya existe en el sitio administrativo)

Usaremos el paquete django-bootstrap3-datetimepicker para incluir un control de calendario compatible con Bootstrap.

Código disponible en:
https://github.com/networkfaculty/Fundamentos-Django/releases/tag/d-2.3.9
Instalar paquete de calendario (versión mas reciente):

pip install git+https://github.com/nkunihiko/django-bootstrap3-datetimepicker.git 

Agregar bootstrap3_datetime a INSTALLED_APPS en curso/settings.py

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bootstrap3',
    'bootstrap3_datetime',
    'encuestas',
)


curso/encuestas/forms.py

from django import forms
from .models import Pregunta, Opcion
from bootstrap3_datetime.widgets import DateTimePicker
from django.forms.models import inlineformset_factory

class FormContacto(forms.Form):
   asunto = forms.CharField(max_length=100)
   mensaje = forms.CharField(widget=forms.Textarea)
   remitente = forms.EmailField()
   cc_remitente = forms.BooleanField(label='Envíame una copia', required=False)

class FormPregunta(forms.ModelForm):
   class Meta:
       model = Pregunta
       fields = ['texto_pregunta','fe_publicacion']
       widgets = {'texto_pregunta': forms.Textarea(attrs={'rows':3}),
                  'fe_publicacion': DateTimePicker(options={"format": "YYYY-MM-DD",
                                      "pickTime": False})}

FormPreguntaOpciones = inlineformset_factory(
   Pregunta,
   Opcion,
   fields = ('texto_opcion',),
   max_num = 5,
   extra = 5,
)

Usar formularios asociados a modelos con clases genéricas en curso/encuestas/views.py

...
from .forms import *


from django.views.generic.edit import CreateView, UpdateView

...


class VistaCrearPregunta(CreateView):
   model = Pregunta
   form_class = FormPregunta
   template_name = 'encuestas/agregar_pregunta.html'
   success_url = reverse_lazy('encuestas:index')



class VistaEditarPregunta(UpdateView):
   model = Pregunta
   form_class = FormPregunta
   template_name = 'encuestas/editar_pregunta.html'
   success_url = reverse_lazy('encuestas:index')



class VistaOpciones(UpdateView):
   '''
   En este ejemplo utilizamos un Formset personalizado para editar las opciones
   correspondientes a una pregunta. Por ello, utilizamos el modelo "padre" (Pregunta),
   y personalizamos los métodos get/post para obtener y guardar la lista de opciones
   '''
   model = Pregunta
   form_class = FormPregunta
   template_name = 'encuestas/editar_opciones.html'
   success_url = reverse_lazy('encuestas:index')

   def get(self, request, *args, **kwargs):
       self.object = self.get_object()
       opciones_form = FormPreguntaOpciones(instance=self.object)
       return self.render_to_response(
           self.get_context_data(opciones_form=opciones_form,))

   def post(self, request, *args, **kwargs):
       self.object = self.get_object()
       opciones_form = FormPreguntaOpciones(request.POST, instance=self.object)
       if opciones_form.is_valid():
           opciones_form.save()
           messages.success(request, 'Opciones guardadas')
           return HttpResponseRedirect(self.get_success_url())
       else:
           messages.error(request, 'No se pudieron guardar las opciones')
           return self.render_to_response(
               self.get_context_data(opciones_form=opciones_form,))


Plantilla para agregar pregunta en curso/encuestas/templates/encuestas/agregar_pregunta.html

{% extends 'encuestas/base.html' %}

{% block extra-head %}
   {{ form.media }}
{% endblock%}

{% block contenido %}

{% load bootstrap3 %}

<h2>Agrega una nueva pregunta</h2>

<form action="{% url 'encuestas:agregar' %}" method="post">
   {% csrf_token %}
   {% bootstrap_form form layout='horizontal' %}
   {% buttons %}
   <button type="submit" class="btn btn-primary">
       {% bootstrap_icon "floppy-disk" %} Agregar Pregunta
   </button>
   {% endbuttons %}
</form>

{% endblock contenido %}

Agregar bloque personalizable en el <head> desde curso/encuestas/templates/encuestas/base.html

    <script src="{% static 'encuestas/js/jquery-1.11.3.min.js' %}"></script>
    <script src="{% static 'encuestas/js/bootstrap.min.js' %}"></script>
    {% block extra-head %}{% endblock %}
  </head>

Agregar botones para editar en vista de detalle curso/encuestas/templates/encuestas/detalle.html

…
            <a class="btn btn-info" href="{% url 'encuestas:resultados' pregunta.id %}">
                Ver resultados
            </a>
            <a class="btn btn-default" href="{% url 'encuestas:editar' pregunta.id %}">
                {% bootstrap_icon "pencil" %} Editar Pregunta
            </a>
            <a class="btn btn-default" href="{% url 'encuestas:opciones' pregunta.id %}">
                {% bootstrap_icon "pencil" %} Editar Opciones
            </a>
…

curso/encuestas/templates/encuestas/editar_opciones.html

{% extends 'encuestas/base.html' %}
{% load bootstrap3 %}

{% block contenido %}

<h2>Editar Opciones para pregunta #{{ pregunta.id }}</h2>

<h4>{{ pregunta.texto_pregunta }}</h4>

<form action="{% url 'encuestas:opciones' pregunta.id %}" method="post">
   {% csrf_token %}

   {% bootstrap_formset opciones_form %}

   {% buttons %}
   <button type="submit" class="btn btn-primary">
       {% bootstrap_icon "floppy-disk" %} Guardar Cambios
   </button>
   {% endbuttons %}
</form>

{% endblock contenido %}

curso/encuestas/templates/encuestas/editar_pregunta.html

{% extends 'encuestas/base.html' %}
{% load bootstrap3 %}

{% block extra-head %}
   {{ form.media }}
{% endblock%}

{% block contenido %}

<h2>Editar pregunta #{{ pregunta.id }}</h2>

<form action="{% url 'encuestas:editar' pregunta.id %}" method="post">
   {% csrf_token %}
   {% bootstrap_form form layout='horizontal' %}
   {% buttons %}
   <button type="submit" class="btn btn-primary">
       {% bootstrap_icon "floppy-disk" %} Guardar Cambios
   </button>
   {% endbuttons %}
</form>

{% endblock contenido %}

Agregar botones para editar y agregar en curso/encuestas/templates/encuestas/index.html

...
                href="{% url 'encuestas:detalle' pregunta.id %}">Votar</a>
                <a class="btn btn-info" role="button"
                href="{% url 'encuestas:resultados' pregunta.id %}">Ver resultados</a>
                <a class="btn btn-default" role="button"
                href="{% url 'encuestas:editar' pregunta.id %}">
                    {% bootstrap_icon "pencil" %} Editar Pregunta</a>
            </div>
        </li>
    {% endfor %}
...
    <p>No hay encuestas disponibles.</p>
{% endif %}
    <a class="btn btn-default" role="button"
    href="{% url 'encuestas:agregar' %}">{% bootstrap_icon "plus" %} Nueva Encuesta</a>
...


Agregar nuevas rutas en curso/encuestas/urls.py

from django.conf.urls import url
from . import views

urlpatterns = [
   # ej: /encuestas/
   url(r'^$', views.VistaIndex.as_view(), name='index'),
   # ej: /encuestas/5
   # Las vistas detalladas genéricas esperan que el argumento se llame 'pk'
   url(r'^(?P<pk>\d+)/$', views.VistaDetalle.as_view(), name='detalle'),
   # ej: /encuestas/5/resultados
   url(r'^(?P<pk>\d+)/resultados$', views.VistaResultados.as_view(), name='resultados'),
   # ej: /encuestas/5/votar
   url(r'^(?P<id_pregunta>\d+)/votar$', views.votar, name='votar'),
   url(r'^contacto$', views.contacto, name='contacto'),
   url(r'^agregar$', views.VistaCrearPregunta.as_view(), name='agregar'),
   url(r'^(?P<pk>\d+)/editar', views.VistaEditarPregunta.as_view(), name='editar'),
   url(r'^(?P<pk>\d+)/opciones', views.VistaOpciones.as_view(), name='opciones'),
]

Rating

Global

Ver video en playlist

comments powered by Disqus

Headshot of Juan Paredes

Juan Paredes

Ingeniero de Sistemas con amplia experiencia, especializado en el desarrollo y arquitectura de software.