Các hình thức Django có vi phạm MVC không?


16

Tôi mới bắt đầu làm việc với Django đến từ những năm của Spring MVC và các hình thức triển khai biểu mẫu là hơi điên rồ. Nếu bạn không quen thuộc, các biểu mẫu Django bắt đầu bằng một lớp mô hình biểu mẫu xác định các trường của bạn. Mùa xuân tương tự bắt đầu với một đối tượng ủng hộ mẫu. Nhưng trong đó Spring cung cấp một taglib cho các thành phần biểu mẫu liên kết với đối tượng sao lưu trong tệp JSP của bạn, Django có các tiện ích biểu mẫu được gắn trực tiếp vào mô hình. Có các widget mặc định nơi bạn có thể thêm các thuộc tính kiểu cho các trường của mình để áp dụng CSS hoặc định nghĩa các widget hoàn toàn tùy chỉnh như các lớp mới. Tất cả diễn ra trong mã python của bạn. Điều đó có vẻ như hạt dẻ với tôi. Đầu tiên, bạn đang đưa thông tin về chế độ xem của bạn trực tiếp vào mô hình của mình và thứ hai là bạn đang ràng buộc mô hình của mình với một chế độ xem cụ thể. Tui bỏ lỡ điều gì vậy?

EDIT: Một số mã ví dụ theo yêu cầu.

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

MVC mùa xuân:

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>

"Thông tin về quan điểm của bạn trực tiếp trong mô hình của bạn"? Hãy cụ thể. "ràng buộc mô hình của bạn với một cái nhìn cụ thể"? Hãy cụ thể. Vui lòng cung cấp cụ thể, ví dụ cụ thể. Một lời phàn nàn chung chung, vẫy tay như thế này rất khó hiểu, ít phản hồi hơn.
S.Lott

Tôi chưa xây dựng gì cả, chỉ đọc tài liệu. Bạn liên kết một tiện ích HTML cùng với các lớp CSS với lớp Biểu mẫu của bạn trực tiếp bằng mã Python. Đó là những gì tôi đang gọi.
nhảy lên

nơi nào khác bạn muốn làm ràng buộc này? Vui lòng cung cấp một ví dụ hoặc liên kết hoặc trích dẫn cho điều cụ thể mà bạn phản đối. Lập luận giả thuyết là khó theo dõi.
S.Lott

Tôi đã làm. Hãy nhìn cách Spring MVC làm điều đó. Bạn đưa đối tượng sao lưu biểu mẫu (như lớp Django Form) vào chế độ xem của bạn. Sau đó, bạn viết HTML bằng cách sử dụng taglib để bạn có thể thiết kế HTML của mình như bình thường và chỉ cần thêm một thuộc tính đường dẫn sẽ liên kết nó với các thuộc tính của đối tượng sao lưu biểu mẫu của bạn.
nhảy lên

Vui lòng cập nhật câu hỏi để làm cho nó hoàn toàn rõ ràng những gì bạn đang phản đối. Câu hỏi khó theo dõi. Nó không có mã ví dụ để làm cho quan điểm của bạn hoàn toàn rõ ràng.
S.Lott

Câu trả lời:


21

Đúng, các hình thức Django là một mớ hỗn độn từ phối cảnh MVC, giả sử bạn đang làm việc trong một trò chơi siêu anh hùng MMO lớn và bạn đang tạo mô hình Hero:

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

Bây giờ bạn được yêu cầu tạo một biểu mẫu cho nó, để người chơi MMO có thể nhập siêu năng lực anh hùng của họ:

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

Vì Shark Repellent là một vũ khí rất mạnh, nên ông chủ của bạn yêu cầu bạn hạn chế nó. Nếu một anh hùng có Shark Repellent thì anh ta không thể bay. Những gì hầu hết mọi người làm chỉ đơn giản là thêm quy tắc kinh doanh này trong biểu mẫu sạch và gọi nó là một ngày:

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

Mẫu này trông rất tuyệt và có thể hoạt động trên các dự án nhỏ, nhưng theo kinh nghiệm của tôi, điều này rất khó để duy trì trong các dự án lớn có nhiều nhà phát triển. Vấn đề là hình thức là một phần của khung nhìn của MVC. Vì vậy, bạn sẽ phải nhớ quy tắc kinh doanh đó mỗi khi bạn:

  • Viết một hình thức khác liên quan đến mô hình Hero.
  • Viết một kịch bản nhập anh hùng từ một trò chơi khác.
  • Thay đổi thủ công mô hình trong quá trình cơ học trò chơi.
  • Vân vân.

