Biznes məntiqi və djangoda verilən məlumatların ayrılması

models.py bir layihə yazıram və kodun% models.py faylında olduğunu models.py . Bu kod qarışıqdır və bir müddət sonra nə baş verdiyini başa düşmürəm.

Bu məni narahat edir:

  • Çağırış edirəm ki, mənim model səviyyəm (yalnız verilənlər bazası məlumatları ilə işləmək üçün məsuliyyət daşıyırdı), həmçinin e-poçt göndərir, API vasitəsilə digər xidmətlərə keçir.
  • Bununla yanaşı biznes mantığını bir baxımdan yerləşdirmək qəbuledilməz hesab edirəm, çünki bu şəkildə nəzarət etmək çətindir. Məsələn, mənim ərizəmdə yeni User nümunələri yaratmaq üçün ən azı üç yolu vardır, lakin texniki cəhətdən onları bərabər yaratmalıdırlar.
  • Mənim modellərin metodları və xüsusiyyətləri qeyri-deterministik və yan təsirləri inkişaf etdikdə həmişə fərqlənmir.

Burada sadə bir nümunədir. İlk olaraq User modeli:

 class User(db.Models): def get_present_name(self): return self.name or 'Anonymous' def activate(self): self.status = 'activated' self.save() 

Vaxt keçdikcə bu vəziyyətə çevrildi:

 class User(db.Models): def get_present_name(self): # property became non-deterministic in terms of database # data is taken from another service by api return remote_api.request_user_name(self.uid) or 'Anonymous' def activate(self): # method now has a side effect (send message to user) self.status = 'activated' self.save() send_mail('Your account is activated!', '…', [self.email]) 

Obyektləri kodumda ayırmaq istəyirəm:

  • Mənim verilənlər bazası obyektlərim, verilənlər bazası səviyyəsi: tətbiqimdə nə var?
  • Mənim ərizəmizin obyektləri, işgüzar məntiq səviyyəsi: mənim tətbiqimə nə edə bilər?

Django'da tətbiq oluna biləcək bir yanaşmanın həyata keçirilməsi üçün ən yaxşı təcrübələr hansılardır?

279
25 сент. defuz 25 santimetr təyin edilir . 2012-09-25 11:23 '12 saat 11:23 'da 2012-09-25 11:23
@ 8 cavab

Məlumat modelləri və domen modeli arasındakı fərq barədə soruşursunuz. Sonuncu, biznes məntiqini və son istifadəçinizin qəbul etdiyinə aid olan şəxsləri tapa biləcəyiniz yerdir, birincisi, məlumatlarınızı həqiqətən saxladığınız yerdir.

Bundan əlavə, sorğunun üçüncü hissəsini aşağıdakı kimi şərh edirəm: bu modellərin ayrı-ayrılıqda saxlanılmasının imtina edilməsini necə bildirirəm.

Bunlar tamamilə fərqli iki anlayışdır və bunları ayırmaq həmişə çətindir. Bununla belə, bu məqsəd üçün istifadə edilə biləcək bəzi ümumi nümunələr və vasitələr var.

Domen modelləri haqqında

Tanıdığınız ilk şey domen modelinizin məlumatlarla əlaqəli olmadığıdır; "bu istifadəçi aktivləşdirmək", "bu istifadəçini söndürmək", "hansı istifadəçilər hazırda aktivləşdirilmişdir?" kimi fəaliyyət və məsələlərdən danışırıq. və "bu istifadəçi adı nədir?". Klassik baxımdan bu suallar və əmrlər haqqında.

Komandalarda düşünün

Sizin nümunənizdə əmrlərə baxaraq başlayın: "bu istifadəçi aktivləşdirin" və "bu istifadəçini ləğv et". Komandalar haqqında ən xoş bir şey, onlar asanlıqla kiçik bir "zaman" ssenarisi ilə ifadə edilə bilər:

bu effektiv istifadəçi
administrator bu istifadəçi aktivləşdirəndə
sonra istifadəçi aktiv olur
və bir təsdiq e-poçtu istifadəçiyə göndərilir
sistem girişinə bir giriş əlavə edilir
(və s.)

Belə bir skript sizin infrastrukturunuzun müxtəlif hissələrinə bir komandan necə təsir göstərdiyini görmək faydalıdır - bu halda, verilənlər bazası (bir növ "aktiv" bayraq), poçt serveriniz, syslog və s.

