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
« 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
6from django.apps import apps
7from django.conf import settings
8from django.core.checks import Error, Tags, Warning, register
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
93def _check_lazy_references(apps, ignore=None):
94 """
95 Ensure all lazy (i.e. string) model references have been resolved.
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.
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())
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 []
110 from django.db.models import signals
112 model_signals = {
113 signal: name
114 for name, signal in vars(signals).items()
115 if isinstance(signal, signals.ModelSignal)
116 }
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.
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
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
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().
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")
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")
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")
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 }
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
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 )
225@register(Tags.models)
226def check_lazy_references(app_configs=None, **kwargs):
227 return _check_lazy_references(apps)