Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from datetime import timedelta, datetime 

2from time import sleep 

3from typing import Type, List, Tuple, Any, Optional 

4from django.db.models import Model 

5from django.utils.encoding import force_text 

6from django.utils.timezone import now 

7from jutil.format import choices_label 

8 

9 

10def get_object_or_none(cls: Any, **kwargs) -> Any: 

11 """ 

12 Returns model instance or None if not found. 

13 :param cls: Class or queryset 

14 :param kwargs: Filters for get() call 

15 :return: Object or None 

16 """ 

17 try: 

18 qs = cls._default_manager.all() if hasattr(cls, "_default_manager") else cls # pylint: disable=protected-access 

19 return qs.get(**kwargs) 

20 except Exception: 

21 return None 

22 

23 

24def wait_object_or_none(cls: Any, timeout: float = 5.0, sleep_interval: float = 1.0, **kwargs) -> Any: 

25 """ 

26 Returns model instance or None if not found after specified timeout. 

27 Waits timeout before returning if no object available. 

28 Waiting is done by sleeping specified intervals. 

29 :param cls: Class or queryset 

30 :param timeout: Timeout in seconds 

31 :param sleep_interval: Sleep interval in seconds 

32 :param kwargs: Filters for get() call 

33 :return: Object or None 

34 """ 

35 t0: Optional[datetime] = None 

36 t1: Optional[datetime] = None 

37 qs0 = cls._default_manager if hasattr(cls, "_default_manager") else cls # pylint: disable=protected-access 

38 while t0 is None or t0 < t1: # type: ignore 

39 obj = qs0.all().filter(**kwargs).first() 

40 if obj is not None: 

41 return obj 

42 t0 = now() 

43 if t1 is None: 

44 t1 = t0 + timedelta(seconds=timeout) 

45 sleep(sleep_interval) 

46 return qs0.all().filter(**kwargs).first() 

47 

48 

49def get_model_field_label_and_value(instance, field_name: str) -> Tuple[str, str]: 

50 """ 

51 Returns model field label and value (as text). 

52 :param instance: Model instance 

53 :param field_name: Model attribute name 

54 :return: (label, value) tuple 

55 """ 

56 label = field_name 

57 value = str(getattr(instance, field_name)) if hasattr(instance, field_name) else None 

58 for f in instance._meta.fields: 58 ↛ 64line 58 didn't jump to line 64, because the loop on line 58 didn't complete

59 if f.attname == field_name: 

60 label = f.verbose_name 

61 if hasattr(f, "choices") and f.choices: 61 ↛ 62line 61 didn't jump to line 62, because the condition on line 61 was never true

62 value = choices_label(f.choices, value) 

63 break 

64 if value is None: 64 ↛ 65line 64 didn't jump to line 65, because the condition on line 64 was never true

65 value = "" 

66 val = force_text(value) 

67 return label, val 

68 

69 

70def is_model_field_changed(instance, field_name: str) -> bool: 

71 """ 

72 Compares model instance field value to value stored in DB. 

73 If object has not been stored in DB (yet) field is considered unchanged. 

74 :param instance: Model instance 

75 :param field_name: Model attribute name 

76 :return: True if field value has been changed compared to value stored in DB. 

77 """ 

78 assert hasattr(instance, field_name) 

79 if not hasattr(instance, "pk") or instance.pk is None: 79 ↛ 80line 79 didn't jump to line 80, because the condition on line 79 was never true

80 return False 

81 qs = instance.__class__.objects.all() 

82 params = {"pk": instance.pk, field_name: getattr(instance, field_name)} 

83 return not qs.filter(**params).exists() 

84 

85 

86def get_model_keys( 

87 instance, cls: Optional[Type[Model]] = None, exclude_fields: tuple = ("id",), base_class_suffix: str = "_ptr" 

88) -> List[str]: 

89 if cls is None: 89 ↛ 90line 89 didn't jump to line 90, because the condition on line 89 was never true

90 cls = instance.__class__ 

91 return [f.name for f in cls._meta.fields if f.name not in exclude_fields and not f.name.endswith(base_class_suffix)] 

92 

93 

94def clone_model( 

95 instance, 

96 cls: Optional[Type[Model]] = None, 

97 commit: bool = True, 

98 exclude_fields: tuple = ("id",), 

99 base_class_suffix: str = "_ptr", 

100 **kw 

101): 

102 """ 

103 Assigns model fields to new object. Ignores exclude_fields list and 

104 attributes ending with pointer suffix (default '_ptr') 

105 :param instance: Instance to copy 

106 :param cls: Class (opt) 

107 :param commit: Save or not 

108 :param exclude_fields: List of fields to exclude 

109 :param base_class_suffix: End of name for base class pointers, e.g. model Car(Vehicle) has implicit vehicle_ptr 

110 :return: New instance 

111 """ 

112 if cls is None: 112 ↛ 114line 112 didn't jump to line 114, because the condition on line 112 was never false

113 cls = instance.__class__ 

114 keys = get_model_keys(instance, cls=cls, exclude_fields=exclude_fields, base_class_suffix=base_class_suffix) 

115 new_instance = cls() 

116 for k in keys: 

117 setattr(new_instance, k, getattr(instance, k)) 

118 for k, v in kw.items(): 118 ↛ 119line 118 didn't jump to line 119, because the loop on line 118 never started

119 setattr(new_instance, k, v) 

120 if commit: 120 ↛ 122line 120 didn't jump to line 122, because the condition on line 120 was never false

121 new_instance.save() 

122 return new_instance