@@ -340,6 +340,60 @@ def assert_zero_count(model_type: type[models.Model]) -> None:
340
340
```
341
341
342
342
343
+ ### How to type a custom ` models.Field ` ?
344
+
345
+ > [ !NOTE]
346
+ > This require type generic support, see <a href =" #i-cannot-use-queryset-or-manager-with-type-annotations " >this section</a > to enable it.
347
+
348
+
349
+ Django ` models.Field ` (and subclasses) are generic types with two parameters:
350
+ - ` _ST ` : type that can be used when setting a value
351
+ - ` _GT ` : type that will be returned when getting a value
352
+
353
+ When you create a subclass, you have two options depending on how strict you want
354
+ the type to be for consumers of your custom field.
355
+
356
+ 1 . Generic subclass:
357
+
358
+ ``` python
359
+ from typing import TypeVar, reveal_type
360
+ from django.db import models
361
+
362
+ _ST = TypeVar(" _ST" , contravariant = True )
363
+ _GT = TypeVar(" _GT" , covariant = True )
364
+
365
+ class MyIntegerField (models .IntegerField[_ST , _GT ]):
366
+ ...
367
+
368
+ class User (models .Model ):
369
+ my_field = MyIntegerField()
370
+
371
+
372
+ reveal_type(User().my_field) # N: Revealed type is "int"
373
+ User().my_field = " 12" # OK (because Django IntegerField allows str and will try to coerce it)
374
+ ```
375
+
376
+ 2 . Non-generic subclass (more strict):
377
+
378
+ ``` python
379
+ from typing import reveal_type
380
+ from django.db import models
381
+
382
+ # This is a non-generic subclass being very explicit
383
+ # that it expects only int when setting values.
384
+ class MyStrictIntegerField (models .IntegerField[int , int ]):
385
+ ...
386
+
387
+ class User (models .Model ):
388
+ my_field = MyStrictIntegerField()
389
+
390
+
391
+ reveal_type(User().my_field) # N: Revealed type is "int"
392
+ User().my_field = " 12" # E: Incompatible types in assignment (expression has type "str", variable has type "int")
393
+ ```
394
+
395
+ See mypy section on [ generic classes subclasses] ( https://mypy.readthedocs.io/en/stable/generics.html#defining-subclasses-of-generic-classes ) .
396
+
343
397
## Related projects
344
398
345
399
- [ ` awesome-python-typing ` ] ( https://github.com/typeddjango/awesome-python-typing ) - Awesome list of all typing-related things in Python.
0 commit comments