mirror of
https://github.com/django/django.git
synced 2025-03-31 19:46:42 +00:00
Refs #26220 -- Added SingleObjectMixin._get_model().
This commit is contained in:
parent
43287cbb87
commit
9f716580c4
1
AUTHORS
1
AUTHORS
@ -233,6 +233,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Chris Wilson <chris+github@qwirx.com>
|
Chris Wilson <chris+github@qwirx.com>
|
||||||
Ciaran McCormick <ciaran@ciaranmccormick.com>
|
Ciaran McCormick <ciaran@ciaranmccormick.com>
|
||||||
Claude Paroz <claude@2xlibre.net>
|
Claude Paroz <claude@2xlibre.net>
|
||||||
|
Clifford Gama <cliffygamy@gmail.com>
|
||||||
Clint Ecker
|
Clint Ecker
|
||||||
colin@owlfish.com
|
colin@owlfish.com
|
||||||
Colin Wood <cwood06@gmail.com>
|
Colin Wood <cwood06@gmail.com>
|
||||||
|
@ -66,8 +66,8 @@ class SingleObjectMixin(ContextMixin):
|
|||||||
may not be called if get_object() is overridden.
|
may not be called if get_object() is overridden.
|
||||||
"""
|
"""
|
||||||
if self.queryset is None:
|
if self.queryset is None:
|
||||||
if self.model:
|
if (model := self._get_model()) is not None:
|
||||||
return self.model._default_manager.all()
|
return model._default_manager.all()
|
||||||
else:
|
else:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"%(cls)s is missing a QuerySet. Define "
|
"%(cls)s is missing a QuerySet. Define "
|
||||||
@ -76,6 +76,13 @@ class SingleObjectMixin(ContextMixin):
|
|||||||
)
|
)
|
||||||
return self.queryset.all()
|
return self.queryset.all()
|
||||||
|
|
||||||
|
def _get_model(self):
|
||||||
|
"""Return the model of the object the view is displaying."""
|
||||||
|
model = self.model
|
||||||
|
if model is None and getattr(self, "object", None) is not None:
|
||||||
|
return self.object.__class__
|
||||||
|
return model
|
||||||
|
|
||||||
def get_slug_field(self):
|
def get_slug_field(self):
|
||||||
"""Get the name of a slug field to be used to look up by slug."""
|
"""Get the name of a slug field to be used to look up by slug."""
|
||||||
return self.slug_field
|
return self.slug_field
|
||||||
@ -155,14 +162,14 @@ class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
|
|||||||
self.template_name_suffix,
|
self.template_name_suffix,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif getattr(self, "model", None) is not None and issubclass(
|
elif (model := getattr(self, "_get_model", lambda: None)()) and issubclass(
|
||||||
self.model, models.Model
|
model, models.Model
|
||||||
):
|
):
|
||||||
names.append(
|
names.append(
|
||||||
"%s/%s%s.html"
|
"%s/%s%s.html"
|
||||||
% (
|
% (
|
||||||
self.model._meta.app_label,
|
model._meta.app_label,
|
||||||
self.model._meta.model_name,
|
model._meta.model_name,
|
||||||
self.template_name_suffix,
|
self.template_name_suffix,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -87,18 +87,7 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
|
|||||||
if self.form_class:
|
if self.form_class:
|
||||||
return self.form_class
|
return self.form_class
|
||||||
else:
|
else:
|
||||||
if self.model is not None:
|
model = self._get_model() or self.get_queryset().model
|
||||||
# If a model has been explicitly provided, use it
|
|
||||||
model = self.model
|
|
||||||
elif getattr(self, "object", None) is not None:
|
|
||||||
# If this view is operating on a single object, use
|
|
||||||
# the class of that object
|
|
||||||
model = self.object.__class__
|
|
||||||
else:
|
|
||||||
# Try to get a queryset and extract the model class
|
|
||||||
# from that
|
|
||||||
model = self.get_queryset().model
|
|
||||||
|
|
||||||
if self.fields is None:
|
if self.fields is None:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
"Using ModelFormMixin (base class of %s) without "
|
"Using ModelFormMixin (base class of %s) without "
|
||||||
|
@ -226,7 +226,10 @@ Forms
|
|||||||
Generic Views
|
Generic Views
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
* ...
|
* The new :meth:`~django.views.generic.detail.SingleObjectMixin._get_model`
|
||||||
|
method returns the model defined for the view, or a custom model if
|
||||||
|
overridden. If ``queryset`` is provided, it takes precedence as the source
|
||||||
|
of objects.
|
||||||
|
|
||||||
Internationalization
|
Internationalization
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -219,3 +219,48 @@ class DetailViewTest(TestCase):
|
|||||||
res = self.client.get("/detail/nonmodel/1/")
|
res = self.client.get("/detail/nonmodel/1/")
|
||||||
self.assertEqual(res.status_code, 200)
|
self.assertEqual(res.status_code, 200)
|
||||||
self.assertEqual(res.context["object"].id, "non_model_1")
|
self.assertEqual(res.context["object"].id, "non_model_1")
|
||||||
|
|
||||||
|
def test_get_model_override(self):
|
||||||
|
res = self.client.get("/detail/author/getmodel/%s/" % self.author1.pk)
|
||||||
|
self.assertEqual(res.status_code, 200)
|
||||||
|
self.assertEqual(res.context["object"], self.author1)
|
||||||
|
self.assertTemplateUsed(res, "generic_views/author_detail.html")
|
||||||
|
|
||||||
|
def test_invalid_model_configuration(self):
|
||||||
|
msg = (
|
||||||
|
"MissingModelView is missing a QuerySet. Define MissingModelView.model, "
|
||||||
|
"MissingModelView.queryset, or override MissingModelView.get_queryset()."
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(ImproperlyConfigured, msg):
|
||||||
|
self.client.get("/missing-model/")
|
||||||
|
|
||||||
|
def test_conflicting_model_and_get_model(self):
|
||||||
|
"""
|
||||||
|
`_get_model` takes precedence over model.
|
||||||
|
"""
|
||||||
|
url = "/detail/conflicting-model-and-getmodel/%s/" % self.author1.pk
|
||||||
|
res = self.client.get(url)
|
||||||
|
self.assertEqual(res.status_code, 200)
|
||||||
|
self.assertEqual(res.context["object"], self.author1)
|
||||||
|
self.assertTemplateUsed(res, "generic_views/author_detail.html")
|
||||||
|
|
||||||
|
def test_model_and_queryset_ignores_model(self):
|
||||||
|
"""
|
||||||
|
Queryset takes precedence over model as source of objects.
|
||||||
|
"""
|
||||||
|
url = "/detail/author/conflicting-model-and-queryset/%s/" % self.author1.pk
|
||||||
|
res = self.client.get(url)
|
||||||
|
self.assertEqual(res.status_code, 200)
|
||||||
|
self.assertEqual(res.context["object"], self.author1)
|
||||||
|
self.assertTemplateUsed(res, "generic_views/author_detail.html")
|
||||||
|
|
||||||
|
def test_queryset_and_get_model_ignores_get_model(self):
|
||||||
|
"""
|
||||||
|
When queryset is defined, it takes precedence over `_get_model` as
|
||||||
|
source of model objects.
|
||||||
|
"""
|
||||||
|
url = "/detail/conflicting-queryset-and-getmodel/%s/" % self.artist1.pk
|
||||||
|
res = self.client.get(url)
|
||||||
|
self.assertEqual(res.status_code, 200)
|
||||||
|
self.assertEqual(res.context["object"], self.artist1)
|
||||||
|
self.assertTemplateUsed(res, "generic_views/artist_detail.html")
|
||||||
|
@ -5,7 +5,7 @@ from django.views.decorators.cache import cache_page
|
|||||||
from django.views.generic import TemplateView, dates
|
from django.views.generic import TemplateView, dates
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
from .models import Book
|
from .models import Artist, Book
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# TemplateView
|
# TemplateView
|
||||||
@ -75,6 +75,23 @@ urlpatterns = [
|
|||||||
path("detail/author/invalid/qs/", views.AuthorDetail.as_view(queryset=None)),
|
path("detail/author/invalid/qs/", views.AuthorDetail.as_view(queryset=None)),
|
||||||
path("detail/nonmodel/1/", views.NonModelDetail.as_view()),
|
path("detail/nonmodel/1/", views.NonModelDetail.as_view()),
|
||||||
path("detail/doesnotexist/<pk>/", views.ObjectDoesNotExistDetail.as_view()),
|
path("detail/doesnotexist/<pk>/", views.ObjectDoesNotExistDetail.as_view()),
|
||||||
|
path(
|
||||||
|
"detail/author/getmodel/<int:pk>/",
|
||||||
|
views.AuthorDetailOverridesGetModel.as_view(),
|
||||||
|
),
|
||||||
|
path("missing-model/", views.MissingModelView.as_view()),
|
||||||
|
path(
|
||||||
|
"detail/conflicting-model-and-getmodel/<int:pk>/",
|
||||||
|
views.AuthorDetailOverridesGetModel.as_view(model=Artist),
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"detail/author/conflicting-model-and-queryset/<int:pk>/",
|
||||||
|
views.AuthorDetailConflictingModelAndQueryset.as_view(),
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"detail/conflicting-queryset-and-getmodel/<int:pk>/",
|
||||||
|
views.AuthorDetailOverridesGetModel.as_view(queryset=Artist.objects.all()),
|
||||||
|
),
|
||||||
# FormView
|
# FormView
|
||||||
path("contact/", views.ContactView.as_view()),
|
path("contact/", views.ContactView.as_view()),
|
||||||
path("late-validation/", views.LateValidationView.as_view()),
|
path("late-validation/", views.LateValidationView.as_view()),
|
||||||
|
@ -43,6 +43,20 @@ class AuthorCustomDetail(generic.DetailView):
|
|||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorDetailOverridesGetModel(generic.DetailView):
|
||||||
|
def _get_model(self):
|
||||||
|
return Author
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorDetailConflictingModelAndQueryset(generic.DetailView):
|
||||||
|
queryset = Author.objects.all()
|
||||||
|
model = Artist
|
||||||
|
|
||||||
|
|
||||||
|
class MissingModelView(generic.DetailView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PageDetail(generic.DetailView):
|
class PageDetail(generic.DetailView):
|
||||||
queryset = Page.objects.all()
|
queryset = Page.objects.all()
|
||||||
template_name_field = "template"
|
template_name_field = "template"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user