Belə bir skript həqiqətən test inkişaf mühit yaratmaqda sizə kömək edəcəkdir.

Və nəhayət, komanda düşünmə həqiqətən bir vəzifə yönümlü proqramlar yaratmanıza kömək edir. Sizin istifadəçilər bunu yüksək qiymətləndirirlər: -)

Komanda ifadə

Django əmrləri ifadə etmək üçün iki sadə yol təqdim edir; həm də etibarlı seçimdir, həm də iki yanaşmanın qarışığı qeyri-adi deyil.

Xidmət səviyyəsi

Xidmət modulu artıq @Hedde tərəfindən təsvir edilmişdir . Burada ayrı bir modul müəyyənləşdirir və hər komanda bir funksiya kimi təmsil olunur.

services.py

 def activate_user(user_id): user = User.objects.get(pk=user_id) # set active flag user.active = True user.save() # mail user send_mail(...) # etc etc 

Formaları istifadə

Başqa bir üsul isə hər komanda üçün Django Formasını istifadə etməkdir. Mən bu yanaşmanı üstün tuturam, çünki çox yaxından əlaqələndirən aspektləri birləşdirir:

  • komanda icraatı (o nə edir?)
  • komanda parametrlərini yoxlayın (bu mümkün ola bilərmi?)
  • komanda təqdimatı (necə edə bilərəm?)

forms.py

 class ActivateUserForm(forms.Form): user_id = IntegerField(widget = UsernameSelectWidget, verbose_name="Select a user to activate") # the username select widget is not a standard Django widget, I just made it up def clean_user_id(self): user_id = self.cleaned_data['user_id'] if User.objects.get(pk=user_id).active: raise ValidationError("This user cannot be activated") # you can also check authorizations etc. return user_id def execute(self): """ This is not a standard method in the forms API; it is intended to replace the 'extract-data-from-form-in-view-and-do-stuff' pattern by a more testable pattern. """ user_id = self.cleaned_data['user_id'] user = User.objects.get(pk=user_id) # set active flag user.active = True user.save() # mail user send_mail(...) # etc etc 

Sorgularda düşünün

Məsələn, hər hansı bir istək yoxdur, buna görə də bir çox faydalı istəklər yaratmaq üçün azadlıq aldım. Mən "sual" termini istifadə etməyə üstünlük verirəm, amma sorgular klassik terminolojidir. Maraqlı suallar: "Bu istifadəçi adı nədir?", "Bu istifadəçi daxil ola bilərmi?", "Əlil istifadəçilərin siyahısını göstər" və "Aktiv olanların coğrafi bölüşdürülməsi nədir?"

Bu istəklərə cavab vermədən əvvəl həmişə özünüzə iki sual verməlisiniz: bu, yalnız şablonlarım və / və ya əmrlərimin icrası ilə bağlı biznes məntiqi sorğusu və / və ya bir hesabat tələbi üçün təqdimat tələbidir.

Təqdimat tələbləri sadəcə istifadəçi interfeysi təkmilləşdirmək üçün edilir. İşgüzar tələblərə cavablar əmrlərinizin icrasını birbaşa təsir edir. Hesabat tələbləri yalnız analitik məqsədlər üçündir və məhdud vaxt məhdudiyyətləri vardır. Bu kateqoriyalar bir-birindən fərqlənmir.

Başqa bir sual: "Cavabların üzərində tam nəzarət edirəmmi?" Məsələn, bir istifadəçi adını (bu məzmunda) soruşarkən nəticəni üzərində heç bir nəzarət yoxdur, çünki biz xarici API-yə istinad edirik.

Sorgu icraatı

Django'dakı ən sadə sorğu Manager obyektini istifadə etməkdir:

 User.objects.filter(active=True) 

Əlbəttə ki, bu məlumat yalnız məlumat modelinizdə təqdim edildikdə işləyir. Həmişə belə deyil. Bu hallarda aşağıdakı variantları nəzərdən keçirə bilərsiniz.

Xüsusi etiket və filtreler

İlk seçim yalnız təqdimatlı istəklər üçün faydalıdır: xüsusi etiketlər və şablon filtrləri.

template.html

 <h1>Welcome, {{ user|friendly_name }}</h1> 

template_tags.py

 @register.filter def friendly_name(user): return remote_api.get_cached_name(user.id) 

