2 Module is used to infer Django model fields.
4 from inspect
import Parameter
7 from jedi
.inference
.cache
import inference_state_function_cache
8 from jedi
.inference
.base_value
import ValueSet
, iterator_to_value_set
, ValueWrapper
9 from jedi
.inference
.filters
import DictFilter
, AttributeOverwrite
10 from jedi
.inference
.names
import NameWrapper
, BaseTreeParamName
11 from jedi
.inference
.compiled
.value
import EmptyCompiledName
12 from jedi
.inference
.value
.instance
import TreeInstance
13 from jedi
.inference
.value
.klass
import ClassMixin
14 from jedi
.inference
.gradual
.base
import GenericClass
15 from jedi
.inference
.gradual
.generics
import TupleGenericManager
16 from jedi
.inference
.signature
import AbstractSignature
20 'IntegerField': (None, 'int'),
21 'BigIntegerField': (None, 'int'),
22 'PositiveIntegerField': (None, 'int'),
23 'SmallIntegerField': (None, 'int'),
24 'CharField': (None, 'str'),
25 'TextField': (None, 'str'),
26 'EmailField': (None, 'str'),
27 'GenericIPAddressField': (None, 'str'),
28 'URLField': (None, 'str'),
29 'FloatField': (None, 'float'),
30 'BinaryField': (None, 'bytes'),
31 'BooleanField': (None, 'bool'),
32 'DecimalField': ('decimal', 'Decimal'),
33 'TimeField': ('datetime', 'time'),
34 'DurationField': ('datetime', 'timedelta'),
35 'DateField': ('datetime', 'date'),
36 'DateTimeField': ('datetime', 'datetime'),
37 'UUIDField': ('uuid', 'UUID'),
40 _FILTER_LIKE_METHODS
= ('create', 'filter', 'exclude', 'update', 'get',
41 'get_or_create', 'update_or_create')
44 @inference_state_function_cache()
45 def _get_deferred_attributes(inference_state
):
46 return inference_state
.import_module(
47 ('django', 'db', 'models', 'query_utils')
48 ).py__getattribute__('DeferredAttribute').execute_annotation()
51 def _infer_scalar_field(inference_state
, field_name
, field_tree_instance
, is_instance
):
53 module_name
, attribute_name
= mapping
[field_tree_instance
.py__name__()]
58 return _get_deferred_attributes(inference_state
)
60 if module_name
is None:
61 module
= inference_state
.builtins_module
63 module
= inference_state
.import_module((module_name
,))
65 for attribute
in module
.py__getattribute__(attribute_name
):
66 return attribute
.execute_with_values()
69 @iterator_to_value_set
70 def _get_foreign_key_values(cls
, field_tree_instance
):
71 if isinstance(field_tree_instance
, TreeInstance
):
72 # TODO private access..
73 argument_iterator
= field_tree_instance
._arguments
.unpack()
74 key
, lazy_values
= next(argument_iterator
, (None, None))
75 if key
is None and lazy_values
is not None:
76 for value
in lazy_values
.infer():
77 if value
.py__name__() == 'str':
78 foreign_key_class_name
= value
.get_safe_value()
79 module
= cls
.get_root_context()
80 for v
in module
.py__getattribute__(foreign_key_class_name
):
83 elif value
.is_class():
87 def _infer_field(cls
, field_name
, is_instance
):
88 inference_state
= cls
.inference_state
89 result
= field_name
.infer()
90 for field_tree_instance
in result
:
91 scalar_field
= _infer_scalar_field(
92 inference_state
, field_name
, field_tree_instance
, is_instance
)
93 if scalar_field
is not None:
96 name
= field_tree_instance
.py__name__()
97 is_many_to_many
= name
== 'ManyToManyField'
98 if name
in ('ForeignKey', 'OneToOneField') or is_many_to_many
:
100 return _get_deferred_attributes(inference_state
)
102 values
= _get_foreign_key_values(cls
, field_tree_instance
)
104 return ValueSet(filter(None, [
105 _create_manager_for(v
, 'RelatedManager') for v
in values
108 return values
.execute_with_values()
110 debug
.dbg('django plugin: fail to infer `%s` from class `%s`',
111 field_name
.string_name
, cls
.py__name__())
115 class DjangoModelName(NameWrapper
):
116 def __init__(self
, cls
, name
, is_instance
):
117 super().__init
__(name
)
119 self
._is
_instance
= is_instance
122 return _infer_field(self
._cls
, self
._wrapped
_name
, self
._is
_instance
)
125 def _create_manager_for(cls
, manager_cls
='BaseManager'):
126 managers
= cls
.inference_state
.import_module(
127 ('django', 'db', 'models', 'manager')
128 ).py__getattribute__(manager_cls
)
130 if m
.is_class_mixin():
131 generics_manager
= TupleGenericManager((ValueSet([cls
]),))
132 for c
in GenericClass(m
, generics_manager
).execute_annotation():
137 def _new_dict_filter(cls
, is_instance
):
138 filters
= list(cls
.get_filters(
139 is_instance
=is_instance
,
140 include_metaclasses
=False,
141 include_type_when_class
=False)
144 name
.string_name
: DjangoModelName(cls
, name
, is_instance
)
145 for filter_
in reversed(filters
)
146 for name
in filter_
.values()
149 # Replace the objects with a name that amounts to nothing when accessed
150 # in an instance. This is not perfect and still completes "objects" in
151 # that case, but it at least not inferes stuff like `.objects.filter`.
152 # It would be nicer to do that in a better way, so that it also doesn't
153 # show up in completions, but it's probably just not worth doing that
154 # for the extra amount of work.
155 dct
['objects'] = EmptyCompiledName(cls
.inference_state
, 'objects')
157 return DictFilter(dct
)
160 def is_django_model_base(value
):
161 return value
.py__name__() == 'ModelBase' \
162 and value
.get_root_context().py__name__() == 'django.db.models.base'
165 def get_metaclass_filters(func
):
166 def wrapper(cls
, metaclasses
, is_instance
):
167 for metaclass
in metaclasses
:
168 if is_django_model_base(metaclass
):
169 return [_new_dict_filter(cls
, is_instance
)]
171 return func(cls
, metaclasses
, is_instance
)
175 def tree_name_to_values(func
):
176 def wrapper(inference_state
, context
, tree_name
):
177 result
= func(inference_state
, context
, tree_name
)
178 if tree_name
.value
in _FILTER_LIKE_METHODS
:
179 # Here we try to overwrite stuff like User.objects.filter. We need
180 # this to make sure that keyword param completion works on these
183 if v
.get_qualified_names() == ('_BaseQuerySet', tree_name
.value
) \
184 and v
.parent_context
.is_module() \
185 and v
.parent_context
.py__name__() == 'django.db.models.query':
186 qs
= context
.get_value()
187 generics
= qs
.get_generics()
188 if len(generics
) >= 1:
189 return ValueSet(QuerySetMethodWrapper(v
, model
)
190 for model
in generics
[0])
192 elif tree_name
.value
== 'BaseManager' and context
.is_module() \
193 and context
.py__name__() == 'django.db.models.manager':
194 return ValueSet(ManagerWrapper(r
) for r
in result
)
196 elif tree_name
.value
== 'Field' and context
.is_module() \
197 and context
.py__name__() == 'django.db.models.fields':
198 return ValueSet(FieldWrapper(r
) for r
in result
)
203 def _find_fields(cls
):
204 for name
in _new_dict_filter(cls
, is_instance
=False).values():
205 for value
in name
.infer():
206 if value
.name
.get_qualified_names(include_module_names
=True) \
207 == ('django', 'db', 'models', 'query_utils', 'DeferredAttribute'):
211 def _get_signatures(cls
):
212 return [DjangoModelSignature(cls
, field_names
=list(_find_fields(cls
)))]
215 def get_metaclass_signatures(func
):
216 def wrapper(cls
, metaclasses
):
217 for metaclass
in metaclasses
:
218 if is_django_model_base(metaclass
):
219 return _get_signatures(cls
)
220 return func(cls
, metaclass
)
224 class ManagerWrapper(ValueWrapper
):
225 def py__getitem__(self
, index_value_set
, contextualized_node
):
227 GenericManagerWrapper(generic
)
228 for generic
in self
._wrapped
_value
.py__getitem__(
229 index_value_set
, contextualized_node
)
233 class GenericManagerWrapper(AttributeOverwrite
, ClassMixin
):
234 def py__get__on_class(self
, calling_instance
, instance
, class_value
):
235 return calling_instance
.class_value
.with_generics(
236 (ValueSet({class_value}
),)
237 ).py__call__(calling_instance
._arguments
)
239 def with_generics(self
, generics_tuple
):
240 return self
._wrapped
_value
.with_generics(generics_tuple
)
243 class FieldWrapper(ValueWrapper
):
244 def py__getitem__(self
, index_value_set
, contextualized_node
):
246 GenericFieldWrapper(generic
)
247 for generic
in self
._wrapped
_value
.py__getitem__(
248 index_value_set
, contextualized_node
)
252 class GenericFieldWrapper(AttributeOverwrite
, ClassMixin
):
253 def py__get__on_class(self
, calling_instance
, instance
, class_value
):
254 # This is mostly an optimization to avoid Jedi aborting inference,
255 # because of too many function executions of Field.__get__.
256 return ValueSet({calling_instance}
)
259 class DjangoModelSignature(AbstractSignature
):
260 def __init__(self
, value
, field_names
):
261 super().__init
__(value
)
262 self
._field
_names
= field_names
264 def get_param_names(self
, resolve_stars
=False):
265 return [DjangoParamName(name
) for name
in self
._field
_names
]
268 class DjangoParamName(BaseTreeParamName
):
269 def __init__(self
, field_name
):
270 super().__init
__(field_name
.parent_context
, field_name
.tree_name
)
271 self
._field
_name
= field_name
274 return Parameter
.KEYWORD_ONLY
277 return self
._field
_name
.infer()
280 class QuerySetMethodWrapper(ValueWrapper
):
281 def __init__(self
, method
, model_cls
):
282 super().__init
__(method
)
283 self
._model
_cls
= model_cls
285 def py__get__(self
, instance
, class_value
):
286 return ValueSet({QuerySetBoundMethodWrapper(v
, self
._model
_cls
)
287 for v
in self
._wrapped
_value
.py__get__(instance
, class_value
)})
290 class QuerySetBoundMethodWrapper(ValueWrapper
):
291 def __init__(self
, method
, model_cls
):
292 super().__init
__(method
)
293 self
._model
_cls
= model_cls
295 def get_signatures(self
):
296 return _get_signatures(self
._model
_cls
)