Django formları MVC'yi ihlal ediyor mu?


16

Bahar MVC yıllarından gelen Django ile çalışmaya başladım ve form uygulama grevleri biraz çılgınca. Eğer aşina değilseniz, Django formları alanlarınızı tanımlayan bir form modeli sınıfıyla başlar. Bahar da benzer şekilde form destekli bir nesne ile başlar. Ancak Spring, JSP'nizdeki destek nesnesine form öğelerini bağlamak için bir taglib sağlarsa , Django'nun doğrudan modele bağlı form widget'ları vardır. CSS uygulamak veya tamamen özel widget'ları yeni sınıflar olarak tanımlamak için alanlarınıza stil nitelikleri ekleyebileceğiniz varsayılan widget'lar vardır. Her şey python kodunuza giriyor. Bana deli gibi geliyor. İlk olarak, görünümünüzle ilgili bilgileri doğrudan modelinize koyuyorsunuz ve ikinci olarak modelinizi belirli bir görünüme bağlıyorsunuz. Bir şey mi kaçırıyorum?

EDIT: Bazı örnek kod istendiği gibi.

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

Bahar 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>

"doğrudan modelinizde görüşünüz hakkında bilgi"? Lütfen açık ol. "modelinizi belirli bir görünüme bağlama"? Lütfen açık ol. Lütfen somut, spesifik örnekler verin. Bunun gibi genel, el sallayan bir şikayeti anlamak zordur, çok daha az yanıt verir.
S.Lott

Henüz hiçbir şey yapmadım, sadece belgeleri okudum. Bir HTML widget'ını CSS sınıflarıyla birlikte Form sınıfınıza doğrudan Python koduyla bağlarsınız. Ben bunu söylüyorum.
jiggy

bu bağlamayı başka nerede yapmak istersin? Lütfen itiraz ettiğiniz belirli bir örneğe veya bağlantıya veya fiyat teklifi verin . Varsayımsal iddiayı takip etmek zor.
S.Lott

Yaptım. Spring MVC'nin bunu nasıl yaptığına bakın. Form destekleme nesnesini (Django Form sınıfı gibi) görünümünüze enjekte edersiniz. Ardından HTML'nizi normal olarak tasarlayabilmeniz için HTML'nizi yazarsınız ve form destekleme nesnenizin özelliklerine bağlayacak bir yol özniteliği ekleyebilirsiniz.
jiggy

Neye itiraz ettiğinizi tam olarak netleştirmek için lütfen soruyu güncelleyin . Soruyu takip etmek zor. Noktanızı mükemmel bir şekilde ortaya koymak için örnek bir kodu yoktur .
S.Lott

Yanıtlar:


21

Evet, Django formları MVC perspektifinden bir karmaşa, büyük bir MMO süper kahraman oyununda çalıştığınızı ve Hero modelini oluşturduğunuzu varsayalım:

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

Şimdi bunun için bir form oluşturmanız isteniyor, böylece MMO oyuncuları kahraman süper güçlerini girebiliyor:

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

Köpekbalığı Kovucu çok güçlü bir silah olduğu için patronunuz sizden onu sınırlamanızı istedi. Bir kahramanın Köpekbalığı Kovucu varsa, o zaman uçamaz. Çoğu insanın yaptığı şey, bu iş kuralını temiz bir şekilde eklemek ve bir gün olarak adlandırmaktır:

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

Bu model harika görünüyor ve küçük projelerde işe yarayabilir, ancak benim deneyimime göre, bu, birden fazla geliştiriciye sahip büyük projelerde sürdürülmesi çok zordur. Sorun formun MVC görüşünün bir parçası olmasıdır . Bu nedenle, her zaman bu iş kuralını hatırlamanız gerekecek:

  • Hero modeliyle ilgilenen başka bir form yazın.
  • Başka bir oyundan kahramanları içe aktaran bir senaryo yazın.
  • Oyun mekaniği sırasında model örneğini manuel olarak değiştirin.
  • vb.

Demek istediğim, forms.py'nin form düzeni ve sunumu hakkında olması, spagetti koduyla uğraşmaktan hoşlanmadıkça asla bu dosyaya iş mantığı eklememelisiniz.