Sorgu metodları

Sizin sorğunuz yalnız bir təqdimatçı deyilsə, services.py (əgər istifadə edirsinizsə) və ya modulu queries.py əlavə edin :

queries.py

 def inactive_users(): return User.objects.filter(active=False) def users_called_publysher(): for user in User.objects.all(): if remote_api.get_cached_name(user.id) == "publysher": yield user 

Proxy modelləri

Proxy modelləri biznes məntiq və hesabat bağlamında çox faydalıdır. Əsasən modelinizin genişlənmiş alt kümesini müəyyənləşdirirsiniz.

models.py

 class InactiveUserManager(models.Manager): def get_query_set(self): query_set = super(InactiveUserManager, self).get_query_set() return query_set.filter(active=False) class InactiveUser(User): """ >>> for user in InactiveUser.objects.all(): … assert user.active is False """ objects = InactiveUserManager() class Meta: proxy = True 

Sorgu modelləri

Mürəkkəb kompleks olan, lakin tez-tez yerinə yetirilən sorgular üçün sorğu modelləri mövcuddur. Bir sorgu modeli, tek bir sorgunun ilgili verileri ayrı bir modelde depolandığında denormalizasyon formasıdır. Əlbəttə ki, hiylə, əsas modeli ilə sinxronlaşdırma ilə denormalized modeli saxlamaqdır. Sorgu modelləri yalnız dəyişikliklər tamamilə nəzarət altında olduqda istifadə edilə bilər.

models.py

 class InactiveUserDistribution(models.Model): country = CharField(max_length=200) inactive_user_count = IntegerField(default=0) 

İlk seçim sizin komandalarınızda bu modellərin yenilənməsidir. Bu modellər yalnız bir və ya iki qrup tərəfindən dəyişdirildikdə çox faydalıdır.

forms.py

 class ActivateUserForm(forms.Form): # see above def execute(self): # see above query_model = InactiveUserDistribution.objects.get_or_create(country=user.country) query_model.inactive_user_count -= 1 query_model.save() 

Ən yaxşı seçim xüsusi siqnallardan istifadə etmək olar. Əlbəttə, siqnallarınız sizin komandalar tərəfindən yayılır. Siqnalların üstünlüyü, bir neçə sorgu modelini orijinal modeli ilə sinxronizasiya edə bilərsiniz. Bundan əlavə, siqnal emalı Kereviz və ya oxşar strukturları istifadə edərək fon vəzifələrinə yüklənə bilər.

Signals.py

 user_activated = Signal(providing_args = ['user']) user_deactivated = Signal(providing_args = ['user']) 

forms.py

 class ActivateUserForm(forms.Form): # see above def execute(self): # see above user_activated.send_robust(sender=self, user=user) 

models.py

 class InactiveUserDistribution(models.Model): # see above @receiver(user_activated) def on_user_activated(sender, **kwargs): user = kwargs['user'] query_model = InactiveUserDistribution.objects.get_or_create(country=user.country) query_model.inactive_user_count -= 1 query_model.save() 

Təmizliyin qorunması

Bu yanaşma istifadə edərək kodunuzun təmiz qaldığını asanlıqla müəyyən etmək gülməli olur. Yalnız bu qaydalara əməl edin:

  • Modelim verilənlər bazasının vəziyyətini idarə etmədən daha çox olan üsulları ehtiva edirmi? Komanda çıxarmaq lazımdır.
  • Modelim verilənlər bazasında görünməyən xüsusiyyətləri ehtiva edirmi? İstəyinizi almalısınız.
  • Mənim istinad infrastrukturum mənim bazam mənim bazam (məsələn, poçt) deyildir? Komanda çıxarmaq lazımdır.

Eyni idraklara da aiddir (anlayışlar tez-tez eyni problemdən qaynaqlanır).

  • Mənim fikrim aktiv məlumat bazası modelini idarə edirmi? Komanda çıxarmaq lazımdır.

Bəzi əlaqələr

Django Sənədləri: Proxy Modelleri

Django Sənədləri: Siqnallar

Memarlıq: domain idarəli dizayn

381
12 окт. Cavab verən yazıçı 12 oktyabr. 2012-10-12 13:59 '12 at 1:59 pm 2012-10-12 13:59

