Veri modeli ve etki alanı modeli arasındaki farkı soruyorsunuz gibi görünüyor - ikincisi, son kullanıcı tarafından algılanan iş mantığını ve varlıkları bulabileceğiniz yerdir, birincisi verilerinizi gerçekten depoladığınız yerdir.
Dahası, sorunuzun 3. bölümünü şu şekilde yorumladım: bu modelleri ayrı tutmanın nasıl başarısız olduğunu nasıl fark edersiniz?
Bunlar çok farklı iki kavramdır ve onları ayrı tutmak her zaman zordur. Bununla birlikte, bu amaç için kullanılabilecek bazı yaygın kalıplar ve araçlar vardır.
Alan Modeli Hakkında
Tanımanız gereken ilk şey, etki alanı modelinizin gerçekte verilerle ilgili olmamasıdır; o hakkındadır eylemler ve sorular "bu kullanıcının adının ne?" gibi, "Bu kullanıcıyı devre dışı", "Bu kullanıcının aktif hale" "kullanıcıların şu anda aktif hale gelir?" gibi ve. Klasik anlamda: sorgular ve komutlarla ilgilidir .
Komutlarda Düşünme
Örneğinizdeki komutlara bakarak başlayalım: "bu kullanıcıyı etkinleştir" ve "bu kullanıcıyı devre dışı bırak". Komutlarla ilgili güzel olan şey, o zaman verilen küçük senaryolarla kolayca ifade edilebilmeleridir:
Verilen aktif olmayan kullanıcı yönetici kullanıcının aktive sonra kullanıcı aktif hale gelir ve bir onay e-posta kullanıcıya gönderilir ve bir giriş sistemi günlüğüne eklenir
(vs vs)
Bu tür senaryolar, altyapınızın farklı bölümlerinin tek bir komuttan nasıl etkilenebileceğini görmek için yararlıdır - bu durumda veritabanınız (bir tür 'etkin' bayrak), posta sunucunuz, sistem günlüğünüz, vb.
Bu tür senaryolar, Test Odaklı Geliştirme ortamı kurmanıza da gerçekten yardımcı olur.
Ve son olarak, komutları düşünmek gerçekten görev odaklı bir uygulama oluşturmanıza yardımcı olur. Kullanıcılarınız bunu takdir edecek :-)
Komutları İfade Etme
Django, komutları ifade etmenin iki kolay yolunu sunar; ikisi de geçerli seçeneklerdir ve iki yaklaşımı karıştırmak olağandışı değildir.
Hizmet katmanı
Servis modülü zaten edilmiştir @Hedde tarafından tarif . Burada ayrı bir modül tanımlarsınız ve her komut bir işlev olarak temsil edilir.
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
Formları kullanma
Diğer yol, her komut için bir Django Formu kullanmaktır. Bu yaklaşımı tercih ediyorum, çünkü yakından ilgili birçok yönü birleştiriyor:
- komutun yürütülmesi (ne işe yarar?)
- komut parametrelerinin doğrulanması (bunu yapabilir mi?)
- komutun sunumu (bunu nasıl yapabilirim?)
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üşünme
Örneğin herhangi bir sorgu içermiyordu, bu yüzden birkaç yararlı sorgu oluşturma özgürlüğünü aldım. "Soru" terimini kullanmayı tercih ediyorum, fakat sorgular klasik terminolojidir. İlginç sorgular şunlardır: "Bu kullanıcının adı nedir?", "Bu kullanıcı giriş yapabilir mi?", "Devre dışı bırakılan kullanıcıların listesini göster" ve "Devre dışı bırakılan kullanıcıların coğrafi dağılımı nedir?"
Bu sorguları yanıtlamaya başlamadan önce, kendinize her zaman iki soru sormalısınız: Bu, yalnızca şablonlarım için bir sunum sorgusu ve / veya komutlarımı yürütmeye bağlı bir iş mantığı sorgusu ve / veya bir raporlama sorgusu mu?
Sunum sorguları yalnızca kullanıcı arayüzünü geliştirmek için yapılır. İş mantığı sorgularının yanıtları, komutlarınızın yürütülmesini doğrudan etkiler. Raporlama sorguları yalnızca analitik amaçlıdır ve daha gevşek zaman kısıtlamalarına sahiptir. Bu kategoriler birbirini dışlamaz.
Diğer soru şudur: "Cevaplar üzerinde tam kontrolüm var mı?" Örneğin, kullanıcının adını sorgularken (bu bağlamda), sonuç üzerinde herhangi bir kontrole sahip değiliz, çünkü harici bir API'ye güveniyoruz.
Sorgulama
Django'daki en temel sorgu Manager nesnesinin kullanılmasıdır:
User.objects.filter(active=True)
Tabii ki, bu sadece veriler veri modelinizde gerçekten temsil ediliyorsa işe yarar. Bu her zaman böyle değildir. Bu durumlarda, aşağıdaki seçenekleri göz önünde bulundurabilirsiniz.
Özel etiketler ve filtreler
İlk alternatif yalnızca sunum olan sorgular için kullanışlıdır: özel etiketler ve şablon filtreleri.
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 yöntemleri
Sorgunuz yalnızca sunum amaçlı değilse , services.py (eğer kullanıyorsanız) sorgu ekleyebilir veya bir queries.py modülü tanıtabilirsiniz :
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 modelleri
Proxy modelleri iş mantığı ve raporlama bağlamında çok faydalıdır. Temel olarak modelinizin gelişmiş bir alt kümesini tanımlarsınız. Manager.get_queryset()
Yöntemi geçersiz kılarak bir Yöneticinin temel QuerySet'ini geçersiz kılabilirsiniz .
models.py
class InactiveUserManager(models.Manager):
def get_queryset(self):
query_set = super(InactiveUserManager, self).get_queryset()
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 modelleri
Doğal olarak karmaşık, ancak oldukça sık yürütülen sorgular için, sorgu modelleri olasılığı vardır. Bir sorgu modeli, tek bir sorgu için ilgili verilerin ayrı bir modelde saklandığı bir tür denormalizasyon şeklidir. İşin püf noktası, normalize edilmemiş modeli birincil modelle senkronize tutmaktır. Sorgu modelleri yalnızca değişiklikler tamamen sizin kontrolünüz altındaysa kullanılabilir.
models.py
class InactiveUserDistribution(models.Model):
country = CharField(max_length=200)
inactive_user_count = IntegerField(default=0)
İlk seçenek bu modelleri komutlarınızda güncellemektir. Bu modeller yalnızca bir veya iki komutla değiştirilirse çok kullanışlı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()
Özel sinyalleri kullanmak daha iyi bir seçenek olabilir. Bu sinyaller elbette komutlarınız tarafından yayılır. Sinyaller, birden çok sorgu modelini orijinal modelinizle senkronize halde tutabilmeniz avantajına sahiptir. Ayrıca, sinyal işleme Kereviz veya benzer çerçeveler kullanılarak arka plan görevlerine yüklenebilir.
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()
Temiz tutulması
Bu yaklaşımı kullanırken, kodunuzun temiz kalıp kalmadığını belirlemek gülünç kolaylaşır. Sadece şu yönergeleri izleyin:
- Modelim veritabanı durumunu yönetmekten daha fazlasını yapan yöntemler içeriyor mu? Bir komut çıkarmalısınız.
- Modelim veritabanı alanlarına eşlenmeyen özellikler içeriyor mu? Bir sorgu çıkarmalısınız.
- Modelim veritabanım olmayan altyapıyı (posta gibi) referans alıyor mu? Bir komut çıkarmalısınız.
Aynı şey görüşler için de geçerlidir (çünkü görünümler genellikle aynı problemden muzdariptir).
- Benim görüşüm veritabanı modellerini aktif olarak yönetiyor mu? Bir komut çıkarmalısınız.
Bazı Referanslarımız
Django belgeleri: proxy modelleri
Django belgeleri: sinyaller
Mimari: Etki Alanına Dayalı Tasarım