Rozdzielenie logiki biznesowej i dostępu do danych w django

Piszę projekt w Django i widzę, że 80% kodu jest w pliku models.py. Ten kod jest mylący i po pewnym czasie przestaję rozumieć, co się naprawdę dzieje.

Oto co mnie martwi:

  1. uważam za brzydkie, że mój poziom modelu (który miał być odpowiedzialny tylko za pracę z danymi z bazy danych) jest również wysyłanie e-maili, chodzenie po API do innych usług itp.
  2. również uważam za niedopuszczalne umieszczenie logiki biznesowej w widok, bo w ten sposób staje się trudne do kontrolowania. Na przykład, w moim aplikacja istnieją co najmniej trzy sposoby tworzenia nowych instancje User, ale technicznie powinny tworzyć je jednolicie.
  3. nie zawsze zauważam, kiedy metody i własności moich modeli stają się niedeterministyczne, a kiedy rozwijają skutki uboczne.

Oto prosty przykład. Na początku model User wyglądał tak:

class User(db.Models):

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

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

Z czasem przekształciła się w to:

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

Chcę oddzielić encje w kodzie:

  1. podmioty mojej bazy danych, Poziom bazy danych: co zawiera moja aplikacja?
  2. podmioty mojej aplikacji, poziom logiki biznesowej: co może zrobić moja aplikacja?

Jakie są dobre praktyki, aby wdrożyć takie podejście, które można zastosować w Django?

Author: Adam, 2012-09-25

8 answers

Wygląda na to, że pytasz o różnicę między modelem danych a modelem domeny -ten ostatni to miejsce, w którym możesz znaleźć logikę biznesową i podmioty postrzegane przez użytkownika końcowego, pierwszy to miejsce, w którym faktycznie przechowujesz swoje dane.

Ponadto, zinterpretowałem trzecią część twojego pytania jako: Jak zauważyć, że nie można trzymać tych modeli osobno.

Są to dwa bardzo różne pojęcia i zawsze trudno je utrzymać osobno. Istnieją jednak pewne wspólne wzorce i narzędzia, które można wykorzystać w tym celu.

O modelu domeny

Pierwszą rzeczą, którą musisz rozpoznać, jest to, że twój model domeny tak naprawdę nie dotyczy danych; chodzi o działania i pytania, takie jak "Aktywuj tego użytkownika", "Dezaktywuj tego użytkownika", "którzy użytkownicy są obecnie aktywowani?", oraz " jak nazywa się ten użytkownik?". W klasycznym ujęciu: chodzi o zapytania i polecenia.

Myślenie w poleceniach

Zacznijmy od poleceń w twoim przykładzie: "Aktywuj tego użytkownika" i "dezaktywuj tego użytkownika". Ładną rzeczą w poleceniach jest to, że można je łatwo wyrazić za pomocą małych scenariuszy given-when-then: {]}

Podany nieaktywny użytkownik
kiedy admin aktywuje tego użytkownika
następnie użytkownik staje się aktywny
i E-mail z potwierdzeniem jest wysyłany do użytkownika
i wpis jest dodawany do dziennika systemowego
(itd. itd.)

Takie scenariusze są przydatne, aby zobaczyć, jak różne części Twojej infrastruktury mogą być dotknięte pojedynczym poleceniem – w tym przypadku twoja baza danych( jakaś "aktywna" flaga), Twój serwer pocztowy, Twój dziennik systemowy itp.

Taki scenariusz pomaga również w tworzeniu środowiska programistycznego opartego na testach.

I wreszcie, myślenie poleceniami naprawdę pomaga stworzyć aplikacja zorientowana na zadania. Twoi użytkownicy to docenią: -)

Wyrażanie Poleceń

Django dostarcza dwa proste sposoby wyrażania poleceń; oba są poprawnymi opcjami i nie jest niczym niezwykłym mieszanie tych dwóch podejść.

Warstwa usług

