Django iş mantığı ve veri erişim ayırma | Netgez.com
SORU
25 EYLÜL 2012, Salı


Django iş mantığı ve veri erişim ayırma

Django bir proje yazıyorum ve kod €'i dosyası models.py olduÄŸunu görüyorum. Bu kod kafa karıştırıcı ve belli bir zaman sonra, gerçekten ne olup bittiÄŸini anlamıyor.

Canımı sıkan da bu işte:

  1. Benim seviyesi bir model bu çirkin buluyorum, olması gerekiyordu ( bir veritabanı) veri ile sadece bu iş için sorumlu olduğunu da bazı e-posta gönderme, diğer hizmetler, vb apı üzerinde yürür.
  2. Ayrıca, çünkü görünümünde kabul edilemez yerleştirme iş mantığı buluyorum, bu şekilde kontrol edilmesi zor olur. Benim örneğin, uygulama hangi bir yeni oluşturmak için en az üç yolu vardır model User, ama teknik olarak düzgün bir yol oluşturmanız gerekir.
  3. Her zaman bu yöntemleri ne zaman fark yoktur, ve benim modellerinin özelliklerini belirleyici olur ve var olduğunda yan etkileri.

Basit bir örnek. İlk başta şöyle oldu:

class User(db.Models):

    def get_present_name(self):
        return self.name or 'Anonymous'

    def activate(self):
        self.status = 'activated'
        self.save()

Zamanla, bu dönüştü:

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' 

    def activate(self):
        # method was to have a side effect (send message to user)
        self.status = 'activated'
        self.save()
        send_mail('Your account is activated!', '…', [self.email])

Benim istediğim kod varlıkları ayırmak için:

  1. Benim uygulama içerir? veritabanı benim seviye varlıklar:
  2. Başvurum Ne? benim uygulama, iş mantığı seviye varlıklar:

Django uygulanabilecek böyle bir yaklaşım uygulamak için iyi uygulamalar nelerdir?benim sorum: Teşekkür ederim!

CEVAP
12 EKÄ°M 2012, Cuma


Arasındaki fark hakkında soruyorsun gibi görünüyorveri modeliveetki alanı modeli– ikincisi aslında veri depolamak nerede son kullanıcı tarafından algılanan, eski olduğu gibi iş mantığı ve kuruluşlar bulabilirsiniz.

Ayrıca, 3. sorunuza parçası yorumladım: bu modelleri ayrı tutmak, arıza bildirimi için.

Bu çok farklı iki kavram olup, her zaman sabit onları ayrı tutmak. Ancak, bazı ortak kalıpları ve bu amaç için kullanılabilecek araçlar vardır.

Hakkında Etki alanı Modeli

Anlaman gereken ilk şey bir etki alanı modeli çok veri ile ilgili değildir; hakkındaeylemlerivesoru"bu kullanıcı grubuna", "bu kullanıcı devre dışı bırakma", "hangi kullanıcılar şu anda etkin?", gibi ve "bu kullanıcı adı nedir?". Klasik terimlerle ilgilisorgularvekomutlar.

Komutları düşünme

Hadi örnek komutları Aramaya Başla: "bu kullanıcıyı etkinleştir" ve "bu kullanıcı devre dışı bırak". Komutları hakkında güzel bir şey, kolayca küçük-ne zaman-o zaman senaryo ile ifade edilebilir

verildietkin olmayan bir kullanıcıne zamanadmin bu kullanıcı etkinleştirirsonrakullanıcı etkin hale gelirvebir onay e-posta kullanıcı için gönderilirvegiriş sistem günlüğüne eklenir

Böyle bir senaryo bu yararlı görmek nasıl farklı parçaların altyapı etkilenebilecek bir tek komut – bu durumda veritabanı (bir çeşit 'aktif' bayrak), posta sunucusu, sistem günlüğü, vb.

Böyle bir senaryo da gerçekten Test Odaklı Geliştirme ortamı kurma konusunda size yardımcı oluyor.

Ve son olarak, komutları düşünme çok görev odaklı bir uygulama oluşturmanıza yardımcı olur. Kullanıcılarınız bu takdir edecektir :-)

Komutları İfade

Django ifade komutları iki kolay yol sağlar; Her ikisi de geçerli seçenekler var ve bu durum iki yaklaşım karıştırın.

Servis katmanı

