Newer
Older
__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)"
__license__ = "AGPL v3"
from typing import Any, Dict
from django.contrib import messages
from django.db.models.query import QuerySet
from django.forms.forms import BaseForm
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.html import format_html
from django.views import View
from django.views.generic import FormView, ListView
from django.views.generic.detail import SingleObjectMixin
from .forms import HTMXInlineCRUDModelForm
def empty(request):
return HttpResponse("")
class HTMXInlineCRUDModelFormView(FormView):
template_name = "htmx/htmx_inline_crud_form.html"
form_class = HTMXInlineCRUDModelForm
instance_li_template_name = None
target_element_id = "htmx-crud-{instance_type}-{instance_id}"
edit = False
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.instance_type = self.form_class.Meta.model.__name__.lower()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["target_element_id"] = self.get_target_element_id()
context["instance_li_template_name"] = self.instance_li_template_name
context["instance_type"] = self.instance_type
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
context[self.instance_type] = context["instance"] = self.instance
return context
def post(self, request, *args, **kwargs):
self.instance = get_object_or_404(self.form_class.Meta.model, pk=kwargs["pk"])
self.edit = True
return super().post(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
self.instance = get_object_or_404(self.form_class.Meta.model, pk=kwargs["pk"])
self.edit = bool(request.GET.get("edit", None))
super().get(request, *args, **kwargs)
return render(request, self.template_name, self.get_context_data(**kwargs))
def delete(self, request, *args, **kwargs):
self.instance = get_object_or_404(self.form_class.Meta.model, pk=kwargs["pk"])
self.instance.delete()
messages.success(
self.request, f"{self.instance_type.title()} deleted successfully"
)
return empty(request)
def get_form(self) -> BaseForm:
if self.request.method == "GET" and not self.edit:
return None
return super().get_form()
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({"instance": self.instance})
return kwargs
def get_target_element_id(self) -> str:
return self.target_element_id.format(
instance_type=self.instance_type,
instance_id=self.instance.id,
)
def get_success_url(self) -> str:
return self.get_context_data()["view"].request.path
def form_valid(self, form: BaseForm) -> HttpResponse:
form.save()
messages.success(
self.request, f"{self.instance_type.title()} saved successfully"
)
return super().form_valid(form)
class HTMXInlineCRUDModelListView(ListView):
template_name = "htmx/htmx_inline_crud_list.html"
add_form_class = None
model = None
model_form_view_url = None
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self.instance_type = self.model.__name__.lower()
def _append_model_form_view_url(self, queryset: QuerySet, **kwargs) -> QuerySet:
kwargs.update({"pk": object.pk})
self.model_form_view_url, kwargs=kwargs
return queryset
def post(self, request, *args, **kwargs):
"""
Post requests to the list view are treated as new object creation requests.
"""
if self.add_form_class is None:
return empty(request)
add_form = self.add_form_class(request.POST or None, **kwargs)
object = add_form.save()
kwargs.update({"pk": object.pk})
messages.success(self.request, f"{self.instance_type.title()} successfully")
return redirect(reverse(self.model_form_view_url, kwargs=kwargs))
else:
response = TemplateResponse(
request,
"htmx/htmx_inline_crud_new_form.html",
{
"list_url": request.path,
"add_form": add_form,
"instance_type": self.instance_type,
},
)
# Modify headers to swap in place with "HX-Reswap": "outerHTML"
# This will avoid duplication of the form if errors are present
response["HX-Reswap"] = "outerHTML"
return response
def get_context_data(self, **kwargs: Any):
context = super().get_context_data(**kwargs)
context["list_url"] = self.request.path
context["instance_type"] = self.instance_type
return context
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
class HXDynselSelectOptionView(SingleObjectMixin, View):
def get(self, request):
obj = self.get_object()
return HttpResponse(
format_html(
'<option value="{}" selected>{}</option>',
obj.pk,
str(obj),
)
)
def get_object(self):
queryset = self.model.objects.all()
pk = self.request.GET.get("pk")
return get_object_or_404(queryset, pk=pk)
class HXDynselResultPage(View):
model = None
collection_name = "results"
template_name = "htmx/dynsel_list_page.html"
paginate_by = 16
obj_select_option_url = None
def post(self, request):
self.page_nr = request.GET.get("page")
self.q = request.POST.get("q", "")
context = self.get_context_data()
return self.render_to_response(context)
def get_page_obj(self, page_nr):
paginator = Paginator(self.get_queryset(), self.paginate_by)
page_obj = paginator.get_page(page_nr)
return page_obj
def get_queryset(self):
result = self.search(
self.model.objects.all(),
self.q,
)
return result
def render_to_response(self, context):
return TemplateResponse(
self.request,
self.template_name,
context,
)
def search(self, queryset, q):
return queryset
def get_context_data(self, **kwargs):
context = {}
context["collection_name"] = self.collection_name
context["obj_select_option_url"] = self.obj_select_option_url
context["model_name"] = self.model._meta.verbose_name_plural
context["q"] = self.q
context["page_obj"] = self.get_page_obj(self.page_nr)
return context
# def _hx_dynsel_organization_page(request):
# model = Organization
# queryset = model.objects.all()
# collection_name = "organizations-list"
# if q := request.POST.get("q", ""):
# queryset = queryset.filter(
# Q(name__unaccent__icontains=q)
# | Q(name_original__unaccent__icontains=q)
# | Q(acronym__unaccent__icontains=q)
# | Q(ror_json__names__contains=[{"value": q}]) # Search ROR
# )
# paginator = Paginator(queryset, 50)
# page_nr = request.GET.get("page")
# page_obj = paginator.get_page(page_nr)
# context = {
# "page_obj": page_obj,
# "q": q,
# "model_name": model._meta.verbose_name_plural,
# "collection_name": collection_name,
# "obj_select_option_url": reverse(
# "organizations:organization-hx-dynsel-select-option"
# ),
# }
# return TemplateResponse(
# request,
# "organizations/_hx_dynsel_organization_page.html",
# context,
# )