Kahraman sorununu çözmenin en iyi yolu, model temizleme yöntemini ve özel bir sinyali kullanmaktır. Model temiz form temiz gibi çalışır, ancak modelin kendisinde saklanır, HeroForm her temizlendiğinde otomatik olarak Hero clean yöntemini çağırır. Bu iyi bir uygulamadır, çünkü başka bir geliştirici Kahraman için başka bir form yazarsa, kovucu / sinek doğrulamasını ücretsiz olarak alacaktır.

Temiz ile ilgili sorun, sadece bir Model bir form tarafından değiştirildiğinde çağrılmasıdır. Manuel olarak kaydettiğinizde () çağrılmaz ve veritabanınızda geçersiz bir kahramanla sonuçlanabilir. Bu soruna karşı koymak için, bu dinleyiciyi projenize ekleyebilirsiniz:

from django.db.models.signals import pre_save

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

Bu, tüm modelleriniz için her save () çağrısında clean yöntemini çağırır.


Bu çok yardımcı bir cevap. Ancak, formlarımın ve karşılık gelen doğrulamamın çoğu, çeşitli modellerde birkaç alanı içerir. Bunun çok önemli bir senaryo olduğunu düşünüyorum. Modelin temiz yöntemlerinden birinde bu tür bir doğrulamayı nasıl gerçekleştirirsiniz?
Bobort

8

Yığının tamamını karıştırıyorsunuz, birkaç katman var:

  • bir Django Modeli veri yapısını tanımlar.

  • Django Formu, HTML formlarını, alan doğrulamalarını ve Python / HTML değeri çevirilerini tanımlayan bir kısayoldur. Kesinlikle gerekli değildir, ancak genellikle kullanışlıdır.

  • Django ModelForm başka bir kısayoldur, kısacası alanlarını Model tanımından alan bir Form alt sınıfıdır. Veritabanına veri girmek için bir formun kullanıldığı genel durum için kullanışlı bir yol.

ve sonunda:

  • Django mimarları MVC yapısına tam olarak bağlı değil. Bazen MTV (Model Şablon Görünümü) olarak adlandırırlar; çünkü kontrolör diye bir şey yoktur ve şablon (sadece sunum, mantık yok) ve Görünüm (sadece kullanıcı-yüz mantığı, HTML yok) arasındaki ayrım Model'in izolasyonu kadar önemlidir.

Bazı insanlar bunu sapkınlık olarak görür; ancak MVC'nin başlangıçta GUI uygulamaları için tanımlandığını ve web uygulamaları için oldukça uygun olduğunu hatırlamak önemlidir.


Ancak widget'lar sunumdur ve doğrudan Formunuza bağlanırlar. Tabii, onları kullanamam, ama sonra bağlama ve doğrulama faydalarını kaybedersiniz. Demek istediğim, Spring'in size bağlayıcılık, doğrulama ve model ile görüşün tamamen ayrılmasını sağlamasıdır. Django'nun benzer bir şeyi kolayca uygulayabileceğini düşünürdüm. Ve url yapılandırmasına, Spring MVC için oldukça popüler bir model olan bir çeşit dahili ön kontrol cihazı olarak bakıyorum.
jiggy

En kısa kod kazanır.
kevin cline

1
@jiggy: formlar sunumun bir parçasıdır, ciltleme ve doğrulama yalnızca kullanıcı tarafından girilen veriler içindir. modellerin formlardan ayrı ve bağımsız olarak kendi bağlama ve doğrulama işlemleri vardır. model formu 1: 1 (veya neredeyse) olduklarında sadece bir kısayoldur
Javier

Sadece hafif bir not, evet, MVC web uygulamalarında gerçekten mantıklı değildi ... AJAX tekrar yerine koyuncaya kadar.
AlexanderJohannesen

Form görüntüleme görünümdür. Form doğrulaması denetleyicidir. Form verileri modeldir. En azından IMO. Django hepsini bir araya getirir. Pedantry bir yana, bu, özel müşteri tarafı geliştiriciler (şirketimin yaptığı gibi) çalıştırırsanız, her şeyin işe yaramaz olduğu anlamına gelir.
jiggy

