Django 양식이 MVC를 위반합니까?


16

방금 수년간 Spring MVC에서 온 Django와 함께 작업하기 시작했으며 양식 구현이 약간 미친 것으로 나타났습니다. 익숙하지 않은 경우 Django 양식 은 필드를 정의하는 양식 모델 클래스로 시작합니다. 스프링도 폼 백업 객체로 시작합니다. 그러나 Spring이 JSP 내의 지원 객체에 양식 요소를 바인딩하기 위해 taglib 을 제공하는 경우 Django는 모델에 직접 연결된 양식 위젯을 가지고 있습니다. 필드에 스타일 속성을 추가하여 CSS를 적용하거나 완전히 사용자 정의 위젯을 새 클래스로 정의 할 수있는 기본 위젯이 있습니다. 그것은 모두 파이썬 코드에 들어갑니다. 그것은 나에게 견과류처럼 보인다. 먼저 뷰에 대한 정보를 모델에 직접 넣고 두 번째로 모델을 특정 뷰에 바인딩합니다. 뭔가 빠졌습니까?

편집 : 요청 된 일부 예제 코드.

장고 :

# 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

나는 아직 아무것도 만들지 않고 단지 문서를 읽었습니다. CSS 클래스와 함께 HTML 위젯을 Python 코드에서 직접 Form 클래스에 바인딩합니다. 그것이 내가 부르는 것입니다.
jiggy

이 바인딩을 다른 곳에서 하시겠습니까? 반대하는 특정 사안에 대한 예 또는 링크 또는 인용문을 제공하십시오 . 가상의 주장은 따르기가 어렵다.
S.Lott

나는했다. Spring MVC의 작동 방식을 살펴보십시오. Django Form 클래스와 같은 폼 백업 객체를 뷰에 삽입합니다. 그런 다음 taglibs를 사용하여 HTML을 작성하여 HTML을 평소대로 디자인하고 HTML을 폼 백업 객체의 속성에 바인딩하는 경로 속성을 추가하면됩니다.
jiggy

제발 업데이트 가 완벽하게 당신이 반대하는지 명확하게하는 질문을. 질문은 따르기가 어렵다. 포인트를 완벽하게 명확 하게 하는 예제 코드는 없습니다 .
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

Shark Repellent는 매우 강력한 무기이므로 상사에게 제한을 요청했습니다. 영웅이 상어 퇴치제를 가지고 있다면 날 수 없습니다. 대부분의 사람들이하는 일은 단순히이 비즈니스 규칙을 깨끗한 형태로 추가하고 하루라고 부릅니다.

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 의 관점 의 일부라는 것입니다 . 따라서 다음과 같은 경우 항상 해당 비즈니스 규칙을 기억해야합니다.

  • 영웅 모델을 다루는 다른 양식을 작성하십시오.
  • 다른 게임에서 영웅을 가져 오는 스크립트를 작성하십시오.
  • 게임 메커니즘 중에 모델 인스턴스를 수동으로 변경하십시오.
  • 기타

여기서 요점은 forms.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 () 호출에서 clean 메소드를 호출합니다.


이것은 매우 유용한 답변입니다. 그러나 대부분의 양식과 해당 유효성 검사에는 여러 모델의 여러 필드가 포함됩니다. 저는 이것이 매우 중요한 시나리오라고 생각합니다. 모델의 깔끔한 방법 중 하나에서 이러한 유효성 검사를 어떻게 수행 하시겠습니까?
Bobort

8

전체 스택을 혼합하고 있으며 몇 가지 레이어가 있습니다.

  • 장고 모델은 데이터 구조를 정의합니다.

  • Django Form은 HTML 양식, 필드 유효성 검사 및 Python / HTML 값 변환을 정의하는 바로 가기입니다. 꼭 필요한 것은 아니지만 종종 편리합니다.

  • Django ModelForm은 모델 정의에서 필드를 가져 오는 Form 하위 클래스의 또 다른 바로 가기입니다. 데이터베이스에 데이터를 입력하기 위해 양식을 사용하는 일반적인 경우에 편리한 방법입니다.

그리고 마지막으로:

  • Django 건축가는 MVC 구조를 정확하게 준수하지 않습니다. 때로는 MTV (Model Template View)라고합니다. 컨트롤러와 같은 것은 없으며 템플릿 (프레젠테이션, 로직 없음)과 View (사용자 인터페이스, HTML 없음)의 분리는 Model의 격리만큼 중요합니다.

