¿Están violando los formularios de Django MVC?

16

Acabo de empezar a trabajar con Django a partir de los años de Spring MVC y la implementación de formularios me parece un poco loca. Si no está familiarizado, formularios Django comienza con una clase de modelo de formulario que define sus campos. La primavera comienza de manera similar con un objeto de respaldo de formulario. Pero donde Spring proporciona un taglib para vincular elementos de formulario Al objeto de respaldo dentro de su JSP, Django tiene widgets de formulario vinculados directamente al modelo. Hay widgets predeterminados donde puede agregar atributos de estilo a sus campos para aplicar CSS o definir widgets completamente personalizados como nuevas clases. Todo va en su código de python. Eso me parece una locura. Primero, está poniendo información sobre su vista directamente en su modelo y, en segundo lugar, está vinculando su modelo a una vista específica. ¿Me estoy perdiendo algo?

EDITAR: Algún código de ejemplo según lo solicitado.

Django:

# Class defines the data associated with this form
class CommentForm(forms.Form):
    # name is CharField and the argument tells Django to use a <input type="text">
    # and add the CSS class "special" as an attribute. The kind of thing that should
    # go in a template
    name = forms.CharField(
                widget=forms.TextInput(attrs={'class':'special'}))
    url = forms.URLField()
    # Again, comment is <input type="text" size="40" /> even though input box size
    # is a visual design constraint and not tied to the data model
    comment = forms.CharField(
               widget=forms.TextInput(attrs={'size':'40'}))

Spring MVC:

public class User {
    // Form class in this case is a POJO, passed to the template in the controller
    private String firstName;
    private String lastName;
    get/setWhatever() {}
}

<!-- JSP code references an instance of type User with custom tags -->
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!-- "user" is the name assigned to a User instance -->
<form:form commandName="user">
      <table>
          <tr>
              <td>First Name:</td>
              <!-- "path" attribute sets the name field and binds to object on backend -->
              <td><form:input path="firstName" class="special" /></td>
          </tr>
          <tr>
              <td>Last Name:</td>
              <td><form:input path="lastName" size="40" /></td>
          </tr>
          <tr>
              <td colspan="2">
                  <input type="submit" value="Save Changes" />
              </td>
          </tr>
      </table>
  </form:form>
    
pregunta jiggy 08.07.2011 - 01:40

4 respuestas

20

Sí, las formas de Django son un desastre desde la perspectiva de MVC, supongamos que estás trabajando en un gran juego de superhéroes de MMO y estás creando el modelo Hero:

class Hero(models.Model):
    can_fly = models.BooleanField(default=False)
    has_laser = models.BooleanField(default=False)
    has_shark_repellent = models.BooleanField(default=False)

Ahora se le pide que cree un formulario para que los jugadores de MMO puedan ingresar sus superpotencias de héroe:

class HeroForm(forms.ModelForm):
    class Meta:
        model = Hero

Dado que el Repelente de tiburones es un arma muy poderosa, su jefe le pidió que la limite. Si un héroe tiene el Repelente de Tiburones, entonces no puede volar. Lo que la mayoría de la gente hace es simplemente agregar esta regla de negocio en el formulario de limpieza y llamarla un día:

class HeroForm(forms.ModelForm):
    class Meta:
        model = Hero

    def clean(self):
        cleaned_data = super(HeroForm, self).clean()
        if cleaned_data['has_shark_repellent'] and cleaned_data['can_fly']:
            raise ValidationError("You cannot fly and repel sharks!")

Este patrón se ve bien y podría funcionar en proyectos pequeños, pero en mi experiencia es muy difícil de mantener en proyectos grandes con múltiples desarrolladores. El problema es que el formulario es parte de la vista del MVC. Así que tendrás que recordar esa regla de negocio cada vez que:

  • Escribe otra forma que se ocupe del modelo Hero.
  • Escribe un guión que importe héroes de otro juego.
  • Cambie manualmente la instancia del modelo durante la mecánica del juego.
  • etc.

Lo que quiero decir aquí es que forms.py tiene que ver con el diseño y la presentación del formulario, nunca debes agregar lógica de negocios en ese archivo a menos que disfrutes jugando con el código spaghetti.

La mejor manera de manejar el problema del héroe es usar model clean method más una señal personalizada. La limpieza del modelo funciona igual que la forma limpia pero se almacena en el propio modelo, siempre que se limpia el HeroForm, se llama automáticamente el método de limpieza del héroe. Esta es una buena práctica porque si otro desarrollador escribe otra forma para el Héroe, obtendrá la validación de repelente / vuelo de forma gratuita.

El problema con la limpieza es que se llama solo cuando un modelo es modificado por un formulario. No se llama cuando lo guardas () manualmente y puedes terminar con un héroe inválido en tu base de datos. Para contrarrestar este problema, puede agregar esta escucha a su proyecto:

from django.db.models.signals import pre_save