Moduł serwisowy został już opisany przez @Hedde. Tutaj definiujesz osobny moduł, a każde polecenie jest reprezentowane jako funkcja.

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

Używanie formularzy

Innym sposobem jest użycie formy Django dla każdego polecenia. Preferuję takie podejście, ponieważ łączy w sobie wiele ściśle powiązanych ze sobą aspektów:

  • wykonanie polecenia (co robi?)
  • Walidacja parametrów poleceń (może to zrobić?)
  • prezentacja polecenia (jak to zrobić?)

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

Myślenie w Queries

Twój przykład nie zawierał żadnych zapytań, więc pozwoliłem sobie stworzyć kilka przydatnych zapytań. Wolę używać terminu "pytanie" , ale zapytania to klasyczna terminologia. Ciekawe pytania to: "jak nazywa się ten użytkownik?", "Czy ten użytkownik może się zalogować?", "Pokaż mi listę dezaktywowanych użytkowników" i " jaki jest geograficzny rozkład dezaktywowanych użytkowników?"

Zanim zaczniesz odpowiadać na te pytania, zawsze powinieneś zadać sobie dwa pytania: czy to zapytanie prezentacyjne tylko dla moich szablonów i/lub zapytanie logiki biznesowej związane z wykonywaniem moich poleceń i/lub zapytanie raportujące .

Zapytania prezentacyjne są wykonywane jedynie w celu ulepszenia interfejsu użytkownika. Odpowiedzi na zapytania logiki biznesowej mają bezpośredni wpływ na wykonywanie poleceń. Zapytania raportowe służą jedynie do celów analitycznych i mają luźniejsze ograniczenia czasowe. Kategorie te nie wykluczają się wzajemnie.

The inne pytanie brzmi: "Czy mam pełną kontrolę nad odpowiedziami?"Na przykład podczas zapytań o nazwę użytkownika (w tym kontekście) nie mamy żadnej kontroli nad wynikiem, ponieważ polegamy na zewnętrznym API.

Tworzenie Zapytań

Najbardziej podstawowym zapytaniem w Django jest użycie obiektu Manager:

User.objects.filter(active=True)

Oczywiście działa to tylko wtedy, gdy dane są rzeczywiście reprezentowane w modelu danych. Nie zawsze tak jest. W takich przypadkach można rozważyć opcje poniżej.

Niestandardowe znaczniki i filtry

Pierwsza alternatywa jest przydatna dla zapytań, które są tylko prezentacyjne: niestandardowe tagi i filtry szablonów.

Szablon.html

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

Template_tags.py

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

Metody zapytań

Jeśli Twoje zapytanie nie jest tylko prezentacyjne, możesz dodać zapytania do swojego services.py (jeśli tego używasz), lub wprowadzić queries.py moduł:

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 

Modele Proxy

Modele Proxy są bardzo przydatne w kontekście logiki biznesowej i raportowania. Zasadniczo definiujesz ulepszony podzbiór swojego modelu. Możesz nadpisać podstawowy zestaw zapytań Menedżera, nadpisując Manager.get_queryset() metoda.

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

Modele zapytań

Dla zapytań, które są z natury złożone, ale są wykonywane dość często, istnieje możliwość tworzenia modeli zapytań. Model zapytań jest formą denormalizacji, w której odpowiednie dane dla pojedynczego zapytania są przechowywane w oddzielnym modelu. Sztuką jest oczywiście utrzymanie modelu denormalizowanego w synchronizacji z modelem podstawowym. Modele zapytań mogą być używane tylko wtedy, gdy zmiany są całkowicie pod twoją kontrolą.

Models.py

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

Pierwszą opcją jest aktualizacja tych modeli w poleceniach. Jest to bardzo przydatne, jeśli te modele są zmieniane tylko przez jedno lub dwa polecenia.

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