Mən adətən baxış və modellər arasında xidmət səviyyəsini istifadə edirəm. Layihə API kimi fəaliyyət göstərir və helikopterlərlə baş verən hadisələrə yaxşı bir baxış verir. Mən bu təcrübəni Java Layihələri (JSF) ilə bu layering texnikasını istifadə edən həmkarımdan miras aldım, məsələn:

models.py

 class Book: author = models.ForeignKey(User) title = models.CharField(max_length=125) class Meta: app_label = "library" 

services.py

border=0
 from library.models import Book def get_books(limit=None, **filters): """ simple service function for retrieving books can be widely extended """ if limit: return Book.objects.filter(**filters)[:limit] return Book.objects.filter(**filters) 

views.py

 from library.services import get_books class BookListView(ListView): """ simple view, eg implement a _build and _apply filters function """ queryset = get_books() 

Unutmayın ki, modul, fikir və xidmətləri modul səviyyəsində alıram və layihənin ölçüsündən asılı olaraq daha çox ayırıram.

91
25 сент. Hedde van der Heide tərəfindən verilən cavabı 25 Sep. 2012-09-25 11:58 '12 at 11:58 2012-09-25 11:58

Birincisi, təkrar etməyin .

Daha sonra təkrar emal olunmamaq üçün diqqətli olun, bəzən bu yalnız bir vaxt itkisinə səbəb olur və kimsə vacibliyə diqqət yetirməyə səbəb olur. Piytonun zenini mütəmadi olaraq yoxlayın.

Fəal layihələrə nəzər yetirin

  • daha çox insan = daha düzgün təşkil etmək lazımdır
  • django deposu , sadə bir quruluşa sahibdir.
  • Pivə deposu ilə əlaqəli bir əlaqə quruluşu var.
  • Doku deposu da baxılması üçün yaxşı bir şeydir.

    • Bütün modellərinizi yourapp/models/logicalgroup.py altında yerləşdirə bilərsiniz
  • məsələn, User , Group və əlaqəli modellər, yourapp/models/users.py altında ola bilər
  • məsələn, Poll , Question , Answer ... yourapp/models/polls.py altında ola bilər
  • yourapp/models/__init__.py daxilində yourapp/models/__init__.py üçün lazım __all__ yourapp/models/__init__.py

MVC-də daha çox

  • Model
  • - məlumatlarınız
    • bu, faktiki məlumatlarınızı ehtiva edir
    • bu da sessiya / cookie / cache / fs / index məlumatlarını ehtiva edir
  • istifadəçi modeli idarə etmək üçün nəzarətçi ilə qarşılıqlı əlaqə qurur
    • məlumatlarınızın saxlanılması / yenilənməsi üçün API və ya görünüş ola bilər.
    • bu, request.GET / request.POST ... və s. istifadə edərək konfiqurasiya edilə bilər.
    • paging və ya filtreleme .
  • məlumat yeniləmə görünüşü
    • Şablonlar məlumat alır və buna görə format verir.
    • Şablonsuz hətta API, görünüşün bir hissəsidir; məsələn tastypie və ya piston
    • bu da ara proqramı nəzərdən keçirməlidir.

Orta proqram / templatetags istifadə edin

  • Hər bir tələb üçün bir işə ehtiyac varsa, ara proqram bu yollardan biridir.
    • məsələn. zaman damgaları əlavə edin
    • məsələn. pageview metriklərini yeniləyir
    • məsələn. önbellek doldurma
  • Həmişə obyektləri formatlaşdırmaq üçün təkrarlanan kod snippetləri varsa, templatetags yaxşıdır.
    • məsələn. aktiv sekmeler / url çörək parçaları

Model idarəçilərindən faydalanın

  • Bir User yaratmaq UserManager(models.Manager) bilər.
  • models.Model üçün qəribə məlumatlar models.Model üçün models.Model . models.Model .
  • queryset üçün queryset detaylar models.Manager .
  • User bir-bir yaratmaq tələb oluna bilər, buna görə o, modelin özündə yaşamalı olduğunu düşünsəniz, ancaq obyekti yaratarkən ehtimal ki, bütün detalları yoxdur:

Məsələn:

 class UserManager(models.Manager): def create_user(self, username, ...): # plain create def create_superuser(self, username, ...): # may set is_superuser field. def activate(self, username): # may use save() and send_mail() def activate_in_bulk(self, queryset): # may use queryset.update() instead of save() # may use send_mass_mail() instead of send_mail() 

