.. _custom_methods:
.. index::
single: Model Methoden
single: Django Style Guide
single: Methoden Reihenfolge
single: Property
Eigene Methoden am Model definieren
*************************************
Wenn man komplizierte, wiederkehrende Operationen mit dem ``ORM`` durchführen will, definiert man dafür besser eine Methode am Model. So folgt man dem ``Don’t Repeat Yourself``-Prinzip der Software-Entwicklung und kann dadurch den Code an verschiedenen Stellen nutzen.
Wir können überall in unserem Code Funktionen und Methoden definieren: in den Views, in Utility-Dateien oder im Zwischenlayer, falls er existiert.
Methoden, die direkt das ``Geschäftsmodell`` betreffen, können direkt am Modell definiert werden. Diese Methoden werden als ``Fachfunktionen`` bezeichnet und sind überall
dort nutzbar, wo ein Objekt der Klasse zur Verfügung steht.
.. admonition:: Fat Model or Fat View?
Die Frage ist: wo definiere ich die Methoden am besten? Natürlich kann man
Funktionen auch direkt in der View definieren, aber dann müssten
wir an anderer Stelle die gleiche Logik womöglich nochmal schreiben, wenn wir das gleiche Ergebnis benötigen. Da wir
nicht gegen das ``DRY-Prizip`` verzichten wollen, ist es ratsam, Methoden
zentral am Model oder in eigenen Service-Layern zu definieren.
Related Events
----------------------
Wir wollen die Möglichkeit bieten, dem User später auch Events anzuzeigen, die zu dem angezeigten Event ähnlich sind.
Diese Ähnlichkeit definieren wir selbst anhand von uns gewählter Attribute und
fügen dazu eine Methode in das Event-Model ein. Wir selektieren alle
Event-Objekte und filtern sie nach den Attributen ``min_group`` und
``category``.
Ändern wir nun das Event-Model in :file:`event_manager/events/models.py` ab.
.. code-block:: python
def related_events(self):
"""
Ähnliche Events aus der gleichen Kategorie und der selben
min-group.
"""
related_events = Event.objects.filter(
min_group=self.min_group,
category=self.category
)
return related_events.exclude(pk=self.id)
Events in der Vergangenheit
-----------------------------
Wir wollen jetzt noch eine weitere Methode in unserem Model implementieren, und
zwar diesemal als ``property``. Wir wollen prüfen, ob ein Event in der
Vergangenheit liegt. Das könnte zum Beispiel nützlich sein, wenn wir nur Events
anzeigen wollen, die in der Zukunft liegen. Oder ein asynchroner Task im
Hintergrund laufend prüft, ob ein Event veraltet ist und gelöscht werden sollte.
.. admonition:: Was ist eine property?
Eine ``property`` ist eine pythonische Art, Getter und Setter in der objektorientierten Programmierung zu verwenden.
Python bietet uns einen eingebauten ``@property``-Dekorator, der die Verwendung
von ``getter und setter`` in der objektorientierten Programmierung
vereinfacht. Eine gute Erklärung zu dem Konzept findet sich auf
``realpython.com``: ``_
Ändern wir nun das Event-Model in ``event_manager/events/models.py`` ab und
implementieren die Methode ``has_finished``. Da wir die Methode mit dem
``@property``-Dekorator dekoriert haben, können wir diese später ohne Klammern
aufrufen.
.. code-block:: python
from django.utils import timezone
[..]
@property
def has_finished(self) -> bool:
"""Wenn das Event in der Vergangenheit liegt, return True."""
now = timezone.now()
return self.date <= now
Nun können wir das Ergebnis auf der Shell ausprobieren:
.. code-block:: python
>>> some_event = Event.objects.last()
>>> some_event.has_finished
>>> False
Unsere :file:`event_manager/events/models.py` sieht jetzt so aus:
.. rli:: https://raw.githubusercontent.com/realcaptainsolaris/event_manager_code/main/models/events/4_events.py
:language: python
.. admonition:: Was gehört als Methode in das Model?
Oft stellt sich dem Entwickler die Frage, welche Methoden im Model definiert werden
und welche an anderer Stelle implementiert werden sollen. Als groben Ansatz
kann man sagen, dass jede Methode, die genau eine Instanz des Models, also
eines Objekts, betrifft, im Model definiert wird.
Das trifft auch dann zu, wenn die Rückgabe dieser Funktion eine Menge repräsentiert, wie in der Methode
``related_events``, die als Rückgabewert ein Queryset hat.
Funktionen und Methoden, die nicht ein Objekt konkret betreffen, zum Beispiel Submengen von Objekten, sollte eher in eigene
``Manager`` oder Views ausgelagert werden. Funktionen, die sich im Grunde
auf gar kein Objekt beziehen, können zum Beispiel in einem
``Service-Layer`` abgelegt werden. Zum Beispiel das Anfragen an eine entfernte API oder ähnliches.
Hypothetisch könnte das so aussehen: unter
``event_manager/events/services.py`` tragen wir eine Klasse ein, die eine
entfernte API anspricht und die wir in der Event-App benötigen.
.. code-block:: python
class ServiceAPI:
def __init__(self):
...
In den Views könnte man diese Klasse importieren und nutzen. **Nicht jede
Klasse, die in Django verwendet wird, muss also zwingend ein Model sein!**
.. code-block:: python
from .services import ServiceAPI
def my_view(request):
"""Eine Beispiel-View ohne Aufgabe."""
api = ServiceAPI()
...
Der Django Style Guide
-----------------------
Die Reihenfolge der Methode und Attribute in dem Django-Model ist nicht völlig
willkürlich, sondern orientiert sich an dem Django Styleguide, der unter
``_
einzusehen ist. Hier wird unter anderem auch die Reihenfolge der Methoden und
Attribute in einem Django-Model angegeben.
- an erster Stelle kommen die Attribute (Datenbank-Felder)
- die Angabe der Manager (falls vorhanden)
- die Meta-Klasse (falls benötigt)
- die String-Repräsentation ``__str__()``
- die ``save()`` - Methode (falls benötigt)
- die ``get_absolute_url()`` - Methode
- eigene Methoden
Die Einhaltung der Reihenfolge sollte man sich mittelfristig einprägen und
einhalten, vor allem die Reihenfolge der Methodendefinitionen.
Generell bietet der Styleguide noch weitere interessante Informationen. So ist
zum Beispiel die präferierte Zeilenlänge in Django ``88 Zeichen`` und nicht 79, wie
das zum Beispiel in der `PEP8 `_ vorgeschlagen wird.
Beim Einsatz von Lintern wie ``Flake8`` oder Formattern wie ``black`` oder
``autopep8`` sollte man seine Einstellungen so anpassen, falls man sich an dem
Styleguide orientieren möchte.
Weiterführende Links
-----------------------
* ``_
* ``_
* ``_