Lepszym rozwiązaniem byłoby użycie niestandardowych sygnałów. Sygnały te są oczywiście emitowane przez twoje polecenia. Sygnały mają tę zaletę, że można synchronizować wiele modeli zapytań z oryginalnym modelem. Ponadto przetwarzanie sygnałów może być przenoszone do zadań w tle, przy użyciu Celery lub podobnych frameworków.

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

Utrzymanie czystości

Używając tego podejścia, niezwykle łatwo jest określić, czy Twój kod pozostaje czysty. Po prostu postępuj zgodnie z tymi wytycznymi:

  • czy mój model zawiera metody, które robią więcej niż zarządzanie stanem bazy danych? Powinieneś wyodrębnić polecenie.
  • czy mój model zawiera właściwości, które nie mapują do bazy danych pola? Powinieneś wyodrębnić zapytanie.
  • czy mój model odnosi się do infrastruktury, która nie jest moją bazą danych(taką jak poczta)? Powinieneś wyodrębnić polecenie.

To samo dotyczy widoków (ponieważ widoki często cierpią na ten sam problem).

  • czy mój widok aktywnie zarządza modelami baz danych? Powinieneś wyodrębnić polecenie.

Niektóre Odniesienia

Dokumentacja Django: modele proxy

Dokumentacja Django: sygnały

Architektura: Domain Driven Design

 462
Author: publysher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-28 14:40:31

Zazwyczaj implementuję warstwę usług pomiędzy widokami a modelami. To działa jak API twojego projektu i daje dobry widok helikoptera na to, co się dzieje. Odziedziczyłem tę praktykę po moim koledze, który często używa tej techniki warstw w projektach Java (JSF), np.:

Models.py

class Book:
   author = models.ForeignKey(User)
   title = models.CharField(max_length=125)

   class Meta:
       app_label = "library"

