Чи порушують форми Джанго порушення МВС?


16

Я щойно почав працювати з Django, починаючи з років Spring MVC, і впровадження форм вражає як трохи божевільний. Якщо ви не знайомі, форми Django починаються з класу моделі форми, яка визначає ваші поля. Весна аналогічно починається з об'єкта, що підтримує форму. Але там, де Spring надає таліб для прив’язки елементів форми до резервного об'єкта у вашому JSP, Django має віджети форми, прив'язані безпосередньо до моделі. Існують віджети за замовчуванням, де ви можете додати атрибути стилю до своїх полів, щоб застосувати CSS або визначити повністю власні віджети як нові класи. Все це входить у ваш пітон-код. Мені це здається гайкою. По-перше, ви вводите інформацію про свій погляд безпосередньо у свою модель, а по-друге, ви прив'язуєте свою модель до певного виду. Я щось пропускаю?

EDIT: Деякі приклади коду за запитом.

Джанго:

# 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'}))

Весняний 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>

"інформація про ваш погляд безпосередньо у вашій моделі"? Будь ласка, будьте конкретні. "прив'язування вашої моделі до певного виду"? Будь ласка, будьте конкретні. Надайте конкретні конкретні приклади. Загальну скаргу, розмахуючи руками, подібну до цього важко зрозуміти, тим більше відповідати.
S.Lott

Я ще нічого не побудував, просто читав документи. Ви прив'язуєте віджет HTML разом із класами CSS до класу Form безпосередньо в коді Python. Ось що я кличу.
джиггі

де ще ви хочете зробити цю прив'язку? Надайте приклад або посилання або цитату на конкретну річ, проти якої ви заперечуєте. Гіпотетичний аргумент важко дотримуватися.
S.Lott

Я зробив. Подивіться, як це робить Spring MVC. Ви вводите об'єкт, що підтримує форму (наприклад, клас форми Django) у свій погляд. Потім ви пишете свій HTML за допомогою талібів, щоб ви могли спроектувати свій HTML як звичайний та просто додати атрибут шляху, який прив’яже його до властивостей об’єкта, що підтримує форму.
джиггі

Будь ласка, оновіть питання, щоб чітко зрозуміти, на що ви заперечуєте. Питання важко відповісти. У ній немає прикладу коду, щоб зробити вашу точку зору абсолютно зрозумілою.
S.Lott

Відповіді:


21

Так, форми Django - це безлад з точки зору MVC, припустимо, ви працюєте у великій грі супергерой MMO і створюєте модель Hero:

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

Тепер вам пропонується створити для нього форму, щоб гравці MMO могли вводити свої супергерої героя:

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

Оскільки Репелент для акул - дуже потужна зброя, ваш начальник попросив вас обмежити це. Якщо у героя є Репелент для акул, він не може літати. Більшість людей - це просто додати це ділове правило у форму «чисто» та називати це щодня:

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!")

Ця модель виглядає круто і може працювати в невеликих проектах, але, на мій досвід, це дуже важко підтримувати у великих проектах з кількома розробниками. Проблема полягає в тому, що форма є частиною погляду MVC. Тож вам доведеться пам’ятати про це правило кожного разу, коли:

  • Напишіть іншу форму, яка стосується моделі Hero.
  • Напишіть сценарій, який імпортує героїв з іншої гри.
  • Вручну міняйте екземпляр моделі під час ігрової механіки.
  • тощо.

Моя думка в тому, що form.py - це все про макет форми та презентацію, ви ніколи не повинні додавати у цей файл ділову логіку, якщо вам не подобається возитися зі кодом спагетті.

Найкращий спосіб вирішити проблему героя - це використати чистий метод моделі плюс спеціальний сигнал. Очищення моделі працює як форма чиста, але вона зберігається у самій моделі, кожного разу, коли HeroForm очищається, вона автоматично викликає метод Hero clean. Це хороша практика, тому що якщо інший розробник напише іншу форму для героя, він отримає перевірку відштовхування / льоту безкоштовно.

Проблема з чистим полягає в тому, що він називається лише тоді, коли модель модифікується формою. Він не викликається, коли ви вручну зберігаєте його (), і ви можете закінчити з недійсним героєм у вашій базі даних. Щоб вирішити цю проблему, ви можете додати цього слухача до свого проекту:

from django.db.models.signals import pre_save

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

Це викликає чистий метод під час кожного дзвінка save () для всіх ваших моделей.


Це дуже корисна відповідь. Однак більша частина моїх форм і відповідних перевірок включає кілька полів на декількох моделях. Я думаю, що це дуже важливий сценарій. Як би ви виконали таку перевірку одним із чистих методів моделі?
Бобборт

8

Ви змішуєте весь стек, тут задіяно кілька шарів:

  • модель Джанго визначає структуру даних.

  • Форма Django - це ярлик для визначення форм HTML, перевірки поля та перекладів значення Python / HTML. Це не суворо потрібно, але часто зручно.

  • a Django ModelForm - це ще один ярлик, коротше підклас Form, який отримує свої поля з визначення моделі. Просто зручний спосіб для звичайного випадку, коли форма використовується для введення даних у базу даних.