Mümkün olduğu yerlərdə istifadə formaları

Modeldən xəritəsi olan formalar varsa, bir çox kod şablonları aradan qaldırıla bilər. ModelForm documentation olduqca yaxşıdır. Bir çox parametriniz varsa (və ya bəzən daha mürəkkəb tətbiqlər üçün çevik idxal səhvlərindən qaçın), model kodundan forma kodları yaxşı ola bilər.

Mümkün olduğunda nəzarət əmrlərini istifadə edin.

  • məsələn. yourapp/management/commands/createsuperuser.py
  • məsələn. yourapp/management/commands/activateinbulk.py

Biznes mantığınız varsa, onu seçə bilərsiniz.

  • django.contrib.auth istifadə edir , db kimi bir backend var ... və s.
  • iş mantığınız üçün setting əlavə edin (məsələn, AUTHENTICATION_BACKENDS )
  • django.contrib.auth.backends.RemoteUserBackend istifadə edə bilərsiniz
  • yourapp.backends.remote_api.RemoteUserBackend istifadə edə bilərsiniz
  • yourapp.backends.memcached.RemoteUserBackend istifadə edə bilərsiniz
  • ardıcıllığa kompleks biznes məntiqinə nümayəndə göndərin
  • Düzgün I / O gözləmə dəyərini təyin etdiyinizə əmin olun.
  • Biznes məntiqi dəyişdirilməsi bir qəbulu dəyişdirmək kimi asandır :)

Məsələn, backend:

 class User(db.Models): def get_present_name(self): # property became not deterministic in terms of database # data is taken from another service by api return remote_api.request_user_name(self.uid) or 'Anonymous' 

ola bilər:

 class User(db.Models): def get_present_name(self): for backend in get_backends(): try: return backend.get_present_name(self) except: # make pylint happy. pass return None 

dizayn nümunələri haqqında daha ətraflı

interfeys sərhədləri haqqında daha çox məlumat

  • Modellərin bir hissəsini həqiqətən istifadə etmək istədiyiniz kod mu? → yourapp.models
  • Biznes mantığının kod hissəsi mi? → yourapp.vendor
  • Ümumi libs vasitələrinin kod bölməsi mi? → yourapp.libs
  • Libs biznes mantiq kodunun bir hissəsi mi? → yourapp.libs.vendor və ya yourapp.vendor.libs
  • İşdə yaxşı bir şey var: Kodunuzu özünüzdə yoxlaya bilərsinizmi?
    • bəli, yaxşı :)
    • Xeyr, interface ilə bir problem ola bilər.
    • aydın bir bölünmə olduqda, birləşmənin lağ edərək istifadə etdiyi bir esinti olmalıdır
  • Ayrılıq məntiqi mi?
    • bəli, yaxşı :)
    • Xeyr, bu mantıksal konsepsiyaları ayrı-ayrılıqda test etməklə problem ola bilər.
  • 10x kodunu aldığınızda yenidən təşkil etmək lazımdırmı?
    • Bəli, yaxşı deyil, bueno, bir refactor çox iş ola bilər.
    • Xeyr, bu, sadəcə zəhmli!

Bir sözlə, ola bilər

  • yourapp/core/backends.py
  • yourapp/core/models/__init__.py
  • yourapp/core/models/users.py
  • yourapp/core/models/questions.py
  • yourapp/core/backends.py
  • yourapp/core/forms.py
  • yourapp/core/handlers.py
  • yourapp/core/management/commands/__init__.py
  • yourapp/core/management/commands/closepolls.py
  • yourapp/core/management/commands/removeduplicates.py
  • yourapp/core/middleware.py
  • yourapp/core/signals.py
  • yourapp/core/templatetags/__init__.py
  • yourapp/core/templatetags/polls_extras.py
  • yourapp/core/views/__init__.py
  • yourapp/core/views/users.py
  • yourapp/core/views/questions.py
  • yourapp/core/signals.py
  • yourapp/lib/utils.py
  • yourapp/lib/textanalysis.py
  • yourapp/lib/ratings.py
  • yourapp/vendor/backends.py
  • yourapp/vendor/morebusinesslogic.py
  • yourapp/vendor/handlers.py
  • yourapp/vendor/middleware.py
  • yourapp/vendor/signals.py
  • yourapp/tests/test_polls.py
  • yourapp/tests/test_questions.py
  • yourapp/tests/test_duplicates.py
  • yourapp/tests/test_ratings.py

