There is no universal answer to that question, and the only reputable source you will find about it is Django's reference and documentation, which don't make any mention about annotations for migrations.
The "correct way to handle type hinting in Django" strictly depends on your company's policy. Prior to asking how, you must ask why. Once you know why, figuring out how will likely be straightforward. However, if instead of asking "why?", you (or your boss) ask "why not?", that's taking the issue upside down. You need a reason to do something, but you don't need a reason not to do something. (1)
If the reason is "to pass CI", this is no valid reason. CI is meant to work for you, not the opposite.
If the reason is "to document the code", this is no valid reason either. Type hints are documentation, the purpose of writing documentation is not "to document the code". Why is any documentation needed? Who is going to read this documentation? What information do they need that is not already clear in the code?
I see 3 valid reasons to use type hints:
- Provide help for developers using the functions. In the case of migrations,
forwards
and backwards
functions are only used internally by Django. Developers reading your code are expected to have knowledge about migrations. If they don't know what apps
and schema_editor
are, it seems fair to expect them to refer to migrations.RunPython
documentation, rather than re-explaining it for every single backwards
and forwards
callbacks of every single RunPython
operation of every single migration.
- QA type checking. But type checking is only useful for your own code. You'll never import the functions from migrations to use them in your code, so there is nothing to check.
- Provide meta information for some smart tools (internal or third party) using introspection to do magic stuff. An excellent example of this use case is FastAPI, their use of annotations is highly advanced and a powerful part of their API.
Unless your case fits in one of these three, or you have any other reason to use type hints, don't try to fit to "policies" blindly. Don't write annotations just because you can. Keep calm and (re)read the Zen of Python. :) (2)
TLDR: Ask your boss to tell you why he wants type hints for migrations. If the answer is a well-defined purpose, write hints to serve that purpose. If the answer is anyhow stupid, write stupid type hints. ¯\(ツ)/¯ (3)
Footnotes
(1) The double negation might be confusing. What I mean here is that if someone can't explain the purpose of doing something, you don't need to find the purpose of not doing it. Or at least, there is a very straightforward purpose: saving the time and resources required to do it and maintain it.
(2) Don't try to find any principle specific to this situation in the Zen of Python. I only meant this reference as a general reminder of Python's philosophy: keep things simple, clear explicit code is worth more than poor paraphrasing documentation, that kind of stuff.
(3) If the purpose is "to pass CI" or such, I'd personally recommend something silly like def forwards(apps: ..., schema_editor: ...) -> ...
(I mean, literally annotate with the Ellipsis object). Either nobody notice and it was indeed pointless, either your boss or your colleagues throw it back at you and hopefully come with a better defined purpose.