mirror of
https://github.com/HackSoftware/Django-Styleguide.git
synced 2025-02-06 22:50:53 +03:00
Django styleguide used in HackSoft projects
README.md |
Django Styleguide
Django styleguide used in HackSoft projects.
Expect often updates as we discuss & decide upon different things.
Overview
In Django, business logic should live in:
- Model properties (with some exceptions).
- Model
clean
method for additional validations (with some exceptions). - Services - functions, that take care of code written to the database.
- Selectors - functions, that take care of code taken from the database.
In Django, business logic should not live in:
- APIs and Views.
- Serializers and Forms.
- Form tags.
- Model
save
method.
Model properties vs selectors:
- If the model property spans multiple relations, it should better be a selector.
- If a model property, added to some list API, will cause
N + 1
problem that cannot be easily solved withselect_related
, it should better be a selector.
Services
A service is a simple function that:
- Lives in
your_app/services.py
module - Takes keyword-only arguments
- Is type-annotated (even if you are not using
mypy
at the moment) - Works mostly with models & other services and selectors
- Does business logic - from simple model creation to complex cross-cutting concerns, to calling external services & tasks.
An example service that creates an user:
def create_user(
*,
email: str,
name: str
) -> User:
user = User(email=email)
user.full_clean()
user.save()
create_profile(user=user, name=name)
send_confirmation_email(user=user)
return user
As you can see, this service calls 2 other services - create_profile
and send_confirmation_email
Selectors
A selector is a simple function that:
- Lives in
your_app/selectors.py
module - Takes keyword-only arguments
- Is type-annotated (even if you are not using
mypy
at the moment) - Works mostly with models & other services and selectors
- Does business logic around fetching data from your database
An example selector that list users from the database:
def get_users(*, fetched_by: User) -> Iterable[User]:
user_ids = get_visible_users_for(user=fetched_by)
query = Q(id__in=user_ids)
return User.objects.filter(query)
As you can see, get_visible_users_for
is another selector.