Quan điểm của tôi ở đây là Forms.txt là tất cả về cách bố trí và trình bày biểu mẫu, bạn không bao giờ nên thêm logic nghiệp vụ vào tệp đó trừ khi bạn thích làm rối với mã spaghetti.

Cách tốt nhất để xử lý vấn đề anh hùng là sử dụng phương pháp làm sạch mô hình cộng với tín hiệu tùy chỉnh. Mô hình sạch hoạt động giống như biểu mẫu sạch nhưng được lưu trữ trong chính mô hình, bất cứ khi nào HeroForm được xóa, nó sẽ tự động gọi phương thức dọn dẹp Hero. Đây là một cách thực hành tốt vì nếu một nhà phát triển khác viết một biểu mẫu khác cho Hero, anh ta sẽ được xác nhận miễn phí / bay.

Vấn đề với sự sạch sẽ là nó chỉ được gọi khi một Mô hình được sửa đổi bởi một hình thức. Nó không được gọi khi bạn lưu thủ công () nó và bạn có thể kết thúc với một anh hùng không hợp lệ trong cơ sở dữ liệu của mình. Để khắc phục vấn đề này, bạn có thể thêm người nghe này vào dự án của bạn:

from django.db.models.signals import pre_save

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

Điều này sẽ gọi phương thức sạch trên mỗi lệnh gọi save () cho tất cả các kiểu máy của bạn.


Đây là một câu trả lời rất hữu ích. Tuy nhiên, phần lớn các biểu mẫu và xác nhận tương ứng của tôi liên quan đến một số trường trên một số mô hình. Tôi nghĩ đó là một kịch bản rất quan trọng để xem xét. Làm thế nào bạn sẽ thực hiện xác nhận như vậy trên một trong các phương pháp sạch của mô hình?
Bobort

8

Bạn đang trộn toàn bộ ngăn xếp, có một số lớp liên quan:

  • Mô hình Django xác định cấu trúc dữ liệu.

  • Biểu mẫu Django là lối tắt để xác định biểu mẫu HTML, xác thực trường và bản dịch giá trị Python / HTML. Nó không thực sự cần thiết, nhưng thường tiện dụng.

  • Django ModelForm là một lối tắt khác, nói ngắn gọn là một lớp con Form lấy các trường của nó từ một định nghĩa Model. Chỉ là một cách thuận tiện cho trường hợp phổ biến trong đó một biểu mẫu được sử dụng để nhập dữ liệu vào cơ sở dữ liệu.

và cuối cùng:

  • Kiến trúc sư Django không tuân thủ chính xác cấu trúc MVC. Đôi khi họ gọi nó là MTV (Chế độ xem mẫu); bởi vì không có thứ gọi là bộ điều khiển và sự phân chia giữa khuôn mẫu (chỉ trình bày, không logic) và Chế độ xem (chỉ logic đối diện với người dùng, không có HTML) cũng quan trọng như sự cô lập của Mô hình.

Một số người coi đó là dị giáo; nhưng điều quan trọng cần nhớ là MVC ban đầu được xác định cho các ứng dụng GUI và nó khá phù hợp với các ứng dụng web.


Nhưng các vật dụng được trình bày và chúng được nối thẳng vào Biểu mẫu của bạn. Chắc chắn, tôi không thể sử dụng chúng, nhưng sau đó bạn mất đi lợi ích của việc ràng buộc và xác nhận. Quan điểm của tôi là Spring cung cấp cho bạn ràng buộc và xác nhận và tách biệt hoàn toàn mô hình và khung nhìn. Tôi nghĩ Django có thể dễ dàng thực hiện một cái gì đó tương tự. Và tôi xem cấu hình url như một loại bộ điều khiển phía trước được tích hợp sẵn, đây là một mẫu khá phổ biến cho Spring MVC.
nhảy lên

Mã ngắn nhất sẽ thắng.
kevin cline

1
@jiggy: biểu mẫu là một phần của bản trình bày, ràng buộc và xác thực chỉ dành cho dữ liệu do người dùng nhập. các mô hình có ràng buộc và xác nhận riêng, tách biệt và độc lập với các biểu mẫu. mô hình chỉ là một lối tắt khi chúng là 1: 1 (hoặc gần như)
Javier

Chỉ cần lưu ý một chút rằng, vâng, MVC không thực sự có ý nghĩa trong các ứng dụng web ... cho đến khi AJAX đưa nó trở lại một lần nữa.
AlexanderJohannesen

