diff --git a/.gitmodules b/.gitmodules index 368cba0a..1a59e543 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/davidhalter/typeshed.git [submodule "jedi/third_party/django-stubs"] path = jedi/third_party/django-stubs - url = https://github.com/typeddjango/django-stubs + url = https://github.com/davidhalter/django-stubs diff --git a/jedi/inference/base_value.py b/jedi/inference/base_value.py index 8d7b0961..dfc7f46c 100644 --- a/jedi/inference/base_value.py +++ b/jedi/inference/base_value.py @@ -244,6 +244,9 @@ class Value(HelperValueMixin): debug.warning("No __get__ defined on %s", self) return ValueSet([self]) + def py__get__on_class(self, calling_instance, instance, class_value): + return NotImplemented + def get_qualified_names(self): # Returns Optional[Tuple[str, ...]] return None diff --git a/jedi/inference/filters.py b/jedi/inference/filters.py index e7dc3849..00e594f7 100644 --- a/jedi/inference/filters.py +++ b/jedi/inference/filters.py @@ -255,7 +255,7 @@ class _BuiltinMappedMethod(ValueWrapper): def py__call__(self, arguments): # TODO add TypeError if params are given/or not correct. - return self._method(self._value) + return self._method(self._value, arguments) class SpecialMethodFilter(DictFilter): @@ -330,7 +330,7 @@ class _AttributeOverwriteMixin(object): def get_filters(self, *args, **kwargs): yield SpecialMethodFilter(self, self.overwritten_methods, self._wrapped_value) - for filter in self._wrapped_value.get_filters(): + for filter in self._wrapped_value.get_filters(*args, **kwargs): yield filter diff --git a/jedi/inference/gradual/base.py b/jedi/inference/gradual/base.py index 2c0ac993..2b5511f8 100644 --- a/jedi/inference/gradual/base.py +++ b/jedi/inference/gradual/base.py @@ -156,7 +156,7 @@ class DefineGenericBaseClass(LazyValueWrapper): ) -class GenericClass(ClassMixin, DefineGenericBaseClass): +class GenericClass(DefineGenericBaseClass, ClassMixin): """ A class that is defined with generics, might be something simple like: @@ -205,6 +205,9 @@ class GenericClass(ClassMixin, DefineGenericBaseClass): return True return self._class_value.is_sub_class_of(class_value) + def with_generics(self, generics_tuple): + return self._class_value.with_generics(generics_tuple) + def infer_type_vars(self, value_set): # Circular from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts @@ -292,6 +295,9 @@ class _LazyGenericBaseClass(object): new |= ValueSet([type_var]) yield new + def __repr__(self): + return '<%s: %s>' % (self.__class__.__name__, self._lazy_base_class) + class _GenericInstanceWrapper(ValueWrapper): def py__stop_iteration_returns(self): diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index 0f3cd5f8..e3a95009 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -203,7 +203,7 @@ class _TypingClassMixin(ClassMixin): return ValueName(self, self._tree_name) -class TypingClassWithGenerics(_TypingClassMixin, ProxyWithGenerics): +class TypingClassWithGenerics(ProxyWithGenerics, _TypingClassMixin): def infer_type_vars(self, value_set): type_var_dict = {} annotation_generics = self.get_generics() @@ -240,7 +240,7 @@ class TypingClassWithGenerics(_TypingClassMixin, ProxyWithGenerics): ) -class ProxyTypingClassValue(_TypingClassMixin, ProxyTypingValue): +class ProxyTypingClassValue(ProxyTypingValue, _TypingClassMixin): index_class = TypingClassWithGenerics diff --git a/jedi/inference/value/instance.py b/jedi/inference/value/instance.py index bc06cfd4..5435c791 100644 --- a/jedi/inference/value/instance.py +++ b/jedi/inference/value/instance.py @@ -288,6 +288,11 @@ class _BaseTreeInstance(AbstractInstanceValue): """ # Arguments in __get__ descriptors are obj, class. # `method` is the new parent of the array, don't know if that's good. + for cls in self.class_value.py__mro__(): + result = cls.py__get__on_class(self, instance, class_value) + if result is not NotImplemented: + return result + names = self.get_function_slot_names(u'__get__') if names: if instance is None: diff --git a/jedi/inference/value/iterable.py b/jedi/inference/value/iterable.py index d23ca13e..43692c57 100644 --- a/jedi/inference/value/iterable.py +++ b/jedi/inference/value/iterable.py @@ -58,13 +58,13 @@ class GeneratorBase(LazyAttributeOverwrite, IterableMixin): return True @publish_method('__iter__') - def py__iter__(self, contextualized_node=None): + def _iter(self, arguments): return ValueSet([self]) @publish_method('send') @publish_method('next', python_version_match=2) @publish_method('__next__', python_version_match=3) - def py__next__(self): + def py__next__(self, arguments): return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__()) def py__stop_iteration_returns(self): @@ -290,12 +290,12 @@ class DictComprehension(ComprehensionMixin, Sequence, _DictKeyMixin): return ValueSet.from_sets(values for keys, values in self._iterate()) @publish_method('values') - def _imitate_values(self): + def _imitate_values(self, arguments): lazy_value = LazyKnownValues(self._dict_values()) return ValueSet([FakeList(self.inference_state, [lazy_value])]) @publish_method('items') - def _imitate_items(self): + def _imitate_items(self, arguments): lazy_values = [ LazyKnownValue( FakeTuple( @@ -457,12 +457,12 @@ class DictLiteralValue(_DictMixin, SequenceLiteralValue, _DictKeyMixin): yield LazyKnownValues(types) @publish_method('values') - def _imitate_values(self): + def _imitate_values(self, arguments): lazy_value = LazyKnownValues(self._dict_values()) return ValueSet([FakeList(self.inference_state, [lazy_value])]) @publish_method('items') - def _imitate_items(self): + def _imitate_items(self, arguments): lazy_values = [ LazyKnownValue(FakeTuple( self.inference_state, @@ -552,7 +552,7 @@ class FakeDict(_DictMixin, Sequence, _DictKeyMixin): return lazy_value.infer() @publish_method('values') - def _values(self): + def _values(self, arguments): return ValueSet([FakeTuple( self.inference_state, [LazyKnownValues(self._dict_values())] diff --git a/jedi/inference/value/klass.py b/jedi/inference/value/klass.py index 4a47da0e..33510445 100644 --- a/jedi/inference/value/klass.py +++ b/jedi/inference/value/klass.py @@ -114,8 +114,6 @@ class ClassFilter(ParserTreeFilter): if expr_stmt is not None and expr_stmt.type == 'expr_stmt': annassign = expr_stmt.children[1] if annassign.type == 'annassign': - # TODO this is not proper matching - # If there is an =, the variable is obviously also # defined on the class. if 'ClassVar' not in annassign.children[1].get_code() \ @@ -138,7 +136,7 @@ class ClassMixin(object): def is_class_mixin(self): return True - def py__call__(self, arguments=None): + def py__call__(self, arguments): from jedi.inference.value import TreeInstance from jedi.inference.gradual.typing import TypedDict @@ -189,12 +187,13 @@ class ClassMixin(object): mro.append(cls_new) yield cls_new - def get_filters(self, origin_scope=None, is_instance=False, include_metaclasses=True): + def get_filters(self, origin_scope=None, is_instance=False, + include_metaclasses=True, include_type_when_class=True): if include_metaclasses: metaclasses = self.get_metaclasses() if metaclasses: - for f in self.get_metaclass_filters(metaclasses): - yield f + for f in self.get_metaclass_filters(metaclasses, is_instance): + yield f # Python 2.. for cls in self.py__mro__(): if cls.is_compiled(): @@ -206,7 +205,7 @@ class ClassMixin(object): origin_scope=origin_scope, is_instance=is_instance ) - if not is_instance: + if not is_instance and include_type_when_class: from jedi.inference.compiled import builtin_from_name type_ = builtin_from_name(self.inference_state, u'type') assert isinstance(type_, ClassValue) @@ -228,6 +227,11 @@ class ClassMixin(object): # Since calling staticmethod without a function is illegal, the Jedi # plugin doesn't return anything. Therefore call directly and get what # we want: An instance of staticmethod. + metaclasses = self.get_metaclasses() + if metaclasses: + sigs = self.get_metaclass_signatures(metaclasses) + if sigs: + return sigs args = ValuesArguments([]) init_funcs = self.py__call__(args).py__getattribute__('__init__') return [sig.bind(self) for sig in init_funcs.get_signatures()] @@ -270,50 +274,6 @@ class ClassMixin(object): return True return False - -class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase)): - api_type = u'class' - - @inference_state_method_cache() - def list_type_vars(self): - found = [] - arglist = self.tree_node.get_super_arglist() - if arglist is None: - return [] - - for stars, node in unpack_arglist(arglist): - if stars: - continue # These are not relevant for this search. - - from jedi.inference.gradual.annotation import find_unknown_type_vars - for type_var in find_unknown_type_vars(self.parent_context, node): - if type_var not in found: - # The order matters and it's therefore a list. - found.append(type_var) - return found - - def _get_bases_arguments(self): - arglist = self.tree_node.get_super_arglist() - if arglist: - from jedi.inference import arguments - return arguments.TreeArguments(self.inference_state, self.parent_context, arglist) - return None - - @inference_state_method_cache(default=()) - def py__bases__(self): - args = self._get_bases_arguments() - if args is not None: - lst = [value for key, value in args.unpack() if key is None] - if lst: - return lst - - if self.py__name__() == 'object' \ - and self.parent_context.is_builtins_module(): - return [] - return [LazyKnownValues( - self.inference_state.builtins_module.py__getattribute__('object') - )] - def py__getitem__(self, index_value_set, contextualized_node): from jedi.inference.gradual.base import GenericClass if not index_value_set: @@ -360,9 +320,53 @@ class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase )]) return ValueSet({self}) + +class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase)): + api_type = u'class' + + @inference_state_method_cache() + def list_type_vars(self): + found = [] + arglist = self.tree_node.get_super_arglist() + if arglist is None: + return [] + + for stars, node in unpack_arglist(arglist): + if stars: + continue # These are not relevant for this search. + + from jedi.inference.gradual.annotation import find_unknown_type_vars + for type_var in find_unknown_type_vars(self.parent_context, node): + if type_var not in found: + # The order matters and it's therefore a list. + found.append(type_var) + return found + + def _get_bases_arguments(self): + arglist = self.tree_node.get_super_arglist() + if arglist: + from jedi.inference import arguments + return arguments.TreeArguments(self.inference_state, self.parent_context, arglist) + return None + + @inference_state_method_cache(default=()) + def py__bases__(self): + args = self._get_bases_arguments() + if args is not None: + lst = [value for key, value in args.unpack() if key is None] + if lst: + return lst + + if self.py__name__() == 'object' \ + and self.parent_context.is_builtins_module(): + return [] + return [LazyKnownValues( + self.inference_state.builtins_module.py__getattribute__('object') + )] + @plugin_manager.decorate() - def get_metaclass_filters(self, metaclass): - debug.dbg('Unprocessed metaclass %s', metaclass) + def get_metaclass_filters(self, metaclasses, is_instance): + debug.warning('Unprocessed metaclass %s', metaclasses) return [] @inference_state_method_cache(default=NO_VALUES) @@ -382,3 +386,7 @@ class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase if values: return values return NO_VALUES + + @plugin_manager.decorate() + def get_metaclass_signatures(self, metaclasses): + return [] diff --git a/jedi/plugins/django.py b/jedi/plugins/django.py index 89bc38c6..88a3bfae 100644 --- a/jedi/plugins/django.py +++ b/jedi/plugins/django.py @@ -1,13 +1,18 @@ """ Module is used to infer Django model fields. """ +from jedi._compatibility import Parameter from jedi import debug -from jedi.inference.base_value import ValueSet, iterator_to_value_set -from jedi.inference.filters import ParserTreeFilter, DictFilter -from jedi.inference.names import NameWrapper +from jedi.inference.cache import inference_state_function_cache +from jedi.inference.base_value import ValueSet, iterator_to_value_set, ValueWrapper +from jedi.inference.filters import DictFilter, AttributeOverwrite +from jedi.inference.names import NameWrapper, BaseTreeParamName +from jedi.inference.compiled.value import EmptyCompiledName from jedi.inference.value.instance import TreeInstance +from jedi.inference.value.klass import ClassMixin from jedi.inference.gradual.base import GenericClass from jedi.inference.gradual.generics import TupleGenericManager +from jedi.inference.signature import AbstractSignature mapping = { @@ -31,13 +36,26 @@ mapping = { 'UUIDField': ('uuid', 'UUID'), } +_FILTER_LIKE_METHODS = ('create', 'filter', 'exclude', 'update', 'get', + 'get_or_create', 'update_or_create') -def _infer_scalar_field(inference_state, field_name, field_tree_instance): + +@inference_state_function_cache() +def _get_deferred_attributes(inference_state): + return inference_state.import_module( + ('django', 'db', 'models', 'query_utils') + ).py__getattribute__('DeferredAttribute').execute_annotation() + + +def _infer_scalar_field(inference_state, field_name, field_tree_instance, is_instance): try: module_name, attribute_name = mapping[field_tree_instance.py__name__()] except KeyError: return None + if not is_instance: + return _get_deferred_attributes(inference_state) + if module_name is None: module = inference_state.builtins_module else: @@ -65,16 +83,21 @@ def _get_foreign_key_values(cls, field_tree_instance): yield value -def _infer_field(cls, field_name): +def _infer_field(cls, field_name, is_instance): inference_state = cls.inference_state - for field_tree_instance in field_name.infer(): - scalar_field = _infer_scalar_field(inference_state, field_name, field_tree_instance) + result = field_name.infer() + for field_tree_instance in result: + scalar_field = _infer_scalar_field( + inference_state, field_name, field_tree_instance, is_instance) if scalar_field is not None: return scalar_field name = field_tree_instance.py__name__() is_many_to_many = name == 'ManyToManyField' if name in ('ForeignKey', 'OneToOneField') or is_many_to_many: + if not is_instance: + return _get_deferred_attributes(inference_state) + values = _get_foreign_key_values(cls, field_tree_instance) if is_many_to_many: return ValueSet(filter(None, [ @@ -85,16 +108,17 @@ def _infer_field(cls, field_name): debug.dbg('django plugin: fail to infer `%s` from class `%s`', field_name.string_name, cls.py__name__()) - return field_name.infer() + return result class DjangoModelName(NameWrapper): - def __init__(self, cls, name): + def __init__(self, cls, name, is_instance): super(DjangoModelName, self).__init__(name) self._cls = cls + self._is_instance = is_instance def infer(self): - return _infer_field(self._cls, self._wrapped_name) + return _infer_field(self._cls, self._wrapped_name, self._is_instance) def _create_manager_for(cls, manager_cls='BaseManager'): @@ -109,25 +133,163 @@ def _create_manager_for(cls, manager_cls='BaseManager'): return None -def _new_dict_filter(cls): - filters = cls.get_filters(is_instance=True, include_metaclasses=False) +def _new_dict_filter(cls, is_instance): + filters = list(cls.get_filters( + is_instance=is_instance, + include_metaclasses=False, + include_type_when_class=False) + ) dct = { - name.string_name: DjangoModelName(cls, name) - for filter_ in reversed(list(filters)) + name.string_name: DjangoModelName(cls, name, is_instance) + for filter_ in reversed(filters) for name in filter_.values() } - manager = _create_manager_for(cls) - if manager: - dct['objects'] = manager.name + if is_instance: + # Replace the objects with a name that amounts to nothing when accessed + # in an instance. This is not perfect and still completes "objects" in + # that case, but it at least not inferes stuff like `.objects.filter`. + # It would be nicer to do that in a better way, so that it also doesn't + # show up in completions, but it's probably just not worth doing that + # for the extra amount of work. + dct['objects'] = EmptyCompiledName(cls.inference_state, 'objects') + return DictFilter(dct) +def is_django_model_base(value): + return value.py__name__() == 'ModelBase' \ + and value.get_root_context().py__name__() == 'django.db.models.base' + + def get_metaclass_filters(func): + def wrapper(cls, metaclasses, is_instance): + for metaclass in metaclasses: + if is_django_model_base(metaclass): + return [_new_dict_filter(cls, is_instance)] + + return func(cls, metaclasses, is_instance) + return wrapper + + +def tree_name_to_values(func): + def wrapper(inference_state, context, tree_name): + result = func(inference_state, context, tree_name) + if tree_name.value in _FILTER_LIKE_METHODS: + # Here we try to overwrite stuff like User.objects.filter. We need + # this to make sure that keyword param completion works on these + # kind of methods. + for v in result: + if v.get_qualified_names() == ('_BaseQuerySet', tree_name.value) \ + and v.parent_context.is_module() \ + and v.parent_context.py__name__() == 'django.db.models.query': + qs = context.get_value() + generics = qs.get_generics() + if len(generics) >= 1: + return ValueSet(QuerySetMethodWrapper(v, model) + for model in generics[0]) + + elif tree_name.value == 'BaseManager' and context.is_module() \ + and context.py__name__() == 'django.db.models.manager': + return ValueSet(ManagerWrapper(r) for r in result) + + elif tree_name.value == 'Field' and context.is_module() \ + and context.py__name__() == 'django.db.models.fields': + return ValueSet(FieldWrapper(r) for r in result) + return result + return wrapper + + +def _find_fields(cls): + for name in _new_dict_filter(cls, is_instance=False).values(): + for value in name.infer(): + if value.name.get_qualified_names(include_module_names=True) \ + == ('django', 'db', 'models', 'query_utils', 'DeferredAttribute'): + yield name + + +def _get_signatures(cls): + return [DjangoModelSignature(cls, field_names=list(_find_fields(cls)))] + + +def get_metaclass_signatures(func): def wrapper(cls, metaclasses): for metaclass in metaclasses: - if metaclass.py__name__() == 'ModelBase' \ - and metaclass.get_root_context().py__name__() == 'django.db.models.base': - return [_new_dict_filter(cls)] - - return func(cls, metaclasses) + if is_django_model_base(metaclass): + return _get_signatures(cls) + return func(cls, metaclass) return wrapper + + +class ManagerWrapper(ValueWrapper): + def py__getitem__(self, index_value_set, contextualized_node): + return ValueSet( + GenericManagerWrapper(generic) + for generic in self._wrapped_value.py__getitem__( + index_value_set, contextualized_node) + ) + + +class GenericManagerWrapper(AttributeOverwrite, ClassMixin): + def py__get__on_class(self, calling_instance, instance, class_value): + return calling_instance.class_value.with_generics( + (ValueSet({class_value}),) + ).py__call__(calling_instance._arguments) + + def with_generics(self, generics_tuple): + return self._wrapped_value.with_generics(generics_tuple) + + +class FieldWrapper(ValueWrapper): + def py__getitem__(self, index_value_set, contextualized_node): + return ValueSet( + GenericFieldWrapper(generic) + for generic in self._wrapped_value.py__getitem__( + index_value_set, contextualized_node) + ) + + +class GenericFieldWrapper(AttributeOverwrite, ClassMixin): + def py__get__on_class(self, calling_instance, instance, class_value): + # This is mostly an optimization to avoid Jedi aborting inference, + # because of too many function executions of Field.__get__. + return ValueSet({calling_instance}) + + +class DjangoModelSignature(AbstractSignature): + def __init__(self, value, field_names): + super(DjangoModelSignature, self).__init__(value) + self._field_names = field_names + + def get_param_names(self, resolve_stars=False): + return [DjangoParamName(name) for name in self._field_names] + + +class DjangoParamName(BaseTreeParamName): + def __init__(self, field_name): + super(DjangoParamName, self).__init__(field_name.parent_context, field_name.tree_name) + self._field_name = field_name + + def get_kind(self): + return Parameter.KEYWORD_ONLY + + def infer(self): + return self._field_name.infer() + + +class QuerySetMethodWrapper(ValueWrapper): + def __init__(self, method, model_cls): + super(QuerySetMethodWrapper, self).__init__(method) + self._model_cls = model_cls + + def py__get__(self, instance, class_value): + return ValueSet({QuerySetBoundMethodWrapper(v, self._model_cls) + for v in self._wrapped_value.py__get__(instance, class_value)}) + + +class QuerySetBoundMethodWrapper(ValueWrapper): + def __init__(self, method, model_cls): + super(QuerySetBoundMethodWrapper, self).__init__(method) + self._model_cls = model_cls + + def get_signatures(self): + return _get_signatures(self._model_cls) diff --git a/jedi/plugins/stdlib.py b/jedi/plugins/stdlib.py index 19d86dd3..382b8ced 100644 --- a/jedi/plugins/stdlib.py +++ b/jedi/plugins/stdlib.py @@ -260,13 +260,12 @@ class ReversedObject(AttributeOverwrite): super(ReversedObject, self).__init__(reversed_obj) self._iter_list = iter_list - @publish_method('__iter__') - def py__iter__(self, contextualized_node=None): + def py__iter__(self, contextualized_node): return self._iter_list @publish_method('next', python_version_match=2) @publish_method('__next__', python_version_match=3) - def py__next__(self): + def py__next__(self, arguments): return ValueSet.from_sets( lazy_value.infer() for lazy_value in self._iter_list ) @@ -395,13 +394,13 @@ class PropertyObject(AttributeOverwrite, ValueWrapper): def py__get__(self, instance, class_value): if instance is None: - return NO_VALUES + return ValueSet([self]) return self._function.execute_with_values(instance) @publish_method('deleter') @publish_method('getter') @publish_method('setter') - def _return_self(self): + def _return_self(self, arguments): return ValueSet({self}) @@ -518,6 +517,8 @@ class PartialObject(ValueWrapper): class PartialMethodObject(PartialObject): def py__get__(self, instance, class_value): + if instance is None: + return ValueSet([self]) return ValueSet([PartialObject(self._actual_value, self._arguments, instance)]) @@ -802,7 +803,7 @@ _implemented = { def get_metaclass_filters(func): - def wrapper(cls, metaclasses): + def wrapper(cls, metaclasses, is_instance): for metaclass in metaclasses: if metaclass.py__name__() == 'EnumMeta' \ and metaclass.get_root_context().py__name__() == 'enum': @@ -810,7 +811,7 @@ def get_metaclass_filters(func): return [DictFilter({ name.string_name: EnumInstance(cls, name).name for name in filter_.values() })] - return func(cls, metaclasses) + return func(cls, metaclasses, is_instance) return wrapper diff --git a/test/completion/django.py b/test/completion/django.py index 88a508d9..533cab6e 100644 --- a/test/completion/django.py +++ b/test/completion/django.py @@ -4,11 +4,21 @@ import uuid from django.db import models from django.contrib.auth.models import User +from django.db.models.query_utils import DeferredAttribute + + +class TagManager(models.Manager): + def specially_filtered_tags(self): + return self.all() class Tag(models.Model): tag_name = models.CharField() + objects = TagManager() + + custom_objects = TagManager() + class Category(models.Model): category_name = models.CharField() @@ -49,10 +59,23 @@ class BusinessModel(models.Model): unidentifiable = NOT_FOUND + #? models.IntegerField() + integer_field + + def method(self): + return 42 + # ----------------- # Model attribute inference # ----------------- +#? DeferredAttribute() +BusinessModel.integer_field +#? DeferredAttribute() +BusinessModel.tags_m2m +#? DeferredAttribute() +BusinessModel.email_field + model_instance = BusinessModel() #? int() @@ -132,20 +155,45 @@ model_instance.unidentifiable #! ['unidentifiable = NOT_FOUND'] model_instance.unidentifiable +#? int() +model_instance.method() +#! ['def method'] +model_instance.method + # ----------------- # Queries # ----------------- -#? models.query.QuerySet.filter +#? ['objects'] +model_instance.object +#? +model_instance.objects +#? model_instance.objects.filter +#? models.query.QuerySet.filter +BusinessModel.objects.filter #? BusinessModel() None -model_instance.objects.filter().first() +BusinessModel.objects.filter().first() #? str() -model_instance.objects.get().char_field +BusinessModel.objects.get().char_field #? int() -model_instance.objects.update(x='') +BusinessModel.objects.update(x='') #? BusinessModel() -model_instance.objects.create() +BusinessModel.objects.create() + +# ----------------- +# Custom object manager +# ----------------- + +#? TagManager() +Tag.objects +#? Tag() None +Tag.objects.filter().first() + +#? TagManager() +Tag.custom_objects +#? Tag() None +Tag.custom_objects.filter().first() # ----------------- # Inheritance @@ -163,14 +211,27 @@ inherited.char_field #? float() inherited.new_field +#? +Inherited.category_fk2.category_name #? str() inherited.category_fk2.category_name #? str() -inherited.objects.get().char_field +Inherited.objects.get().char_field #? int() -inherited.objects.get().text_field +Inherited.objects.get().text_field #? float() -inherited.objects.get().new_field +Inherited.objects.get().new_field + +# ----------------- +# Model methods +# ----------------- + +#? ['from_db'] +Inherited.from_db +#? ['validate_unique'] +Inherited.validate_uniqu +#? ['validate_unique'] +Inherited().validate_unique # ----------------- # Django Auth @@ -186,8 +247,46 @@ User.objects.get().email # ----------------- #? -model_instance.objects.values_list('char_field')[0] +BusinessModel.objects.values_list('char_field')[0] #? dict() -model_instance.objects.values('char_field')[0] +BusinessModel.objects.values('char_field')[0] #? -model_instance.objects.values('char_field')[0]['char_field'] +BusinessModel.objects.values('char_field')[0]['char_field'] + +# ----------------- +# Completion +# ----------------- + +#? 19 ['text_field='] +Inherited(text_fiel) +#? 18 ['new_field='] +Inherited(new_fiel) +#? 19 ['char_field='] +Inherited(char_fiel) +#? 19 ['email_field='] +Inherited(email_fie) +#? 19 [] +Inherited(unidentif) +#? 21 ['category_fk=', 'category_fk2=', 'category_fk3=', 'category_fk4=', 'category_fk5='] +Inherited(category_fk) +#? 21 ['attached_o2o='] +Inherited(attached_o2) +#? 18 ['tags_m2m='] +Inherited(tags_m2m) + +#? 32 ['tags_m2m='] +Inherited.objects.create(tags_m2) +#? 32 ['tags_m2m='] +Inherited.objects.filter(tags_m2) +#? 35 ['char_field='] +Inherited.objects.exclude(char_fiel) +#? 34 ['char_field='] +Inherited.objects.update(char_fiel) +#? 32 ['email_field='] +Inherited.objects.get(email_fiel) +#? 44 ['category_fk2='] +Inherited.objects.get_or_create(category_fk2) +#? 44 ['uuid_field='] +Inherited.objects.update_or_create(uuid_fiel) +#? 48 ['char_field='] +Inherited.objects.exclude(pk=3).filter(char_fiel)