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 typing import Type, List, Tuple, Any, Optional 

2 

3from django.db.models import Model 

4from django.utils.encoding import force_text 

5from jutil.dict import choices_label 

6 

7 

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

9 """ 

10 Returns model instance or None if not found. 

11 :param cls: Class or queryset 

12 :param kwargs: Filters for get() call 

13 :return: Object or None 

14 """ 

15 try: 

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

17 return qs.get(**kwargs) 

18 except Exception: 

19 return None 

20 

21 

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

23 """ 

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

25 :param instance: Model instance 

26 :param field_name: Model attribute name 

27 :return: (label, value) tuple 

28 """ 

29 label = field_name 

30 value = str(getattr(instance, field_name)) 

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

32 if f.attname == field_name: 

33 label = f.verbose_name 

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

35 value = choices_label(f.choices, value) 

36 break 

37 val = force_text(value) 

38 if val is None: 38 ↛ 39line 38 didn't jump to line 39, because the condition on line 38 was never true

39 val = '' 

40 return label, val 

41 

42 

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

44 """ 

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

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

47 :param instance: Model instance 

48 :param field_name: Model attribute name 

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

50 """ 

51 assert hasattr(instance, field_name) 

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

53 return False 

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

55 params = {'pk': instance.pk, field_name: getattr(instance, field_name)} 

56 return qs.filter(**params).first() is None 

57 

58 

59def get_model_keys(instance, cls: Optional[Type[Model]] = None, 

60 exclude_fields: tuple = ('id',), base_class_suffix: str = '_ptr') -> List[str]: 

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

62 cls = instance.__class__ 

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

64 

65 

66def clone_model(instance, cls: Optional[Type[Model]] = None, commit: bool = True, 

67 exclude_fields: tuple = ('id',), base_class_suffix: str = '_ptr', **kw): 

68 """ 

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

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

71 :param instance: Instance to copy 

72 :param cls: Class (opt) 

73 :param commit: Save or not 

74 :param exclude_fields: List of fields to exclude 

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

76 :return: New instance 

77 """ 

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

79 cls = instance.__class__ 

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

81 new_instance = cls() 

82 for k in keys: 

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

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

85 setattr(new_instance, k, v) 

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

87 new_instance.save() 

88 return new_instance