Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy_utils/models.py: 33%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

30 statements  

1from datetime import datetime, timezone 

2 

3import sqlalchemy as sa 

4 

5 

6class Timestamp: 

7 """Adds `created` and `updated` columns to a derived declarative model. 

8 

9 The `created` column is handled through a default and the `updated` 

10 column is handled through a `before_update` event that propagates 

11 for all derived declarative models. 

12 

13 :: 

14 

15 

16 import sqlalchemy as sa 

17 from sqlalchemy_utils import Timestamp 

18 

19 

20 class SomeModel(Base, Timestamp): 

21 __tablename__ = 'somemodel' 

22 id = sa.Column(sa.Integer, primary_key=True) 

23 """ 

24 

25 created = sa.Column( 

26 sa.DateTime, 

27 default=lambda: datetime.now(tz=timezone.utc).replace(tzinfo=None), 

28 nullable=False, 

29 ) 

30 updated = sa.Column( 

31 sa.DateTime, 

32 default=lambda: datetime.now(tz=timezone.utc).replace(tzinfo=None), 

33 nullable=False, 

34 ) 

35 

36 

37@sa.event.listens_for(Timestamp, 'before_update', propagate=True) 

38def timestamp_before_update(mapper, connection, target): 

39 # When a model with a timestamp is updated; force update the updated 

40 # timestamp. 

41 target.updated = datetime.now(tz=timezone.utc).replace(tzinfo=None) 

42 

43 

44NOT_LOADED_REPR = '<not loaded>' 

45 

46 

47def _generic_repr_method(self, fields): 

48 state = sa.inspect(self) 

49 field_reprs = [] 

50 if not fields: 

51 fields = state.mapper.columns.keys() 

52 for key in fields: 

53 value = state.attrs[key].loaded_value 

54 if key in state.unloaded: 

55 value = NOT_LOADED_REPR 

56 else: 

57 value = repr(value) 

58 field_reprs.append('='.join((key, value))) 

59 

60 return '{}({})'.format(self.__class__.__name__, ', '.join(field_reprs)) 

61 

62 

63def generic_repr(*fields): 

64 """Adds generic ``__repr__()`` method to a declarative SQLAlchemy model. 

65 

66 In case if some fields are not loaded from a database, it doesn't 

67 force their loading and instead repesents them as ``<not loaded>``. 

68 

69 In addition, user can provide field names as arguments to the decorator 

70 to specify what fields should present in the string representation 

71 and in what order. 

72 

73 Example:: 

74 

75 

76 import sqlalchemy as sa 

77 from sqlalchemy_utils import generic_repr 

78 

79 

80 @generic_repr 

81 class MyModel(Base): 

82 __tablename__ = 'mymodel' 

83 id = sa.Column(sa.Integer, primary_key=True) 

84 name = sa.Column(sa.String) 

85 category = sa.Column(sa.String) 

86 

87 session.add(MyModel(name='Foo', category='Bar')) 

88 session.commit() 

89 foo = session.query(MyModel).options(sa.orm.defer('category')).one(s) 

90 

91 assert repr(foo) == 'MyModel(id=1, name='Foo', category=<not loaded>)' 

92 """ 

93 if len(fields) == 1 and callable(fields[0]): 

94 target = fields[0] 

95 target.__repr__ = lambda self: _generic_repr_method(self, fields=None) 

96 return target 

97 else: 

98 

99 def decorator(cls): 

100 cls.__repr__ = lambda self: _generic_repr_method(self, fields=fields) 

101 return cls 

102 

103 return decorator