File tree Expand file tree Collapse file tree 3 files changed +84
-0
lines changed
styleguide_example/common Expand file tree Collapse file tree 3 files changed +84
-0
lines changed Original file line number Diff line number Diff line change
1
+ from typing import TypeVar
2
+
3
+ from django .db import transaction
4
+
5
+ T = TypeVar ('T' )
6
+
7
+
8
+ @transaction .atomic
9
+ def generic_update (* , instance : T , ** fields_to_update ) -> T :
10
+ """
11
+ Generic update service meant to be reused in local update services
12
+
13
+ For example:
14
+
15
+ def user_update(*, user: User, **fields_to_update) -> User:
16
+ user = generic_update(instance=user, **fields_to_update)
17
+
18
+ // Do other actions with the user here
19
+
20
+ return user
21
+ """
22
+ # If the passed instance is `None` - do nothing.
23
+ if instance is None :
24
+ return instance
25
+
26
+ # If there's nothing to update - do not perform unnecessary actions.
27
+ if not fields_to_update :
28
+ return instance
29
+
30
+ for attr , value in fields_to_update .items ():
31
+ setattr (instance , attr , value )
32
+
33
+ instance .full_clean ()
34
+ # Update only the fields that are meant to be updated.
35
+ # Django docs reference: https://docs.djangoproject.com/en/dev/ref/models/instances/#specifying-which-fields-to-save
36
+ update_fields = list (fields_to_update .keys ())
37
+ instance .save (update_fields = update_fields )
38
+
39
+ return instance
Original file line number Diff line number Diff line change
1
+ import unittest
2
+ from unittest .mock import Mock
3
+
4
+ from styleguide_example .common .services import generic_update
5
+
6
+
7
+ class GenericUpdateTests (unittest .TestCase ):
8
+ def test_generic_update_returns_none_if_passed_instance_is_none (self ):
9
+ instance = None
10
+ updated_instance = generic_update (instance = instance )
11
+
12
+ self .assertIsNone (updated_instance )
13
+
14
+ def test_generic_update_returns_none_if_no_update_fields_are_passed (self ):
15
+ instance = Mock ()
16
+ updated_instance = generic_update (instance = instance )
17
+
18
+ self .assertEqual (instance , updated_instance )
19
+ instance .full_clean .assert_not_called ()
20
+ instance .save .assert_not_called ()
21
+
22
+ def test_generic_update_sets_passed_fields_and_executes_full_clean_and_save (self ):
23
+ instance = Mock (
24
+ field_a = None ,
25
+ field_b = None ,
26
+ field_c = None
27
+ )
28
+ fields_to_update = {
29
+ 'field_a' : 'value_a' ,
30
+ 'field_b' : 'value_b' ,
31
+ }
32
+
33
+ self .assertIsNone (instance .field_a )
34
+ self .assertIsNone (instance .field_b )
35
+ self .assertIsNone (instance .field_c )
36
+
37
+ updated_instance = generic_update (instance = instance , ** fields_to_update )
38
+
39
+ instance .full_clean .assert_called_once ()
40
+ instance .save .assert_called_once_with (update_fields = ['field_a' , 'field_b' ])
41
+
42
+ self .assertEqual (updated_instance .field_a , 'value_a' )
43
+ self .assertEqual (updated_instance .field_b , 'value_b' )
44
+ # `field_c` is not updated because it is not passed as a field to update
45
+ self .assertIsNone (updated_instance .field_c )
You can’t perform that action at this time.
0 commit comments