4

Bu eski soruyu cevaplıyorum, çünkü diğer cevaplar belirtilen özel konuyu engelliyor gibi görünüyor.

Django formları, kolayca küçük kod yazmanıza ve aklı başında varsayılanlarla bir form oluşturmanıza olanak tanır. Herhangi bir miktarda özelleştirme çok hızlı bir şekilde "daha fazla kod" ve "daha fazla iş" yapılmasına yol açar ve bir şekilde form sisteminin birincil faydasını geçersiz kılar

Django-widget-tweaks gibi şablon kütüphaneleri, formları özelleştirmeyi çok daha kolay hale getirir. Umarım böyle bir alan özelleştirmesi bir vanilya Django kurulumu ile sonunda kolay olacaktır.

Django-widget-tweaks ile örneğiniz:

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

(Bunu daha okunaklı hale getirmek için MVC kavramlarını göstermek için İtalik kullandım .)

Hayır, bence MVC'yi kırmıyorlar. Django Modelleri / Formları ile çalışırken, tüm MVC yığınını Model olarak kullanmak olarak düşünün :

  1. django.db.models.Modeltemel Modeldir (verileri ve iş mantığını tutar).
  2. django.forms.ModelFormile etkileşim için bir Denetleyici sağlar django.db.models.Model.
  3. django.forms.Form(kalıtım yoluyla sağlandığı şekliyle django.forms.ModelForm), etkileşim kurduğunuz Görünümdür .
    • Bu bir şeyleri bulanıklaştırır, çünkü ModelForma Form, bu nedenle iki katman sıkıca bağlanır. Bence bu, kodumuzdaki kısalık ve Django geliştiricileri kodu içinde kodun yeniden kullanımı için yapıldı.

Bu şekilde django.forms.ModelForm(verileri ve iş mantığı ile) bir Model haline gelir . OOP'ta oldukça yaygın bir uygulama olan (MVC) VC olarak başvurabilirsiniz.

Örneğin, Django'nun django.db.models.Modelsınıfını ele alalım . Biz baktığımızda django.db.models.Modelnesneleri, gördüğümüz Modeli zaten MVC tam uygulamasıdır rağmen. MySQL'in arka uç veritabanı olduğunu varsayarsak:

  • MySQLdbbir model (veri saklama katmanı ve ilgili iş mantığı nasıl etkileşim ile / verileri doğrulamak).
  • django.db.models.queryolan Kontrolör (dan kolları giriş Görünümü ve bunu çevirir Modeli ).
  • django.db.models.Modelolduğu Görünüm (ne ile Kullanıcı etkileşimde).
    • Bu durumda, geliştiriciler (siz ve ben) 'kullanıcı'yız.

Bu etkileşim, nesnelerle yourproject.forms.YourForm(devralma django.forms.ModelForm) nesnelerle çalışırken "istemci tarafı geliştiricileriniz" ile aynıdır :

  • Nasıl etkileşime django.db.models.Modelgireceğimizi bilmemiz gerektiğinden, yourproject.forms.YourForm( Modelleriyle ) nasıl etkileşime gireceklerini bilmeleri gerekir .
  • Bilmemiz gerekmediği için MySQLdb, "istemci tarafı geliştiricilerinizin" hiçbir şey bilmesine gerek yoktur yourproject.models.YourModel.
  • Her iki durumda da, Kontrolörün gerçekte nasıl uygulandığı konusunda çok nadiren endişelenmemiz gerekir .

1
Semantikleri tartışmak yerine, HTML ve CSS'mi şablonlarımda tutmak istiyorum ve bunların hiçbirini .py dosyalarına koymak zorunda değilim. Felsefe bir yana, bu elde etmek istediğim pratik bir son çünkü daha verimli ve daha iyi işbölümü sağlıyor.
jiggy

1
Bu hala mükemmel bir şekilde mümkün. Alanlarınızı şablonlara manuel olarak yazabilir, doğrulamalarınızı manuel olarak görünümlerinize yazabilir ve ardından modellerinizi manuel olarak güncelleyebilirsiniz. Ancak Django'nun Formlarının tasarımı MVC'yi bozmaz.
Jack M.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.