və ya sizə kömək edəcək başqa bir şey; Lazım olan interfeysləri axtarın və sərhədlər sizə kömək edəcəkdir.

48
12 окт. cavab verildi dnozay Oct 12 2012-10-12 10:16 '12 at 10:16 2012-10-12 10:16

Django MVC-nin bir qədər dəyişdirilmiş görünüşünü istifadə edir. Django'da "nəzarətçi" konsepsiyası yoxdur. Ən yaxın vekil MVC-də görünüş daha Django "şablon" kimi olduğundan, adətən konvertasiya MVCs ilə qarışıqlıq səbəb olan bir "görünüşü" dir.

Django'da "model" yalnız məlumat bazasının abstraktlığı deyil. Bəzi yollarla, Django'nun "görünüşü" ilə MVC nəzarətçisi kimi məsuliyyətlərini bölüşür. Nümunə ilə əlaqəli bütün davranış tərzini ehtiva edir. Bu halda davranışının bir hissəsi olaraq xarici API ilə qarşılıqlı əlaqə qurulması lazımdırsa, bu kod hələ də modelləşdirilir. Əslində, modellər ümumiyyətlə verilənlər bazası ilə qarşılıqlı olmaq üçün tələb olunmur, belə ki, xarici API üçün interaktiv bir qat kimi mövcud olan modelləri ola bilər. Bu, "model" in daha azad bir konsepsiyasıdır.

19
25 сент. 25 Sentyabrda Chris Pratt tərəfindən verilmiş cavab 2012-09-25 17:40 '12 at 17:40 2012-09-25 17:40

Django'nun MVC quruluşu, Chris Pratt'ın digər qurumlarda istifadə edilən klassik MVC modelindən fərqli olduğunu söyləyərək, bunun əsas səbəbi CakePHP kimi digər MVC mühitlərində baş verə biləcək çox ciddi tətbiq strukturundan qaçmaqdır.

Django, MVC aşağıdakı kimi həyata keçirilmişdir:

Layer görünüşü ikiyə bölünür. Baxışlar yalnız HTTP istəklərini idarə etmək üçün istifadə olunmalıdır, çağırılır və onlara cavab verir. Görüşlər ərizənizin qalan hissəsi ilə qarşılıqlı əlaqə (formalar, modellər, istifadəçi müəyyən edilmiş dərslər, sadə hallarda modellərlə birbaşa). Bir interfeys yaratmaq üçün Şablonları istifadə edirik. Şablonlar Django'ya bənzəyir, onlar kontekstə uyğunlaşır və bu kontekst ərizə ilə tətbiqə keçirilir (görünüş tələb olunduqda).

Model katmanı encapsulation, abstraction, validation təmin edir və məlumatlarınızı obyektivləşdirir (onlar bir gün DBMS də olacaq). Bu, böyük modelləri yaratmaq məcburiyyətində deyildir (əslində, çox yaxşı məsləhət sizin modellərinizi müxtəlif fayllarda bölüşdürmək, onları "modellər" adlanan bir qovluğa qoymaq, idxal etdiyiniz yerdə "__init__.py" faylını yaratmaqdır) bütün modelləriniz və nəhayət modellər üçün "app_label" atributunu (model sinfi) istifadə edin. Model, məlumatlarla işləməyinizi soyutlamalı, tətbiqinizi asanlaşdırmalıdır. Lazım gələrsə, modelləriniz üçün "alətlər" kimi xarici siniflər yaratmalısınız. Meta-sınıfınızın modelinin "soyut" niteliğini "True" olaraq ayarlayarak, miraslarınızı da istifadə edə bilərsiniz.

Başqaları haradadır? Bəli, kük veb tətbiqləri, bir qayda olaraq, məlumatlara bir sıra interfeys təqdim edir, bəzi kiçik proqram hallarda, sorguya və ya məlumatları əlavə etmək üçün görünüşlərdən istifadə etmək kifayətdir. Daha ümumi hallarda Formlar və ya ModelForms, həqiqətən "nəzarətçiləri" istifadə edəcək. Bu, ümumi bir problemin praktiki həllindən başqa bir şey deyil və çox sürətli. Bu veb sayt istifadə edir.