Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/core/checks/model_checks.py: 43%

92 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1import inspect 

2import types 

3from collections import defaultdict 

4from itertools import chain 

5 

6from django.apps import apps 

7from django.conf import settings 

8from django.core.checks import Error, Tags, Warning, register 

9 

10 

11@register(Tags.models) 

12def check_all_models(app_configs=None, **kwargs): 

13 db_table_models = defaultdict(list) 

14 indexes = defaultdict(list) 

15 constraints = defaultdict(list) 

16 errors = [] 

17 if app_configs is None: 17 ↛ 20line 17 didn't jump to line 20, because the condition on line 17 was never false

18 models = apps.get_models() 

19 else: 

20 models = chain.from_iterable( 

21 app_config.get_models() for app_config in app_configs 

22 ) 

23 for model in models: 

24 if model._meta.managed and not model._meta.proxy: 

25 db_table_models[model._meta.db_table].append(model._meta.label) 

26 if not inspect.ismethod(model.check): 26 ↛ 27line 26 didn't jump to line 27, because the condition on line 26 was never true

27 errors.append( 

28 Error( 

29 "The '%s.check()' class method is currently overridden by %r." 

30 % (model.__name__, model.check), 

31 obj=model, 

32 id="models.E020", 

33 ) 

34 ) 

35 else: 

36 errors.extend(model.check(**kwargs)) 

37 for model_index in model._meta.indexes: 

38 indexes[model_index.name].append(model._meta.label) 

39 for model_constraint in model._meta.constraints: 39 ↛ 40line 39 didn't jump to line 40, because the loop on line 39 never started

40 constraints[model_constraint.name].append(model._meta.label) 

41 if settings.DATABASE_ROUTERS: 41 ↛ 42line 41 didn't jump to line 42, because the condition on line 41 was never true

42 error_class, error_id = Warning, "models.W035" 

43 error_hint = ( 

44 "You have configured settings.DATABASE_ROUTERS. Verify that %s " 

45 "are correctly routed to separate databases." 

46 ) 

47 else: 

48 error_class, error_id = Error, "models.E028" 

49 error_hint = None 

50 for db_table, model_labels in db_table_models.items(): 

51 if len(model_labels) != 1: 51 ↛ 52line 51 didn't jump to line 52, because the condition on line 51 was never true

52 model_labels_str = ", ".join(model_labels) 

53 errors.append( 

54 error_class( 

55 "db_table '%s' is used by multiple models: %s." 

56 % (db_table, model_labels_str), 

57 obj=db_table, 

58 hint=(error_hint % model_labels_str) if error_hint else None, 

59 id=error_id, 

60 ) 

61 ) 

62 for index_name, model_labels in indexes.items(): 

63 if len(model_labels) > 1: 63 ↛ 64line 63 didn't jump to line 64, because the condition on line 63 was never true

64 model_labels = set(model_labels) 

65 errors.append( 

66 Error( 

67 "index name '%s' is not unique %s %s." 

68 % ( 

69 index_name, 

70 "for model" if len(model_labels) == 1 else "among models:", 

71 ", ".join(sorted(model_labels)), 

72 ), 

73 id="models.E029" if len(model_labels) == 1 else "models.E030", 

74 ), 

75 ) 

76 for constraint_name, model_labels in constraints.items(): 76 ↛ 77line 76 didn't jump to line 77, because the loop on line 76 never started

77 if len(model_labels) > 1: 

78 model_labels = set(model_labels) 

79 errors.append( 

80 Error( 

81 "constraint name '%s' is not unique %s %s." 

82 % ( 

83 constraint_name, 

84 "for model" if len(model_labels) == 1 else "among models:", 

85 ", ".join(sorted(model_labels)), 

86 ), 

87 id="models.E031" if len(model_labels) == 1 else "models.E032", 

88 ), 

89 ) 

90 return errors 

91 

92 

93def _check_lazy_references(apps, ignore=None): 

94 """ 

95 Ensure all lazy (i.e. string) model references have been resolved. 

96 

97 Lazy references are used in various places throughout Django, primarily in 

98 related fields and model signals. Identify those common cases and provide 

99 more helpful error messages for them. 

100 

101 The ignore parameter is used by StateApps to exclude swappable models from 

102 this check. 

103 """ 

104 pending_models = set(apps._pending_operations) - (ignore or set()) 

105 

106 # Short circuit if there aren't any errors. 

107 if not pending_models: 107 ↛ 110line 107 didn't jump to line 110, because the condition on line 107 was never false

108 return [] 

109 

110 from django.db.models import signals 

111 