def call_clean(sender, instance, **kwargs):
    instance.clean()
pre_save.connect(call_clean, dispatch_uid='whata')

Esto llamará al método de limpieza en cada llamada a guardar () para todos tus modelos.

    
respondido por el Cesar Canassa 09.07.2011 - 11:38
8

Estás mezclando toda la pila, hay varias capas involucradas:

  • un modelo de Django define la estructura de datos.

  • un Formulario Django es un atajo para definir formularios HTML, validaciones de campo y traducciones de valores Python / HTML. No es estrictamente necesario, pero a menudo es útil.

  • un Django ModelForm es otro atajo, en resumen, una subclase de formulario que obtiene sus campos de una definición de modelo. Solo es una forma práctica para el caso común donde se usa un formulario para ingresar datos en la base de datos.

y finalmente:

  • Los arquitectos Django no se adhieren exactamente a la estructura MVC. A veces lo llaman MTV (Modelo de vista de plantilla); porque no existe un controlador, y la división entre la plantilla (solo la presentación, la lógica) y la vista (solo la lógica orientada al usuario, no el HTML) es tan importante como el aislamiento del Modelo.

Algunas personas ven eso como una herejía; pero es importante recordar que MVC se definió originalmente para las aplicaciones GUI, y es un ajuste bastante incómodo para las aplicaciones web.

    
respondido por el Javier 08.07.2011 - 03:28
4

Estoy respondiendo a esta vieja pregunta porque las otras respuestas parecen evitar el problema específico mencionado.

Los formularios Django le permiten escribir fácilmente código pequeño y crear un formulario con valores predeterminados sanos. Cualquier cantidad de personalización conduce muy rápidamente a "más código" y "más trabajo" y anula de alguna manera el beneficio principal del sistema de formularios

Las bibliotecas de plantillas como django-widget-tweaks facilitan mucho la personalización de formularios. Es de esperar que las personalizaciones de campo como esta finalmente sean fáciles con una instalación de Django de vainilla.

Tu ejemplo con django-widget-tweaks:

{% load widget_tweaks %}
<form>
  <table>
      <tr>
          <td>Name:</td>
          <td>{% render_field form.name class="special" %}</td>
      </tr>
      <tr>
          <td>Comment:</td>
          <td>{% render_field form.comment size="40" %}</td>
      </tr>
      <tr>
          <td colspan="2">
              <input type="submit" value="Save Changes" />
          </td>
      </tr>
  </table>

    
respondido por el Trey Hunner 24.05.2012 - 00:59
1

(He usado Cursivas para indicar los conceptos de MVC para hacer esto más legible.)

No, en mi opinión, no rompen MVC. Cuando trabaje con Django Models / Forms, piense en ello como si se usara una pila MVC completa como Model :

  1. django.db.models.Model es el Modelo base (contiene los datos y la lógica de negocios).
  2. django.forms.ModelForm proporciona un Controlador para interactuar con django.db.models.Model .
  3. django.forms.Form (como se proporciona a través de la herencia por django.forms.ModelForm ) es la Vista con la que interactúa.
    • Esto hace que las cosas salgan borrosas, ya que ModelForm es un Form , por lo que las dos capas están estrechamente acopladas. En mi opinión, esto se hizo por brevedad en nuestro código y para su reutilización dentro del código de los desarrolladores de Django.

De esta manera, django.forms.ModelForm (con sus datos y lógica empresarial) se convierte en un Modelo en sí mismo. Podría hacer referencia a él como (MVC) VC, que es una implementación bastante común en OOP.

Tomemos, por ejemplo, la clase django.db.models.Model de Django. Cuando observamos los objetos django.db.models.Model , vemos Modelo a pesar de que ya es una implementación completa de MVC. Suponiendo que MySQL es la base de datos de back-end:

  • MySQLdb es el Modelo (capa de almacenamiento de datos y lógica de negocios sobre cómo interactuar / validar los datos).
  • django.db.models.query es el Controlador (maneja la entrada de la Vista y la traduce para el Modelo ).
  • django.db.models.Model es la Vista (con lo que el usuario interactúa).
    • En este caso, los desarrolladores (usted y yo) somos el "usuario".

Esta interacción es la misma que la de sus "desarrolladores del lado del cliente" cuando trabaja con objetos yourproject.forms.YourForm (heredando de django.forms.ModelForm ):

  • Como necesitamos saber cómo interactuar con django.db.models.Model , deberían saber cómo interactuar con yourproject.forms.YourForm (su Modelo ).
  • Como no necesitamos saber sobre MySQLdb , sus "desarrolladores del lado del cliente" no necesitan saber nada sobre yourproject.models.YourModel .
  • En ambos casos, rara vez debemos preocuparnos por cómo se implementa realmente el Controlador .
respondido por el Jack M. 08.07.2011 - 18:28

Lea otras preguntas en las etiquetas