Last active
May 8, 2022 15:36
-
-
Save craiglabenz/06d455460bb21bb6562ac301eb04f365 to your computer and use it in GitHub Desktop.
Demonstrates how I connect Models, Managers, and QuerySets, with explanations and open questions for improvement
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db import models | |
class MyModelManager(models.Manager): | |
# A few notes here: | |
# | |
# 1. I add this method for its type-hinted return type. This allows me to | |
# later write "MyModel.objects.for_user(user=user).some_other_queryset_method()" | |
# and have the linter agree with me that it will work. | |
# 2. Ultimately, I call `super().the_mirrored_method()` (in this case, | |
# obviously, `for_user`) because Django has already done the mirroring. | |
# Thus, it is safe to call super() -- the `for_user` method is waiting | |
# for me there. | |
# 3. You'll see an awkward `type: ignore` annotation on the return | |
# statement. That is terrible and I hate it, but necessary, because | |
# I haven't found another good way to convince MyPy (and by extension, | |
# Pylance) that the Manager method returns QuerySet instances. If I | |
# didn't need that `# type: ignore` comment, then I wouldn't need this | |
# whole method! | |
def for_user(self, user: "auth.User") -> "MyModelQuerySet": | |
return super().for_user(user) # type: ignore | |
class MyModelQuerySet(models.QuerySet): | |
def for_user(self, user: "auth.User") -> "MyModelQuerySet": | |
# Example pseudocode. Filters here vary, obviously. | |
return self.filter(user=user) | |
class MyModel(models.Model): | |
user = models.ForeignKey("auth.User") | |
# more fields | |
# This is my recommended way to set up a Manager. I always create my | |
# own Manager, even if I can't think of a reason I need it now, because | |
# I usually will eventually and it's nice to have already exist. | |
# And, setting it up this way correctly wires up the connection between | |
# a Manager and its QuerySet, including all the method mirroring from | |
# QuerySet to Manager. | |
objects: MyModelManager = MyModelManager.from_queryset(MyModelQuerySet)() | |
# With the above, in View code elsewhere, I can write this: | |
# MyModelManager.objects.for_user(user=request.user) | |
# and everyone agrees that the code will work :) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment