Created
August 12, 2021 01:52
-
-
Save MattSegal/912069f629ea59414727d9c1366de7a6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.forms import ModelForm | |
from django.template.loader import render_to_string | |
from django.shortcuts import render | |
from django.http import Http404 | |
from django.contrib import messages | |
class DynamicTableForm(ModelForm): | |
""" | |
Renders a table which can be optionally edited by a user using htmx. | |
Updates the provided instance when the form is submitted. | |
Suggested usage within a view: | |
DYNAMIC_FORMS = { | |
"foo": BarForm, | |
"bar": FooForm, | |
} | |
def thing_view(request, pk, form_slug: str = ""): | |
# ... | |
forms = DynamicTableForm.build_forms(request, form_slug, client, DYNAMIC_FORMS) | |
context = {"whatever": 1, "forms": forms} | |
form_resp = DynamicTableForm.get_response(request, form_slug, forms, context) | |
if form_resp: | |
return form_resp | |
# ... | |
Needs to be hooked up in urls.py as follows: | |
urlpatterns = [ | |
# ... | |
# Thing | |
path("thing/<int:pk>/", views.thing.thing_view, name="thing-detail"), | |
path( | |
"thing/<int:pk>/<str:form_slug>/", | |
views.thing.thing_view, | |
name="thing-detail-form", | |
), | |
# ... | |
] | |
""" | |
template = "case/snippets/_dynamic_table_form.html" | |
def __init__(self, slug: str, *args, editable: bool = False, **kwargs): | |
super().__init__(*args, **kwargs) | |
self.editable = editable | |
self.slug = slug | |
# Build display value for fields | |
for bound_field in self: | |
if hasattr(bound_field.field, "display_value"): | |
bound_field.display_value = bound_field.field.display_value(bound_field) | |
else: | |
bound_field.display_value = bound_field.value() | |
def render_to_string(self): | |
context = {"form": self} | |
return render_to_string(self.template, context) | |
def render_to_response(self, request, context): | |
context = {**context, "form": self} | |
return render(request, self.template, context) | |
def __str__(self): | |
return self.render_to_string() | |
@staticmethod | |
def build_forms(request, slug: str, instance, view_forms: dict) -> dict: | |
form_instances = {} | |
for form_slug, form_cls in view_forms.items(): | |
form_kwargs = dict(instance=instance, slug=form_slug, editable=False) | |
if request.method == "POST" and form_slug == slug: | |
form_kwargs["data"] = request.POST | |
form_instances[form_slug] = form_cls(**form_kwargs) | |
return form_instances | |
@staticmethod | |
def get_response(request, slug: str, forms: dict, context: dict): | |
if not slug: | |
return | |
form = forms.get(slug) | |
if not form: | |
raise Http404() | |
ctx = {**context, "slug": slug} | |
if request.method == "GET": | |
is_editable = request.GET.get("edit") | |
form.editable = is_editable | |
return form.render_to_response(request, ctx) | |
elif request.method == "POST": | |
if form.is_valid(): | |
form.save() | |
messages.success(request, "Edit success") | |
else: | |
form.editable = True | |
return form.render_to_response(request, ctx) | |
else: | |
raise Http404() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment