mirror of
https://github.com/django/django.git
synced 2025-03-29 02:30:48 +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>
|
||||
Ciaran McCormick <ciaran@ciaranmccormick.com>
|
||||
Claude Paroz <claude@2xlibre.net>
|
||||
Clifford Gama <cliffygamy@gmail.com>
|
||||
Clint Ecker
|
||||
colin@owlfish.com
|
||||
Colin Wood <cwood06@gmail.com>
|
||||
|
@ -66,8 +66,8 @@ class SingleObjectMixin(ContextMixin):
|
||||
may not be called if get_object() is overridden.
|
||||
"""
|
||||
if self.queryset is None:
|
||||
if self.model:
|
||||
return self.model._default_manager.all()
|
||||
if (model := self._get_model()) is not None:
|
||||
return model._default_manager.all()
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
"%(cls)s is missing a QuerySet. Define "
|
||||
@ -76,6 +76,13 @@ class SingleObjectMixin(ContextMixin):
|
||||
)
|
||||
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):
|
||||
"""Get the name of a slug field to be used to look up by slug."""
|
||||
return self.slug_field
|
||||
@ -155,14 +162,14 @@ class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
|
||||
self.template_name_suffix,
|
||||
)
|
||||
)
|
||||
elif getattr(self, "model", None) is not None and issubclass(
|
||||
self.model, models.Model
|
||||
elif (model := getattr(self, "_get_model", lambda: None)()) and issubclass(
|
||||
model, models.Model
|
||||
):
|
||||
names.append(
|
||||
"%s/%s%s.html"
|
||||
% (
|
||||
self.model._meta.app_label,
|
||||
self.model._meta.model_name,
|
||||
model._meta.app_label,
|
||||
model._meta.model_name,
|
||||
self.template_name_suffix,
|
||||
)
|
||||
)
|
||||
|
@ -87,18 +87,7 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
|
||||
if self.form_class:
|
||||
return self.form_class
|
||||
else:
|
||||
if self.model is not None:
|
||||
# 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
|
||||
|
||||
model = self._get_model() or self.get_queryset().model
|
||||
if self.fields is None:
|
||||
raise ImproperlyConfigured(
|
||||
"Using ModelFormMixin (base class of %s) without "
|
||||
|
@ -226,7 +226,10 @@ Forms
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -219,3 +219,48 @@ class DetailViewTest(TestCase):
|
||||
res = self.client.get("/detail/nonmodel/1/")
|
||||
self.assertEqual(res.status_code, 200)
|
||||
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 . import views
|
||||
from .models import Book
|
||||
from .models import Artist, Book
|
||||
|
||||
urlpatterns = [
|
||||
# TemplateView
|
||||
@ -75,6 +75,23 @@ urlpatterns = [
|
||||
path("detail/author/invalid/qs/", views.AuthorDetail.as_view(queryset=None)),
|
||||
path("detail/nonmodel/1/", views.NonModelDetail.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
|
||||
path("contact/", views.ContactView.as_view()),
|
||||
path("late-validation/", views.LateValidationView.as_view()),
|
||||
|
@ -43,6 +43,20 @@ class AuthorCustomDetail(generic.DetailView):
|
||||
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):
|
||||
queryset = Page.objects.all()
|
||||
template_name_field = "template"
|
||||
|
Loading…
x
Reference in New Issue
Block a user