servis modülüzaten described by @Hedde. Burada ayrı bir modül tanımlamak ve her bir komut bir fonksiyonu 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 taraftan, her komut için Django bir Form kullanmaktır. Yakından ilgili birden çok yönlerini birleştirir, çünkü bu yaklaşım tercih ederim:

  • komut yürütme (?)
  • komut parametreleri doÄŸrulama (bunu yapabilir mi?)
  • komut sunum (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

Sorgular düşünme

Örnek herhangi bir sorunuz içermiyordu, bir kaç yararlı sorgu yapma cüretinde bulundum. Terimini kullanmayı tercih ederim "soru", ama sorgu klasik terminoloji. İlginç sorgular: "bu kullanıcı adı Nedir?", "Bu kullanıcı giriş yapabilir mi?", "Beni devre dışı kullanıcıların bir listesini göstermek" ve "aktif kullanıcılar coğrafi dağılımı Nedir?"

Bu sorgular cevaplamaya başlamadan önce, her zaman kendinize iki soru sormalısınız: birgörselsadece benim için sorgu şablonları, ve/veya biriş mantığıbenim sorgu komutları yürütmek için bağlı, ve/veya birraporlamasorgu.

Görsel sorgu sadece kullanıcı arayüzü geliştirmek için yapılır. İş mantığı sorguları için cevaplar doğrudan komutlar yürütme etkiler. Raporlama sorgularını sadece analitik amaçlarla ve gevşek zaman kısıtlamaları var. Bu kategoriler birbirini dışlayan değil.

Diğer soru: "cevapları üzerinde tam bir kontrolü var mı?" Kullanıcı adı (bu bağlamda) sorgularken örneğin, harici API güveniyoruz çünkü sonuç üzerinde herhangi bir kontrol yok.

Sorgular, Yapma

Django en temel sorgu Yöneticisi nesnesi kullanımı

User.objects.filter(active=True)

Tabii ki, bu verileri aslında veri modeli temsil ise sadece çalışır. Bu her zaman böyle değildir. Bu durumda, aşağıdaki seçenekleri göz önünde bulundurun.

Özel etiketler ve filtreler

İlk alternatif sadece görsel sorguları için yararlıdır: özel şablon etiketleri ve 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

Eğer sorgu sadece görsel değil, senin için sorgular ekleyebilirsinizservices.pyeğer kullanıyorsanız (), ya da bir takdimqueries.pymodül:

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 modeller

Proxy modeller iş mantığı ve raporlama bağlamında çok yararlıdır. Temelde model geliştirilmiş bir alt kümesi tanımlayın.

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 modeller

Doğası gereği karmaşık, ancak oldukça sık çalıştırılan sorgular için sorgu modeller olasılığı vardır. Sorgu bir model tek bir sorgu için ilgili verileri ayrı bir model olarak depolandığı denormalization şeklidir. Tabii hile birincil model ile senkronize normal dışı modeli tutmaktır. Sorgu modeller yalnızca değişiklikleri tamamen sizin kontrolünüz altında ise kullanılabilir.

models.py

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

İlk seçenek komutlarınızı bu modeller güncellemek için. Bu eğer bu modeller sadece bir ya da iki komut ile değiştirilirse çok yararlı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()

Daha iyi bir seçenek özel sinyaller kullanmak olacaktır. Bu sinyaller elbette komutları tarafından üretilir. Sinyalleri senkronize birden fazla sorgu modeller orijinal modeli ile tutabilir avantajı var. Ayrıca, sinyal işleme arka plan görevleri, Kereviz kullanarak ya da benzer çerçeveler için boşaltılabilir.

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()

Onu temiz tutmak

Bu yaklaşımı kullanarak, gülünç kodunuz temiz kalır eğer belirlemek için kolay olur. Sadece aşağıdaki yönergeleri izleyin:

  • Benim model veritabanı devleti yönetmek daha mı yöntemlerini içeriyor mu? Bir komut ayıklamak gerekir.
  • Benim model veritabanı alanları eÅŸleme özelliklerini içeriyor mu? Bir sorgu ayıklamak gerekir.
  • Benim veritabanı (mail gibi) olmayan modeli referans benim altyapı mu? Bir komut ayıklamak gerekir.

Aynı görüş için görüşlerini sık sık aynı sorundan muzdarip çünkü) gider.

  • Benim görüşüme aktif veritabanı modelleri baÅŸarıyor? Bir komut ayıklamak gerekir.

Bazı Referanslar

Django documentation: proxy models

Django documentation: signals

Architecture: Domain Driven Design

Bunu PaylaÅŸ:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VÄ°DEO

Rastgele Yazarlar

  • DRDAnimation

    DRDAnimation

    28 EYLÃœL 2012
  • Matthew Morrill

    Matthew Morr

    15 EKÄ°M 2011
  • TeachMeComputer

    TeachMeCompu

    31 EKÄ°M 2009