어떤 사람들은 이것을 이단으로 본다. 그러나 MVC는 원래 GUI 응용 프로그램 용으로 정의되었으며 웹 응용 프로그램에는 다소 적합하지 않다는 점을 기억해야합니다.


그러나 위젯은 프레젠테이션이며 양식에 바로 연결됩니다. 물론, 나는 그것들을 사용할 수 없지만 바인딩과 유효성 검사의 이점을 잃습니다. 내 요점은 Spring이 바인딩과 유효성 검사를 제공하고 모델과 뷰를 완전히 분리한다는 것입니다. 장고가 비슷한 것을 쉽게 구현했을 수 있다고 생각합니다. 그리고 URL 구성을 Spring MVC에서 꽤 인기있는 패턴 인 내장형 컨트롤러로 간주합니다.
jiggy

가장 짧은 코드가 승리합니다.
케빈 클라인

1
@jiggy : 양식은 프레젠테이션의 일부이며 바인딩 및 유효성 검사는 사용자가 입력 한 데이터에만 해당됩니다. 모델에는 양식과 별개로 독립적 인 자체 바인딩 및 유효성 검사가 있습니다. 모델 폼은 1 : 1 (또는 거의) 일 때의 지름길입니다
Javier

AJAX가 다시 다시 넣을 때까지 MVC는 웹 앱에서 실제로 의미가 없었습니다.
AlexanderJohannesen

양식 표시가 표시됩니다. 폼 검증은 컨트롤러입니다. 양식 데이터는 모델입니다. 최소한 IMO. Django는 그것들을 모두 함께 섞습니다. Pedantry는 제쳐두고, 그것은 당신이 (내 회사처럼) 고객 전용 개발자를 고용한다면이 모든 것이 쓸모가 없다는 것을 의미합니다.
jiggy

4

다른 답변은 언급 된 특정 문제를 피하는 것처럼 보이기 때문에이 오래된 질문에 대답하고 있습니다.

Django 양식을 사용하면 작은 코드를 쉽게 작성하고 기본값이 잘못된 양식을 만들 수 있습니다. 많은 양의 커스터마이징은 "더 많은 코드"와 "더 많은 작업"으로 이어지고 양식 시스템의 주요 이점을 다소 무효화합니다.

django-widget-tweaks 와 같은 템플릿 라이브러리를 사용 하면 양식을 훨씬 쉽게 사용자 지정할 수 있습니다. 바닐라 장고를 설치하면 이와 같은 필드 커스터마이징이 쉬워지기를 바랍니다.

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 Models / Forms로 작업 할 때 전체 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은 IS 모델 (데이터 저장 층, 및 관련 비즈니스 로직과 어떻게 상호 작용 / 검증 데이터).
  • django.db.models.query은 IS 컨트롤러 (핸들로부터 입력 보기 및 그것을 해석 모델 ).
  • django.db.models.Model는 IS 보기 (와 어떤 사용자 상호 작용은).
    • 이 경우 개발자 (귀하와 본인)는 '사용자'입니다.

이 상호 작용은 때와 협력하여 "클라이언트 측 개발자"와 동일합니다 yourproject.forms.YourForm(상속 django.forms.ModelForm객체) :

  • django.db.models.Model상호 작용하는 방법을 알아야하기 때문에 상호 작용하는 방법을 알아야합니다 yourproject.forms.YourForm( 모델 ).
  • 에 대해 알 필요가 없으므로 MySQLdb"클라이언트 측 개발자"는에 대해 아무것도 알 필요가 없습니다 yourproject.models.YourModel.
  • 두 경우 모두 컨트롤러 가 실제로 어떻게 구현 되는지 걱정할 필요가 거의 없습니다 .

1
의미론에 대해 토론하기보다는 HTML과 CSS를 템플릿에 유지하고 .py 파일에 넣지 않아도됩니다. 철학을 제외하면, 그것은 더 효율적이고 노동의 더 나은 분배를 허용하기 때문에 달성하고자하는 실질적인 목적입니다.
jiggy

1
그것은 여전히 ​​완벽하게 가능합니다. 템플릿에 필드를 수동으로 작성하고보기에 유효성 검사를 수동으로 작성한 다음 모델을 수동으로 업데이트 할 수 있습니다. 그러나 Django 's Forms 디자인은 MVC를 깨뜨리지 않습니다.
Jack M.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.