Howdy!
Now that we have CompositePrimaryKey
(thank you !) I wanted to make a proposal to add native support in
SingleObjectMixin
. Currently you can use generic views with CPK models by overriding get_object
to allow filtering by the two or more path kwargs from the url which correspond to the CPK (assuming you don’t need request information).
If you have many models with CompositePrimaryKey
s this can be a lot of boilerplate.
The proposed solution is a general mechanism using a new attribute queryset_filter_kwargs
, which would be a mapping of path_kwarg
and field_name
.
# models.py
class Story(models.Model):
slug = models.SlugField(primary_key=True)
name = models.CharField()
class Chapter(models.Model):
pk = models.CompositePrimaryKey("story_id", "number")
story = models.ForeignKey(
"Story", on_delete=models.CASCADE, related_name="chapters"
)
number = models.PositiveIntegerField()
title = models.CharField()
body = models.TextField(blank=True)
# views.py
class ChapterDetailView(DetailView):
model = Chapter
queryset_filter_kwargs = {
"story_slug": "story_id", "chapter_number": "number",
}
# urls.py
urlpatterns = [
path(
"<slug:story_slug>/chapter/<int:chapter_number>/", ChapterDetailView.as_view(),
),
]
Why queryset_filter_kwargs
?
This is designed as a general attribute rather than something CPK-specific. While brainstorming possible solutions, I realized the problem was fundamentally about enabling queryset filtering based on multiple url_kwargs
/fields—something I’ve needed to handle frequently.
Also, I couldn’t come up with a good cpk-specific name (considered allowing
pk_url_kwarg
to be a tuple, but also rejected).
By adopting this approach, we can eliminate the need for redundant get_queryset()
and get_object()
overrides, limiting such overrides to cases involving more complex queries or request-specific logic.
In addition, it could potentially also carry the work of (pk|slug)_url_kwarg
and (pk|slug)_field
, which seem slated for some change/removal? (#22724 (Improve SingleObjectMixin) – Django, #21898 (SingleObjectMixin should not require slug or pk if queryset is given) – Django).
Internally, the implementation could look something like:
# in get_queryset or get_object
cpk = {
field_name: self.kwargs.get(path_kwarg)
for path_kwarg, field_name in self.queryset_filter_kwargs.items()
}
queryset = queryset.filter(**cpk)
Would love your thoughts, feedback, or ideas for improvement. Does this sound like a valuable addition to Django?