1from plain.exceptions import ImproperlyConfigured, ObjectDoesNotExist
2from plain.forms import Form
3from plain.http import Http404, Response
4
5from .forms import FormView
6from .templates import TemplateView
7
8
9class ObjectTemplateViewMixin:
10 context_object_name = ""
11
12 def get(self) -> Response:
13 self.load_object()
14 return self.render_template()
15
16 def load_object(self) -> None:
17 try:
18 self.object = self.get_object()
19 except ObjectDoesNotExist:
20 raise Http404
21
22 if not self.object:
23 # Also raise 404 if the object is None
24 raise Http404
25
26 def get_object(self): # Intentionally untyped... subclasses must override this.
27 raise NotImplementedError(
28 f"get_object() is not implemented on {self.__class__.__name__}"
29 )
30
31 def get_template_context(self) -> dict:
32 """Insert the single object into the context dict."""
33 context = super().get_template_context() # type: ignore
34 context["object"] = (
35 self.object
36 ) # Some templates can benefit by always knowing a primary "object" can be present
37 if self.context_object_name:
38 context[self.context_object_name] = self.object
39 return context
40
41
42class DetailView(ObjectTemplateViewMixin, TemplateView):
43 """
44 Render a "detail" view of an object.
45
46 By default this is a model instance looked up from `self.queryset`, but the
47 view will support display of *any* object by overriding `self.get_object()`.
48 """
49
50 pass
51
52
53class CreateView(ObjectTemplateViewMixin, FormView):
54 """
55 View for creating a new object, with a response rendered by a template.
56 """
57
58 def post(self) -> Response:
59 """
60 Handle POST requests: instantiate a form instance with the passed
61 POST variables and then check if it's valid.
62 """
63 # Context expects self.object to exist
64 self.load_object()
65 return super().post()
66
67 def load_object(self) -> None:
68 self.object = None
69
70 # TODO? would rather you have to specify this...
71 def get_success_url(self, form):
72 """Return the URL to redirect to after processing a valid form."""
73 if self.success_url:
74 url = self.success_url.format(**self.object.__dict__)
75 else:
76 try:
77 url = self.object.get_absolute_url()
78 except AttributeError:
79 raise ImproperlyConfigured(
80 "No URL to redirect to. Either provide a url or define"
81 " a get_absolute_url method on the Model."
82 )
83 return url
84
85 def form_valid(self, form):
86 """If the form is valid, save the associated model."""
87 self.object = form.save()
88 return super().form_valid(form)
89
90
91class UpdateView(ObjectTemplateViewMixin, FormView):
92 """View for updating an object, with a response rendered by a template."""
93
94 def post(self) -> Response:
95 """
96 Handle POST requests: instantiate a form instance with the passed
97 POST variables and then check if it's valid.
98 """
99 self.load_object()
100 return super().post()
101
102 def get_success_url(self, form):
103 """Return the URL to redirect to after processing a valid form."""
104 if self.success_url:
105 url = self.success_url.format(**self.object.__dict__)
106 else:
107 try:
108 url = self.object.get_absolute_url()
109 except AttributeError:
110 raise ImproperlyConfigured(
111 "No URL to redirect to. Either provide a url or define"
112 " a get_absolute_url method on the Model."
113 )
114 return url
115
116 def form_valid(self, form):
117 """If the form is valid, save the associated model."""
118 self.object = form.save()
119 return super().form_valid(form)
120
121 def get_form_kwargs(self):
122 """Return the keyword arguments for instantiating the form."""
123 kwargs = super().get_form_kwargs()
124 kwargs.update({"instance": self.object})
125 return kwargs
126
127
128class DeleteView(ObjectTemplateViewMixin, FormView):
129 """
130 View for deleting an object retrieved with self.get_object(), with a
131 response rendered by a template.
132 """
133
134 class EmptyDeleteForm(Form):
135 def __init__(self, instance, *args, **kwargs):
136 self.instance = instance
137 super().__init__(*args, **kwargs)
138
139 def save(self):
140 self.instance.delete()
141
142 form_class = EmptyDeleteForm
143
144 def post(self) -> Response:
145 """
146 Handle POST requests: instantiate a form instance with the passed
147 POST variables and then check if it's valid.
148 """
149 self.load_object()
150 return super().post()
151
152 def get_form_kwargs(self):
153 """Return the keyword arguments for instantiating the form."""
154 kwargs = super().get_form_kwargs()
155 kwargs.update({"instance": self.object})
156 return kwargs
157
158 def form_valid(self, form):
159 """If the form is valid, save the associated model."""
160 form.save()
161 return super().form_valid(form)
162
163
164class ListView(TemplateView):
165 """
166 Render some list of objects, set by `self.get_queryset()`, with a response
167 rendered by a template.
168 """
169
170 context_object_name = ""
171
172 def get(self) -> Response:
173 self.objects = self.get_objects()
174 return super().get()
175
176 def get_objects(self):
177 raise NotImplementedError(
178 f"get_objects() is not implemented on {self.__class__.__name__}"
179 )
180
181 def get_template_context(self) -> dict:
182 """Insert the single object into the context dict."""
183 context = super().get_template_context() # type: ignore
184 context["objects"] = self.objects
185 if self.context_object_name:
186 context[self.context_object_name] = self.objects
187 return context