Move the update handling section inside the cookbook

This commit is contained in:
wencakisa 2022-01-21 16:16:38 +02:00
parent cbd995e730
commit a57cc40c80

View File

@ -424,34 +424,6 @@ As you can see, this service calls 2 other services - `profile_create` and `conf
In this example, everything related to the user creation is in one place and can be traced.
### Handling updates
As for updating, we have a generic update service that we use inside of the actual update services. Here's what a sample `user_update` service would look like:
```python
def user_update(*, user: User, data) -> User:
non_side_effect_fields = ['first_name', 'last_name']
user, has_updated = model_update(
instance=user,
fields=non_side_effect_fields,
data=data
)
# Side-effect fields update here (e.g. username is generated based on first & last name)
# ... some additional tasks with the user ...
return user
```
* We're calling the generic `model_update` service for the fields that have no side-effects related to them (meaning that they're just set to the value that we provide).
* This pattern allows us to extract the repetitive field setting in a generic service and perform only the specific tasks inside of the update service (side-effects).
The generic `model_update` implementation is in the [`Cookbook` section](#generic-update-service).
The full implementation of this approach can be seen [in our example project](https://github.com/HackSoftware/Django-Styleguide-Example/blob/master/styleguide_example/users/services.py).
### Naming convention
Naming convention depends on your taste. It pays off to have something consistent throughout a project.
@ -2303,6 +2275,64 @@ Celery is a complex topic, so it's a good idea to invest time reading the docume
We constantly do that & find new things or find better approaches to our problems.
## Cookbook
Some of the implementations of generic reusable pieces of code are stored here.
### Handling updates with a service
As for updating, we have a generic update service that we use inside of the actual update services. Here's what a sample `user_update` service would look like:
```python
def user_update(*, user: User, data) -> User:
non_side_effect_fields = ['first_name', 'last_name']
user, has_updated = model_update(
instance=user,
fields=non_side_effect_fields,
data=data
)
# Side-effect fields update here (e.g. username is generated based on first & last name)
# ... some additional tasks with the user ...
return user
```
* We're calling the generic `model_update` service for the fields that have no side-effects related to them (meaning that they're just set to the value that we provide).
* This pattern allows us to extract the repetitive field setting in a generic service and perform only the specific tasks inside of the update service (side-effects).
The generic `model_update` implementation looks like this:
```python
def model_update(
*,
instance: DjangoModelType,
fields: List[str],
data: Dict[str, Any]
) -> Tuple[DjangoModelType, bool]:
has_updated = False
for field in fields:
if field not in data:
continue
if getattr(instance, field) != data[field]:
has_updated = True
setattr(instance, field, data[field])
if has_updated:
instance.full_clean()
instance.save(update_fields=fields)
return instance, has_updated
```
The full implementations of these services can be found in our example project:
* [`model_update`](https://github.com/HackSoftware/Django-Styleguide-Example/blob/master/styleguide_example/common/services.py)
* [`user_update`](https://github.com/HackSoftware/Django-Styleguide-Example/blob/master/styleguide_example/users/services.py)
## DX (Developer Experience)
A section with various things that can make your Django developer life better.