Hình thức hiển thị là xem. Xác nhận mẫu là bộ điều khiển. Dữ liệu mẫu là mô hình. IMO, ít nhất. Django kết hợp tất cả chúng lại với nhau. Bỏ qua một bên, điều đó có nghĩa là nếu bạn thuê các nhà phát triển phía khách hàng chuyên dụng (như công ty của tôi làm) thì toàn bộ điều này là vô ích.
nhảy lên

4

Tôi đang trả lời câu hỏi cũ này vì các câu trả lời khác dường như tránh được vấn đề cụ thể được đề cập.

Các hình thức Django cho phép bạn dễ dàng viết ít mã và tạo một biểu mẫu với mặc định lành mạnh. Bất kỳ số lượng tùy chỉnh nào rất nhanh chóng dẫn đến "nhiều mã hơn" và "nhiều công việc hơn" và phần nào vô hiệu hóa lợi ích chính của hệ thống biểu mẫu

Các thư viện mẫu như django-widget-chỉnh sửa giúp việc tùy chỉnh biểu mẫu dễ dàng hơn nhiều. Hy vọng các tùy chỉnh trường như thế này cuối cùng sẽ dễ dàng với cài đặt Django vanilla.

Ví dụ của bạn với django-widget-chỉnh:

{% 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

(Tôi đã sử dụng chữ nghiêng để biểu thị các khái niệm MVC để làm cho điều này dễ đọc hơn.)

Không, theo tôi, họ không phá vỡ MVC. Khi làm việc với Mô hình / Biểu mẫu Django, hãy nghĩ về nó như sử dụng toàn bộ ngăn xếp MVC làm Mô hình :

  1. django.db.models.ModelMô hình cơ sở (giữ dữ liệu và logic nghiệp vụ).
  2. django.forms.ModelFormcung cấp Bộ điều khiển để tương tác với django.db.models.Model.
  3. django.forms.Form(như được cung cấp thông qua kế thừa bởi django.forms.ModelForm) là Chế độ xem bạn tương tác.
    • Điều này không làm cho mọi thứ mờ đi, vì ModelFormlà một Form, vì vậy hai lớp được liên kết chặt chẽ. Theo tôi, điều này được thực hiện để đơn giản trong mã của chúng tôi và để sử dụng lại mã trong mã của nhà phát triển Django.

Theo cách này, django.forms.ModelForm(với dữ liệu và logic kinh doanh của nó) sẽ trở thành một Mô hình . Bạn có thể tham chiếu nó dưới dạng (MVC) VC, đây là một triển khai khá phổ biến trong OOP.

Lấy ví dụ, django.db.models.Modellớp của Django . Khi chúng ta nhìn vào các django.db.models.Modelđối tượng, chúng ta thấy Model mặc dù nó đã là một triển khai đầy đủ của MVC. Giả sử MySQL là cơ sở dữ liệu phía sau:

  • MySQLdbMô hình (lớp lưu trữ dữ liệu và logic nghiệp vụ liên quan đến cách tương tác với / xác thực dữ liệu).
  • django.db.models.queryBộ điều khiển (xử lý đầu vào từ Chế độ xem và dịch nó cho Mô hình ).
  • django.db.models.ModelChế độ xem (những gì người dùng tương tác với).
    • Trong trường hợp này, các nhà phát triển (bạn và tôi) là 'người dùng'.

Tương tác này giống như "nhà phát triển phía máy khách" của bạn khi làm việc với các đối tượng yourproject.forms.YourForm(kế thừa từ django.forms.ModelForm):

  • Khi chúng ta cần biết cách tương tác django.db.models.Model, họ sẽ cần biết cách tương tác với yourproject.forms.YourForm( Mô hình của họ ).
  • Vì chúng tôi không cần biết MySQLdb, "nhà phát triển phía khách hàng" của bạn không cần biết gì về nó yourproject.models.YourModel.
  • Trong cả hai trường hợp, chúng tôi rất ít khi phải lo lắng về cách thức Bộ điều khiển được thực hiện.

1
Thay vì tranh luận về ngữ nghĩa, tôi chỉ muốn giữ HTML và CSS của mình trong các mẫu của mình và không phải đặt bất kỳ tệp nào trong các tệp .py. Bỏ qua triết lý, đó chỉ là một kết thúc thực tế mà tôi muốn đạt được vì nó hiệu quả hơn và cho phép phân công lao động tốt hơn.
nhảy lên

1
Điều đó vẫn hoàn toàn có thể. Bạn có thể tự viết các trường của mình trong các mẫu, viết thủ công xác thực của bạn trong chế độ xem và sau đó cập nhật thủ công các mô hình của bạn. Nhưng thiết kế của Django's Forms không phá vỡ MVC.
Jack M.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.