* Improve typing for unresolved managers
This changes the logic when encountering an unresolvable manager class.
Instead of adding it as a `Manager` we create a subclass of `Manager`
that has `fallback_to_any=True` set. Similarly a `QuerySet` class is
created that also has fallbacks to `Any`. This allows calling custom
methods on the manager and querysets without getting type errors.
* Fix manager created and improve a test
* Fix row type of FallbackQuerySet
Because this inherits from _QuerySet, not QuerySet, it needs to have two
parameters
* Add support for inline from_queryset in model classes
This adds support for calling <Manager>.from_queryset(<QuerySet>)()
inline in models, for example like this:
class MyModel(models.Model):
objects = MyManager.from_queryset(MyQuerySet)()
This is done by inspecting the class body in the transform_class_hook
* Fix missing methods on copied manager
* Add test and other minor tweaks
* Always create manager at module level
When the manager is added at the class level, which happened when it was
created inline in the model body, it's not possible to retrieve the
manager again based on fullname. That lead to problems with inheritance
and the default manager.
* Add test case reproducing Sequence name not defined issue
* Resolve all manager methods as attribute
This changes to logic for resolving methods from the base QuerySet class
on managers from copying the methods to use the attribute approach
that's already used for methods from custom querysets. This resolves the
phantom type errors that stem from the copying.
* Disable cache in test case
Make sure the test will fail regardless of which mypy.ini file is being using.
Co-authored-by: Petter Friberg <petter@5monkeys.se>
* Update comments related to copying methods
* Use a predefined list of manager methods to update
The list of manager methods that returns a queryset, and thus need to
have it's return type changed, is small and well defined. Using a
predefined list of methods rather than trying to detect these at runtime
makes the code much more readable and probably faster as well.
Also add `extra()` to the methods tested in
from_queryset_includes_methods_returning_queryset, and sort the methods
alphabetically.
* Revert changes in .github/workflows/tests.yml
With cache_disable: true on the test case this is no longer needed to
reproduce the bug.
* Remove unsued imports and change type of constant
- Remove unused imports left behind
- Change MANAGER_METHODS_RETURNING_QUERYSET to Final[FrozenSet[str]]
* Import Final from typing_extensions
Was added in 3.8, we still support 3.7
* Sort imports properly
* Remove explicit typing of final frozenset
Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
* Add comment for test case
* Fix typo
* Rename variable
Co-authored-by: Petter Friberg <petter@5monkeys.se>
Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
Char fields with blank=True set should not be considered nullable in the
context of values() and values_list() querysets.
I'm also not a huge fan of the way these fields are made optional in the
constructur to the model classes, it feels like it would be better to
mark the arguments as having a default value, rather than allow sending
in None, but I'd rather keep this fix small and look at the overall
problem at a later point.
* Fix manager types scope
* Restore incremental mode and mention in developer docs
* Separate dev mypy config and regular one
* Document config files usage
When fetching a related field in a values_list queryset Django will
return the object primary key, not model instances as was previously
what the mypy plugin assumed.
* Bump mypy to 0.95x
* Remove the * for inferred types
There was an upstream change (https://github.com/python/mypy/pull/12459)
to remove * from inferred types in the reveal_type output.
As we are asserting the * to exist, all the test cases are now failing
on the 0.950 release. Removing the expected * to mirror the upstream
behaviour change should resolve the test failures.
* Add reproducer for failing case
* Emit warning instead of crashing when encountering enum
* Remove prints, slightly tweak error message
* Remove unused import
* Run black and isort
* Run isort on .pyi file
* Remove unrelated issue from test case
* _default_manager has more specific type for TypeVars
* remove unnecessary # type: ignore
* add test for _base_manager
* add overloads for classproperty.__get__
* readd # type: ignore for WSGIRequestHandler.connection, fails in github's pipeline
* fix _base_manager test: mypy reveals an inferred type
Co-authored-by: Michael Pöhle <michael.poehle@polyteia.de>
* Refactor to more easily support additional config options
* Notify when Manager.from_queryset happens inside model class body
- A warning will be emitted whenever `Manager.from_queryset` happens
inside of a model class body
* Resolve generated default manager types before final iteration
A default manager on a model should always exist, eventually. Although,
we extend to look through dynamically generated managers on each
iteration instead of deferring until the final iteration.
Instead of copying methods over from a QuerySet passed to a basemanager
when invoking '<BaseManager>.from_queryset', any QuerySet methods are
declared as attributes on the manager.
This allows us to properly lookup any QuerySet method types via a
'get_attribute_hook' and will thus remove disorienting phantom errors
occuring from mypy trying to resolve types only existing in the module
where the _original_ (and real) queryset method was declared.
* Fix `MyModel.objects.filter(...).my_method()`
* Fix regression: `MyModel.objects.filter(...).my_method()` no longer worked when using from_queryset
This also fixes the self-type of the copied-over methods of the manager generated by from_queryset.
Previously it was not parameterized by the model class, but used Any.
The handling of unbound types is not tested here as I have not been able to
find a way to create a test case for it. It has been manually tested
against an internal codebase.
* Remove unneeded defer.
* QuerySet.annotate returns self-type. Attribute access falls back to Any.
- QuerySets that have an annotated model do not report errors during .filter() when called with invalid fields.
- QuerySets that have an annotated model return ordinary dict rather than TypedDict for .values()
- QuerySets that have an annotated model return Any rather than typed Tuple for .values_list()
* Fix .annotate so it reuses existing annotated types. Fixes error in typechecking Django testsuite.
* Fix self-typecheck error
* Fix flake8
* Fix case of .values/.values_list before .annotate.
* Extra ignores for Django 2.2 tests (false positives due to tests assuming QuerySet.first() won't return None)
Fix mypy self-check.
* More tests + more precise typing in case annotate called before values_list.
Cleanup tests.
* Test and fix annotate in combination with values/values_list with no params.
* Remove line that does nothing :)
* Formatting fixes
* Address code review
* Fix quoting in tests after mypy changed things
* Use Final
* Use typing_extensions.Final
* Fixes after ValuesQuerySet -> _ValuesQuerySet refactor. Still not passing tests yet.
* Fix inheritance of _ValuesQuerySet and remove unneeded type ignores.
This allows the test
"annotate_values_or_values_list_before_or_after_annotate_broadens_type"
to pass.
* Make it possible to annotate user code with "annotated models", using PEP 583 Annotated type.
* Add docs
* Make QuerySet[_T] an external alias to _QuerySet[_T, _T].
This currently has the drawback that error messages display the internal type _QuerySet, with both type arguments.
See also discussion on #661 and #608.
Fixes#635: QuerySet methods on Managers (like .all()) now return QuerySets rather than Managers.
Address code review by @sobolevn.
* Support passing TypedDicts to WithAnnotations
* Add an example of an error to README regarding WithAnnotations + TypedDict.
* Fix runtime behavior of ValuesQuerySet alias (you can't extend Any, for example).
Fix some edge case with from_queryset after QuerySet changed to be an
alias to _QuerySet. Can't make a minimal test case as this only occurred
on a large internal codebase.
* Fix issue when using from_queryset in some cases when having an argument with a type annotation on the QuerySet.
The mypy docstring on anal_type says not to call defer() after it.