Services.py

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, e.g. implement a _build and _apply filters function """
    queryset = get_books()

Pamiętaj, że zazwyczaj biorę modele, widoki i usługi do poziomu modułu i oddzielać nawet dalej w zależności od wielkości projektu

 106
Author: Hedde van der Heide,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-25 09:25:37

Po pierwsze, nie powtarzaj się.

Następnie, proszę uważać, aby nie overengineer, czasami jest to tylko strata czasu, i sprawia, że ktoś traci koncentrację na tym, co ważne. Od czasu do czasu przeglądaj zen Pythona.

Zobacz aktywne projekty

  • więcej ludzi = więcej potrzeba porządkowania]}
  • repozytorium django mają prostą strukturę.
  • The pip repozytorium mają prostą strukturę katalogów.
  • Repozytorium tkaninjest również dobrym miejscem do obejrzenia.

    • możesz umieścić wszystkie modele pod yourapp/models/logicalgroup.py
  • np. User, Group i pokrewne modele mogą przejść pod yourapp/models/users.py
  • np. Poll, Question, Answer ... could go under yourapp/models/polls.py
  • załaduj to, czego potrzebujesz w __all__ wewnątrz yourapp/models/__init__.py

Więcej o MVC

  • model jest Twoje dane
    • to obejmuje twoje rzeczywiste dane
    • Pliki cookie to niewielkie pliki tekstowe, które przechowywane są w urządzeniu końcowym użytkownika.]}
  • użytkownik wchodzi w interakcję z kontrolerem, aby manipulować modelem
      To może być API lub widok, który zapisuje/aktualizuje dane]}
    • to może być dostrojone z request.GET / request.POST ...etc
    • pomyśl też stronicowanielub filtrowanie.
  • dane aktualizują widok
    • szablony Pobierz Dane i odpowiednio sformatuj je
    • API nawet bez szablonów są częścią widoku; np. tastypie lub piston
    • powinno to również uwzględniać oprogramowanie pośredniczące.

Skorzystaj z middleware / templatetags

  • jeśli potrzebujesz trochę pracy dla każdego żądania, oprogramowanie pośredniczące jest jednym ze sposobów.
    • np. dodawanie znaczników czasu
    • np. aktualizowanie metryk o odsłonach strony
    • np. wypełnianie pamięci podręcznej
  • jeśli masz fragmenty kodu, które zawsze powracają do formatowania obiektów, templatetags są dobre.
    • np. active tab / url breadcrumbs

Skorzystaj z model managerów

  • tworzenie User może przejść w UserManager(models.Manager).
  • krwawe szczegóły dotyczące instancji powinny znaleźć się na models.Model.
  • / Align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center /
  • możesz utworzyć Po kolei, więc możesz myśleć, że powinien on żyć na samym modelu, ale podczas tworzenia obiektu prawdopodobnie nie masz wszystkich szczegółów:]}

Przykład:

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

W miarę możliwości korzystać z formularzy

Wiele kodu boilerplate można wyeliminować, jeśli masz formularze, które odwzorowują model. Na ModelForm documentation jest całkiem dobry. Oddzielenie kodu formularzy od kodu modelu może być dobre, jeśli masz dużo dostosowywania (lub czasami unikasz cyklicznego błędy importu dla bardziej zaawansowanych zastosowań).

Użyj poleceń zarządzania jeśli to możliwe

  • np. yourapp/management/commands/createsuperuser.py
  • np. yourapp/management/commands/activateinbulk.py

Jeśli masz logikę biznesową, możesz ją oddzielić

  • django.contrib.auth używa backendów , tak jak db ma backend...itd.
  • dodaj setting do swojej logiki biznesowej (np. AUTHENTICATION_BACKENDS)
  • you could use django.contrib.auth.backends.RemoteUserBackend
  • you could use yourapp.backends.remote_api.RemoteUserBackend
  • you could użycie yourapp.backends.memcached.RemoteUserBackend
  • deleguj trudną logikę biznesową do backendu
  • upewnij się, że ustawiasz oczekiwanie bezpośrednio na wejściu/wyjściu.
  • zmiana logiki biznesowej jest tak prosta jak zmiana ustawienia:)

Przykład backendu:

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' 

Może stać się:

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

Więcej o wzorcach projektowych

Więcej o granicach interfejsu

  • czy kod, którego chcesz użyć, jest częścią modeli? -> yourapp.models
  • czy kod jest częścią logiki biznesowej? -> yourapp.vendor
  • czy kod jest częścią generic tools / libs? -> yourapp.libs
  • czy kod jest częścią business logic libs? - >yourapp.libs.vendor lub yourapp.vendor.libs
  • Oto dobry: czy możesz przetestować swój kod niezależnie?
    • tak, dobrze:)
    • Nie, możesz mieć problem z interfejsem]}
    • gdy istnieje wyraźne oddzielenie, unittest powinien być powiewem z użyciem szyderstwa
  • Czy rozdzielenie jest logiczne?
    • tak, dobrze:)
    • Nie, możesz mieć problemy z oddzielnym testowaniem tych pojęć logicznych.
  • Czy uważasz, że będziesz musiał refaktorować, gdy otrzymasz 10x więcej kodu?
    • yes, no good, no bueno, refactor może być dużo pracy
    • Nie, to po prostu super!

Krótko mówiąc, możesz mieć

  • 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

Lub Wszystko inne, co ci pomoże; znalezienie interfejsów, których potrzebujesz i granic pomoże Ci.

 51
Author: dnozay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:59

Django wykorzystuje nieco zmodyfikowany rodzaj MVC. W Django nie ma pojęcia o "kontrolerze". Najbliższym proxy jest "widok", który ma tendencję do mylenia z KONWERTAMI MVC, ponieważ w MVC widok jest bardziej podobny do"szablonu" Django.

W Django "model" to nie tylko abstrakcja bazy danych. Pod pewnymi względami dzieli obowiązki z "widokiem" Django jako kontrolerem MVC. Przechowuje całość zachowania związanego z instancją. Jeśli ta instancja musi współdziałać z zewnętrzne API jako część jego zachowania, to nadal jest to kod modelu. W rzeczywistości modele nie są w ogóle wymagane do interakcji z bazą danych, więc można sobie wyobrazić modele, które w całości istnieją jako warstwa interaktywna do zewnętrznego API. Jest to znacznie bardziej swobodna koncepcja "modelu".

 21
Author: Chris Pratt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-25 14:40:12

W Django struktura MVC jest, jak powiedział Chris Pratt, różna od klasycznego modelu MVC używanego w innych frameworkach, myślę, że głównym powodem tego jest unikanie zbyt surowej struktury aplikacji, jak ma to miejsce w innych frameworkach MVC, takich jak CakePHP.

W Django MVC został zaimplementowany w następujący sposób:

Warstwa widoku jest podzielona na dwie części. Widoki powinny być używane tylko do zarządzania żądaniami HTTP, są one wywoływane i reagowane na nie. Widoki komunikują się z resztą twojego aplikacji (formularze, modelformy, klasy niestandardowe, w prostych przypadkach bezpośrednio z modelami). Do stworzenia interfejsu używamy szablonów. Szablony są string-jak w Django, mapuje do nich kontekst, a ten kontekst został przekazany do widoku przez aplikację(gdy view pyta).

Warstwa modelu daje enkapsulację, abstrakcję, walidację, inteligencję i sprawia, że dane są zorientowane obiektowo(mówią, że kiedyś DBMS też). To nie znaczy, że należy zrobić ogromny models.py pliki (w fakt bardzo dobrą radą jest dzielenie modeli w różne pliki, umieszczanie ich w folderze o nazwie 'models', tworzenie '__init__.py 'plik do tego folderu, do którego importujesz wszystkie modele i na koniec używasz atrybutu 'app_label' modeli.Klasa modelu). Model powinien odciążyć Cię od pracy z danymi, dzięki czemu Twoja aplikacja będzie prostsza. Powinieneś również, jeśli jest to wymagane, utworzyć zewnętrzne klasy, takie jak" narzędzia " dla swoich Modeli.Można również użyć heritage w modelach, ustawiając atrybut 'abstract' Meta Klasa Twojego modelu do "True".

Gdzie jest reszta? Cóż, małe aplikacje internetowe na ogół są rodzajem interfejsu do danych, w niektórych małych przypadkach wystarczy użycie widoków do zapytań lub wstawiania danych. Bardziej powszechne przypadki będą używać Forms lub ModelForms, które w rzeczywistości są "kontrolerami". Nie jest to nic innego jak praktyczne rozwiązanie wspólnego problemu i bardzo szybkie. Do tego służy strona internetowa.

Jeśli formularze nie są dla Ciebie enogh, to powinieneś stworzyć swój własny bardzo dobrym tego przykładem jest aplikacja admin: można czytać kod modelu, to faktycznie działa jako kontroler. Nie ma standardowej struktury, proponuję sprawdzić istniejące aplikacje Django, to zależy od każdego przypadku. To jest to, co zamierzali deweloperzy Django, możesz dodać klasę parsera xml, klasę złącza API, dodać selery do wykonywania zadań, skręcić dla aplikacji opartej na reaktorze, używać tylko ORM, tworzyć serwis internetowy, modyfikować aplikację administratora i wiele innych... Twoim zadaniem jest tworzenie dobrej jakości kodu, respektowanie filozofii MVC, czy nie, tworzenie modułów i tworzenie własnych warstw abstrakcji. Jest bardzo elastyczny.

Moja rada: przeczytaj jak najwięcej kodu, wokół jest wiele aplikacji django, ale nie bierz ich tak poważnie. Każdy przypadek jest inny, wzory i teoria Pomagają, ale nie zawsze, jest to nieprecyzyjne, django po prostu dostarcza ci dobre narzędzia, których możesz użyć do ożywienia niektórych bólów (jak interfejs administracyjny, web form validation, i18n, observer pattern implementation, wszystkie wyżej wymienione i inne), ale dobre projekty pochodzą od doświadczonych projektantów.

PS.: Użyj klasy 'User' z aplikacji auth (ze standardowego django), możesz zrobić np. profile użytkowników, lub przynajmniej przeczytać jego kod, będzie to przydatne w Twoim przypadku.

 5
Author: Nate Gentile,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-17 07:14:59

W większości się Zgadzam z wybraną odpowiedzią ( https://stackoverflow.com/a/12857584/871392 ), ale chcesz dodać opcję w sekcji tworzenie zapytań.

Można zdefiniować klasy QuerySet dla modeli dla make filter queries i son on. Następnie możesz zastępować tę klasę queryset dla menedżera modelu, tak jak robią to Klasy build-in Manager i queryset.

Chociaż, jeśli trzeba było odpytywać kilka modeli danych, aby uzyskać jeden model domeny, wydaje mi się bardziej rozsądne umieszczenie tego w osobnym moduł jak sugerowano wcześniej.

 0
Author: l0ki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:21

Stare pytanie, ale i tak chciałbym zaproponować swoje rozwiązanie. Opiera się na akceptacji, że obiekty modelu również wymagają dodatkowej funkcjonalności, podczas gdy trudno jest umieścić je w models.py . ciężka logika biznesowa może być napisana osobno w zależności od osobistego gustu, ale przynajmniej podoba mi się ten model, aby robił wszystko, co z nim związane. To rozwiązanie wspiera również tych, którzy lubią mieć całą logikę umieszczoną w samych modelach.

Jako taki, wymyśliłem a hack , który pozwala mi oddzielić logikę od definicji modelu i nadal uzyskać wszystkie podpowiedzi z mojego IDE.

Zalety powinny być oczywiste, ale to wymienia kilka, które zaobserwowałem:

  • definicje DB pozostają tylko tym - brak logicznych "śmieci" dołączonych
  • logika związana z modelem jest uporządkowana w jednym miejscu
  • Wszystkie usługi (formularze, reszta, widoki) mają jeden punkt dostępu do logiki
  • Najlepsze ze wszystkich: nie musiałem przepisywać żadnych kod kiedy zdałem sobie sprawę, że mój models.py stał się zbyt zaśmiecony i musiał oddzielić logikę. Rozdzielenie jest płynne i iteracyjne: mógłbym wykonać funkcję na raz lub całą klasę lub całą models.py.

Używałem tego z Pythonem 3.4 i większym oraz Django 1.8 i większym.

App/models.py

....
from app.logic.user import UserLogic

class User(models.Model, UserLogic):
    field1 = models.AnyField(....)
    ... field definitions ...

App/logic/user.py

if False:
    # This allows the IDE to know about the User model and its member fields
    from main.models import User

class UserLogic(object):
    def logic_function(self: 'User'):
        ... code with hinting working normally ...

Jedyną rzeczą, której nie mogę rozgryźć, jest to, jak sprawić, by mój IDE (PyCharm w tym przypadku) rozpoznał, że UserLogic jest w rzeczywistości modelem użytkownika. Ale ponieważ jest to oczywiście hack, jestem całkiem szczęśliwy, aby zaakceptować trochę uciążliwości zawsze podając typ dla self parametr.

 0
Author: velis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-27 08:00:23

Django zostało zaprojektowane do łatwego użycia do dostarczania stron internetowych. Jeśli nie jesteś komfortowy z tym być może powinieneś użyć innego rozwiązania.

Piszę root lub wspólne operacje na modelu (aby mieć ten sam interfejs), a pozostałe na kontrolerze modelu. Jeśli potrzebuję operacji z innego modelu, importuję jego kontroler.

Takie podejście wystarcza mi i złożoności moich aplikacji.

Odpowiedź Hedde jest przykładem, który pokazuje elastyczność samego django i Pythona.

Bardzo ciekawe pytanie w każdym razie!

 -2
Author: pvilas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-25 09:40:37