112 model_signals = { 

113 signal: name 

114 for name, signal in vars(signals).items() 

115 if isinstance(signal, signals.ModelSignal) 

116 } 

117 

118 def extract_operation(obj): 

119 """ 

120 Take a callable found in Apps._pending_operations and identify the 

121 original callable passed to Apps.lazy_model_operation(). If that 

122 callable was a partial, return the inner, non-partial function and 

123 any arguments and keyword arguments that were supplied with it. 

124 

125 obj is a callback defined locally in Apps.lazy_model_operation() and 

126 annotated there with a `func` attribute so as to imitate a partial. 

127 """ 

128 operation, args, keywords = obj, [], {} 

129 while hasattr(operation, "func"): 

130 args.extend(getattr(operation, "args", [])) 

131 keywords.update(getattr(operation, "keywords", {})) 

132 operation = operation.func 

133 return operation, args, keywords 

134 

135 def app_model_error(model_key): 

136 try: 

137 apps.get_app_config(model_key[0]) 

138 model_error = "app '%s' doesn't provide model '%s'" % model_key 

139 except LookupError: 

140 model_error = "app '%s' isn't installed" % model_key[0] 

141 return model_error 

142 

143 # Here are several functions which return CheckMessage instances for the 

144 # most common usages of lazy operations throughout Django. These functions 

145 # take the model that was being waited on as an (app_label, modelname) 

146 # pair, the original lazy function, and its positional and keyword args as 

147 # determined by extract_operation(). 

148 

149 def field_error(model_key, func, args, keywords): 

150 error_msg = ( 

151 "The field %(field)s was declared with a lazy reference " 

152 "to '%(model)s', but %(model_error)s." 

153 ) 

154 params = { 

155 "model": ".".join(model_key), 

156 "field": keywords["field"], 

157 "model_error": app_model_error(model_key), 

158 } 

159 return Error(error_msg % params, obj=keywords["field"], id="fields.E307") 

160 

161 def signal_connect_error(model_key, func, args, keywords): 

162 error_msg = ( 

163 "%(receiver)s was connected to the '%(signal)s' signal with a " 

164 "lazy reference to the sender '%(model)s', but %(model_error)s." 

165 ) 

166 receiver = args[0] 

167 # The receiver is either a function or an instance of class 

168 # defining a `__call__` method. 

169 if isinstance(receiver, types.FunctionType): 

170 description = "The function '%s'" % receiver.__name__ 

171 elif isinstance(receiver, types.MethodType): 

172 description = "Bound method '%s.%s'" % ( 

173 receiver.__self__.__class__.__name__, 

174 receiver.__name__, 

175 ) 

176 else: 

177 description = "An instance of class '%s'" % receiver.__class__.__name__ 

178 signal_name = model_signals.get(func.__self__, "unknown") 

179 params = { 

180 "model": ".".join(model_key), 

181 "receiver": description, 

182 "signal": signal_name, 

183 "model_error": app_model_error(model_key), 

184 } 

185 return Error(error_msg % params, obj=receiver.__module__, id="signals.E001") 

186 

187 def default_error(model_key, func, args, keywords): 

188 error_msg = ( 

189 "%(op)s contains a lazy reference to %(model)s, but %(model_error)s." 

190 ) 

191 params = { 

192 "op": func, 

193 "model": ".".join(model_key), 

194 "model_error": app_model_error(model_key), 

195 } 

196 return Error(error_msg % params, obj=func, id="models.E022") 

197 

198 # Maps common uses of lazy operations to corresponding error functions 

199 # defined above. If a key maps to None, no error will be produced. 

200 # default_error() will be used for usages that don't appear in this dict. 

201 known_lazy = { 

202 ("django.db.models.fields.related", "resolve_related_class"): field_error, 

203 ("django.db.models.fields.related", "set_managed"): None, 

204 ("django.dispatch.dispatcher", "connect"): signal_connect_error, 

205 } 

206 

207 def build_error(model_key, func, args, keywords): 

208 key = (func.__module__, func.__name__) 

209 error_fn = known_lazy.get(key, default_error) 

210 return error_fn(model_key, func, args, keywords) if error_fn else None 

211 

212 return sorted( 

213 filter( 

214 None, 

215 ( 

216 build_error(model_key, *extract_operation(func)) 

217 for model_key in pending_models 

218 for func in apps._pending_operations[model_key] 

219 ), 

220 ), 

221 key=lambda error: error.msg, 

222 ) 

223 

224 

225@register(Tags.models) 

226def check_lazy_references(app_configs=None, **kwargs): 

227 return _check_lazy_references(apps)