Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/views/generic/detail.py: 32%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

79 statements  

1from django.core.exceptions import ImproperlyConfigured 

2from django.db import models 

3from django.http import Http404 

4from django.utils.translation import gettext as _ 

5from django.views.generic.base import ContextMixin, TemplateResponseMixin, View 

6 

7 

8class SingleObjectMixin(ContextMixin): 

9 """ 

10 Provide the ability to retrieve a single object for further manipulation. 

11 """ 

12 

13 model = None 

14 queryset = None 

15 slug_field = "slug" 

16 context_object_name = None 

17 slug_url_kwarg = "slug" 

18 pk_url_kwarg = "pk" 

19 query_pk_and_slug = False 

20 

21 def get_object(self, queryset=None): 

22 """ 

23 Return the object the view is displaying. 

24 

25 Require `self.queryset` and a `pk` or `slug` argument in the URLconf. 

26 Subclasses can override this to return any object. 

27 """ 

28 # Use a custom queryset if provided; this is required for subclasses 

29 # like DateDetailView 

30 if queryset is None: 

31 queryset = self.get_queryset() 

32 

33 # Next, try looking up by primary key. 

34 pk = self.kwargs.get(self.pk_url_kwarg) 

35 slug = self.kwargs.get(self.slug_url_kwarg) 

36 if pk is not None: 

37 queryset = queryset.filter(pk=pk) 

38 

39 # Next, try looking up by slug. 

40 if slug is not None and (pk is None or self.query_pk_and_slug): 

41 slug_field = self.get_slug_field() 

42 queryset = queryset.filter(**{slug_field: slug}) 

43 

44 # If none of those are defined, it's an error. 

45 if pk is None and slug is None: 

46 raise AttributeError( 

47 "Generic detail view %s must be called with either an object " 

48 "pk or a slug in the URLconf." % self.__class__.__name__ 

49 ) 

50 

51 try: 

52 # Get the single item from the filtered queryset 

53 obj = queryset.get() 

54 except queryset.model.DoesNotExist: 

55 raise Http404( 

56 _("No %(verbose_name)s found matching the query") 

57 % {"verbose_name": queryset.model._meta.verbose_name} 

58 ) 

59 return obj 

60 

61 def get_queryset(self): 

62 """ 

63 Return the `QuerySet` that will be used to look up the object. 

64 

65 This method is called by the default implementation of get_object() and 

66 may not be called if get_object() is overridden. 

67 """ 

68 if self.queryset is None: 

69 if self.model: 

70 return self.model._default_manager.all() 

71 else: 

72 raise ImproperlyConfigured( 

73 "%(cls)s is missing a QuerySet. Define " 

74 "%(cls)s.model, %(cls)s.queryset, or override " 

75 "%(cls)s.get_queryset()." % {"cls": self.__class__.__name__} 

76 ) 

77 return self.queryset.all() 

78 

79 def get_slug_field(self): 

80 """Get the name of a slug field to be used to look up by slug.""" 

81 return self.slug_field 

82 

83 def get_context_object_name(self, obj): 

84 """Get the name to use for the object.""" 

85 if self.context_object_name: 

86 return self.context_object_name 

87 elif isinstance(obj, models.Model): 

88 return obj._meta.model_name 

89 else: 

90 return None 

91 

92 def get_context_data(self, **kwargs): 

93 """Insert the single object into the context dict.""" 

94 context = {} 

95 if self.object: 

96 context["object"] = self.object 

97 context_object_name = self.get_context_object_name(self.object) 

98 if context_object_name: 

99 context[context_object_name] = self.object 

100 context.update(kwargs) 

101 return super().get_context_data(**context) 

102 

103 

104class BaseDetailView(SingleObjectMixin, View): 

105 """ 

106 Base view for displaying a single object. 

107 

108 This requires subclassing to provide a response mixin. 

109 """ 

110 

111 def get(self, request, *args, **kwargs): 

112 self.object = self.get_object() 

113 context = self.get_context_data(object=self.object) 

114 return self.render_to_response(context) 

115 

116 

117class SingleObjectTemplateResponseMixin(TemplateResponseMixin): 

118 template_name_field = None 

119 template_name_suffix = "_detail" 

120 

121 def get_template_names(self): 

122 """ 

123 Return a list of template names to be used for the request. May not be 

124 called if render_to_response() is overridden. Return a list containing 

125 ``template_name``, if set on the value. Otherwise, return a list 

126 containing: 

127 

128 * the contents of the ``template_name_field`` field on the 

129 object instance that the view is operating upon (if available) 

130 * ``<app_label>/<model_name><template_name_suffix>.html`` 

131 """ 

132 try: 

133 names = super().get_template_names() 

134 except ImproperlyConfigured: 

135 # If template_name isn't specified, it's not a problem -- 

136 # we just start with an empty list. 

137 names = [] 

138 

139 # If self.template_name_field is set, grab the value of the field 

140 # of that name from the object; this is the most specific template 

141 # name, if given. 

142 if self.object and self.template_name_field: 

143 name = getattr(self.object, self.template_name_field, None) 

144 if name: 

145 names.insert(0, name) 

146 

147 # The least-specific option is the default <app>/<model>_detail.html; 

148 # only use this if the object in question is a model. 

149 if isinstance(self.object, models.Model): 

150 object_meta = self.object._meta 

151 names.append( 

152 "%s/%s%s.html" 

153 % ( 

154 object_meta.app_label, 

155 object_meta.model_name, 

156 self.template_name_suffix, 

157 ) 

158 ) 

159 elif getattr(self, "model", None) is not None and issubclass( 

160 self.model, models.Model 

161 ): 

162 names.append( 

163 "%s/%s%s.html" 

164 % ( 

165 self.model._meta.app_label, 

166 self.model._meta.model_name, 

167 self.template_name_suffix, 

168 ) 

169 ) 

170 

171 # If we still haven't managed to find any template names, we should 

172 # re-raise the ImproperlyConfigured to alert the user. 

173 if not names: 

174 raise ImproperlyConfigured( 

175 "SingleObjectTemplateResponseMixin requires a definition " 

176 "of 'template_name', 'template_name_field', or 'model'; " 

177 "or an implementation of 'get_template_names()'." 

178 ) 

179 

180 return names 

181 

182 

183class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView): 

184 """ 

185 Render a "detail" view of an object. 

186 

187 By default this is a model instance looked up from `self.queryset`, but the 

188 view will support display of *any* object by overriding `self.get_object()`. 

189 """