From 1a29ad4f974915301ce3fbaa33dde71d9bd5d1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigurd=20Lj=C3=B8dal?= <544451+ljodal@users.noreply.github.com> Date: Sat, 25 Jun 2022 13:37:25 +0200 Subject: [PATCH] Fix nullability of blank charfields in values and values_list (#1020) 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. --- mypy_django_plugin/django/context.py | 3 +++ .../managers/querysets/test_values.yml | 18 ++++++++++++++++++ .../managers/querysets/test_values_list.yml | 16 ++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/mypy_django_plugin/django/context.py b/mypy_django_plugin/django/context.py index 0d11564..2a425a6 100644 --- a/mypy_django_plugin/django/context.py +++ b/mypy_django_plugin/django/context.py @@ -263,6 +263,9 @@ class DjangoContext: return attname def get_field_nullability(self, field: Union[Field, ForeignObjectRel], method: Optional[str]) -> bool: + if method in ("values", "values_list"): + return field.null + nullable = field.null if not nullable and isinstance(field, CharField) and field.blank: return True diff --git a/tests/typecheck/managers/querysets/test_values.yml b/tests/typecheck/managers/querysets/test_values.yml index dc2e762..d893a5b 100644 --- a/tests/typecheck/managers/querysets/test_values.yml +++ b/tests/typecheck/managers/querysets/test_values.yml @@ -124,3 +124,21 @@ pass class Book(models.Model): authors = models.ManyToManyField(Author, related_name='books') + +- case: queryset_values_blank_charfield + main: | + from myapp.models import Blog + values = Blog.objects.values('text').get() + reveal_type(values) # N: Revealed type is "TypedDict({'text': builtins.str})" + reveal_type(values["text"]) # N: Revealed type is "builtins.str" + installed_apps: + - myapp + files: + - path: myapp/__init__.py + - path: myapp/models.py + content: | + from django.db import models + + class Blog(models.Model): + num_posts = models.IntegerField() + text = models.CharField(max_length=100, blank=True) diff --git a/tests/typecheck/managers/querysets/test_values_list.yml b/tests/typecheck/managers/querysets/test_values_list.yml index aadc9fe..a2026e2 100644 --- a/tests/typecheck/managers/querysets/test_values_list.yml +++ b/tests/typecheck/managers/querysets/test_values_list.yml @@ -265,3 +265,19 @@ pass class Book(models.Model): authors = models.ManyToManyField(Author, related_name='books') +- case: queryset_values_list_blank_charfield + main: | + from myapp.models import Blog + values = Blog.objects.values_list('text').get() + reveal_type(values) # N: Revealed type is "Tuple[builtins.str]" + installed_apps: + - myapp + files: + - path: myapp/__init__.py + - path: myapp/models.py + content: | + from django.db import models + + class Blog(models.Model): + num_posts = models.IntegerField() + text = models.CharField(max_length=100, blank=True)