django-types icon indicating copy to clipboard operation
django-types copied to clipboard

Nullable model fields have type error when trying to set None as value

Open dbushy727 opened this issue 2 years ago • 4 comments

Thank you so much for this package. It really has helped bring so much more visibility into my codebase. 🙏

One thing I'm struggling with is setting the right type when I have a nullable Model field. I find that when i look at an instance of the model, it shows me the value or none, but when trying to set None, i get a type error.

Django version: 3.2.10 Python version: 3.10.1


class MyModel(model.Model):
    # hover on variable shows DecimalField[Decimal]
    total_cost: Optional[models.DecimalField[Decimal]] = models.DecimalField(max_digits=12, decimal_places=2, null=True) 

my_model_instance = MyModel.objects.first()
# hover shows Decimal | None (which is great!)
my_model_instance.total_cost 

# hover shows error Cannot assign member "total_cost" for type "MyModel"  
# "None" is incompatible with "Decimal | Combinable"
my_model.total_cost = None 

Ive tried playing with the annotation and I can't seem to get it to work. I think this may be a bug in how the generic handles None.

class MyModel(model.Model):
    # hover on expression shows Expression of type "DecimalField[Decimal]" cannot be assigned to declared type 
    # "DecimalField[Decimal | None]"
    #  TypeVar "_DEC@DecimalField" is invariant
    #    Type "Decimal" cannot be assigned to type "Decimal | None"
    #      Type cannot be assigned to type "None"
    total_cost: models.DecimalField[Optional[Decimal]] = models.DecimalField()

When looking at the stub file for DecimalField, I noticed that i see a type error for the null field. Screen Shot 2022-04-22 at 2 45 12 PM

I think the Literal is the problem and if we use the one from typings it works for me, but im not sure if there are downstream effects.

I'll submit a PR to showcase what im talking about. Please let me know if this is something im doing wrong, or something we can address to fix. Any help would be super appreciated, thanks!

dbushy727 avatar Apr 22 '22 20:04 dbushy727

That's really weird pylance is erroring about Literal, I think we have some test cases that use that functionality

sbdchd avatar Aug 03 '22 22:08 sbdchd

Locally I've tried importing Literal from typing rather than typing_extensions (python 3.9) and that seems to improve things. I think maybe pyright isn't treating typing_extensions.Literal correctly any more?

sminnee avatar Sep 13 '22 01:09 sminnee

I've noted this on the pyright project here: https://github.com/microsoft/pyright/issues/3936

sminnee avatar Sep 13 '22 02:09 sminnee

Never mind, this was a bug in my environment.

sminnee avatar Sep 13 '22 02:09 sminnee