і, нарешті:

  • Архітектори Django точно не дотримуються структури MVC. Іноді вони називають це MTV (Model Template View); тому що немає такого поняття як контролер, а розкол між шаблоном (просто презентація, немає логіки) та View (просто логіка, орієнтована на користувача, без HTML) так само важливий, як ізоляція Моделі.

Деякі люди сприймають це як єресь; але важливо пам’ятати, що MVC спочатку був визначений для програм GUI, і це досить незручно підходить для веб-додатків.


Але віджети - це презентація, і вони передаються прямо у вашу форму. Звичайно, я не можу їх використовувати, але тоді ви втрачаєте переваги прив’язки та валідації. Моя думка полягає в тому, що Spring надає вам обов'язковість та валідацію та повне розділення моделі та перегляду. Я думаю, що Джанго міг легко реалізувати щось подібне. І я розглядаю конфігурацію URL як свого роду вбудований передній контролер, який є досить популярною схемою для Spring MVC.
джиггі

Найкоротший код виграє.
Кевін Клайн

1
@jiggy: форми є частиною презентації, прив'язка та перевірка лише для введених користувачем даних. моделі мають власну прив'язку та валідацію, окремі та незалежні від форм. Форма моделі - це лише ярлик, коли вони 1: 1 (або майже)
Хав'єр

Лише невелике зауваження, що так, MVC насправді не мав сенсу у веб-додатках ... поки AJAX не повернув його знову.
AlexanderJohannesen

Перегляд форми перегляду. Перевірка форми є контролером. Дані форми - модель. ІМО, принаймні. Джанго задушує їх усіх разом. Якщо уникнути педантизму, це означає, що якщо ви працюєте з відданими розробниками на базі клієнтів (як це робить моя компанія), все це є нічим не марним.
джиггі

4

Я відповідаю на це давнє запитання, оскільки інші відповіді, схоже, уникають конкретного питання, про який йде мова.

Форми Django дозволяють легко писати невеликий код і створювати форму з розумними типовими настройками. Будь-яка кількість налаштування дуже швидко призводить до "більше коду" та "більшої роботи" і дещо зводить нанівець первинну перевагу системи форми

Бібліотеки шаблонів, як налаштування django-widget-tweaks значно спрощують налаштування форм. Сподіваємося, налаштування на місцях, як це, зрештою буде легким за допомогою установки ванільного Django.

Ваш приклад із налаштуваннями 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>


1

(Я використовував курсиви для позначення концепцій MVC, щоб зробити це більш розбірливим.)

Ні, на мою думку, вони не порушують MVC. Працюючи з моделями / формами Django, подумайте про це як про використання всього стека MVC як моделі :

  1. django.db.models.Modelє базовою моделлю (містить дані та логіку бізнесу).
  2. django.forms.ModelFormзабезпечує Контролер для взаємодії з django.db.models.Model.
  3. django.forms.Form(надається через успадкування django.forms.ModelForm) - це Вид, з яким ви взаємодієте.
    • Це робить речі розмитими, оскільки ModelFormце a Form, тому два шари щільно з'єднані. На мою думку, це було зроблено для стислості в нашому коді та для повторного використання коду в коді розробників Django.

Таким чином django.forms.ModelForm(зі своїми даними та діловою логікою) стає моделлю самою . Ви можете посилатися на це як (MVC) VC, що є досить поширеною реалізацією в OOP.

Візьмемо, наприклад, django.db.models.Modelклас Джанго . Коли ми дивимось на django.db.models.Modelоб’єкти, ми бачимо Модель, хоча це вже повноцінна реалізація MVC. Припустимо, що MySQL є базовою базою даних:

  • MySQLdbє Модель (рівень зберігання даних та бізнес-логіка щодо взаємодії з / валідацією даних).
  • django.db.models.queryє контролер (ручки введення з видом і переводить його в моделі ).
  • django.db.models.Modelє Вид (з яким взаємодіє користувач).
    • У цьому випадку розробники (ви та я) є "користувачем".

Ця взаємодія така ж, як і ваші "розробники на стороні клієнта", коли ви працюєте з yourproject.forms.YourForm(успадковуючи від django.forms.ModelForm) об'єктами:

  • Оскільки нам потрібно знати, як взаємодіяти django.db.models.Model, вони повинні знати, як взаємодіяти yourproject.forms.YourForm(їх Модель) ).
  • Оскільки нам не потрібно знати про це MySQLdb, вашим "розробникам на стороні клієнта" нічого не потрібно знати yourproject.models.YourModel.
  • В обох випадках нам дуже рідко потрібно турбуватися про те, як насправді реалізується Контролер .

1
Замість того, щоб обговорювати семантику дискусій, я просто хочу зберігати свій HTML і CSS у своїх шаблонах, і не потрібно розміщувати його у файлах .py. Філософія осторонь, це просто практичний кінець, якого я хочу досягти, оскільки він більш ефективний і дозволяє краще розподілити працю.
джиггі

1
Це все-таки цілком можливо. Ви можете вручну записати свої поля в шаблони, вручну записати валідацію у своїх переглядах, а потім оновити свої моделі вручну. Але дизайн форм Django не порушує MVC.
Джек М.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.