From 0234a5e1c5690b579eeab7872e76bc1945088068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Sat, 12 Mar 2022 13:55:08 +0100 Subject: [PATCH 1/6] add UnicornFormMixin lets Unicorn handle forms easier --- django_unicorn/components/mixins.py | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/django_unicorn/components/mixins.py b/django_unicorn/components/mixins.py index 63572063..e7043d6b 100644 --- a/django_unicorn/components/mixins.py +++ b/django_unicorn/components/mixins.py @@ -1,3 +1,4 @@ +from django import forms from django_unicorn.serializer import model_value @@ -9,3 +10,66 @@ class ModelValueMixin: def value(self, *fields): return model_value(self, *fields) + + +class UnicornFormMixin(forms.FormMixin): + """Mixin for UnicornView to add Forms functionality.""" + + def __init__(self, **kwargs): + if self.success_url is not None: + # FIXME Use AttributeError instead, but AttributeErrors are catched by Unicorn and it overrides it with a generic message + raise AttributeError( + f"You may not use a success URL attribute with Unicorn's {self.__class__.__name__}." + ) + if self.initial: + raise AttributeError( + f"Do not use the 'initial' attr for setting initial data in a component. Set attributes directly in the component class '{self.__class__.__name__}' instead ." + ) + + # set the classes Unicorn attrs dynamically, + for field_name, field in self.form_class.base_fields.items(): # type: str,Field + # don't override existing "initial" attrs + if not hasattr(self, field_name): + setattr(self.__class__, field_name, "") + + # FIXME: Unicorn attr should be made configurable by the user, and maybe per field? + if isinstance(field, forms.BooleanField): + unicorn_attr = "unicorn:model" + else: + unicorn_attr = "unicorn:model.lazy" + field.widget.attrs.update({unicorn_attr: field_name}) + + super().__init__(**kwargs) + + def get_initial(self): + return self._attributes() + + def get_success_url(self): + return "" + + def form_valid(self, form): + # TODO is there anything to do here? + # At least a HttpResponseRedirect to success_url is wrong, like in FormMixin + pass + + def form_invalid(self, form): + # TODO is there anything to do here? + pass + + # from django.forms.FormMixin: + # def get_form_kwargs(self): + # """Return the keyword arguments for instantiating the form.""" + # kwargs = super().get_form_kwargs() + # # kwargs = { + # # # "initial": self.get_initial(), + # # # "prefix": self.get_prefix(), + # # } + # + # if self.request.method in ("POST", "PUT"): + # kwargs.update( + # { + # "data": self.request.POST, + # "files": self.request.FILES, + # } + # ) + # return kwargs From 93dd72a8b3fd8c7120a56672d935312c7997c204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Sat, 12 Mar 2022 14:05:44 +0100 Subject: [PATCH 2/6] fix wrong FormMixin import --- django_unicorn/components/mixins.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/django_unicorn/components/mixins.py b/django_unicorn/components/mixins.py index e7043d6b..6d73e3b5 100644 --- a/django_unicorn/components/mixins.py +++ b/django_unicorn/components/mixins.py @@ -1,4 +1,6 @@ from django import forms +from django.views.generic.edit import FormMixin + from django_unicorn.serializer import model_value @@ -12,7 +14,7 @@ def value(self, *fields): return model_value(self, *fields) -class UnicornFormMixin(forms.FormMixin): +class UnicornFormMixin(FormMixin): """Mixin for UnicornView to add Forms functionality.""" def __init__(self, **kwargs): From 3739abc3bc1d729f18602ffbad395b2d07e7eb62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Sat, 12 Mar 2022 16:26:49 +0100 Subject: [PATCH 3/6] better annotations --- django_unicorn/components/mixins.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/django_unicorn/components/mixins.py b/django_unicorn/components/mixins.py index 6d73e3b5..068f2f62 100644 --- a/django_unicorn/components/mixins.py +++ b/django_unicorn/components/mixins.py @@ -1,4 +1,5 @@ from django import forms +from django.forms import Field from django.views.generic.edit import FormMixin from django_unicorn.serializer import model_value @@ -29,7 +30,10 @@ def __init__(self, **kwargs): ) # set the classes Unicorn attrs dynamically, - for field_name, field in self.form_class.base_fields.items(): # type: str,Field + for ( + field_name, + field, + ) in self.form_class.base_fields.items(): # type: (str, Field) # don't override existing "initial" attrs if not hasattr(self, field_name): setattr(self.__class__, field_name, "") From 39279f2880e22850aa2492cbb968d1c70221d8e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Sat, 12 Mar 2022 18:41:23 +0100 Subject: [PATCH 4/6] add a forms test --- tests/urls.py | 2 ++ tests/views/test_unicorn_forms.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/views/test_unicorn_forms.py diff --git a/tests/urls.py b/tests/urls.py index d91ab894..53c825ca 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,8 +1,10 @@ from django.urls import include, path from django.views.generic import TemplateView +from tests.views.test_unicorn_forms import SimpleFormView urlpatterns = ( path("test", TemplateView.as_view(template_name="templates/test_template.html")), + path("test_forms", SimpleFormView.as_view()), path("", include("django_unicorn.urls")), ) diff --git a/tests/views/test_unicorn_forms.py b/tests/views/test_unicorn_forms.py new file mode 100644 index 00000000..f2b67773 --- /dev/null +++ b/tests/views/test_unicorn_forms.py @@ -0,0 +1,28 @@ +from django.shortcuts import render +from django.template.loader import render_to_string + +from django_unicorn.components import UnicornView +from django_unicorn.components.mixins import UnicornFormMixin +from example.unicorn.forms import ValidationForm + + +class SimpleFormView(UnicornFormMixin, UnicornView): + template_name = "test_simpleform.html" + form_class = ValidationForm + + favourite_color = "red" + + +# noinspection PyUnresolvedReferences +def test_form_fields_attached_as_component_attrs(): + component = SimpleFormView(component_name="test", component_id="12345678") + + assert component.text == "" + assert component.email == "" + + +# def test_form_field_default(client): +# component = SimpleFormView(component_name="test", component_id="12345678") +# +# content = client.get("/test_forms") +# assert content.contains("...") From a4e68980a66c3eda05aaefd581af0c4008b2df42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Sat, 12 Mar 2022 22:58:58 +0100 Subject: [PATCH 5/6] add template --- tests/templates/test_simpleform.html | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/templates/test_simpleform.html diff --git a/tests/templates/test_simpleform.html b/tests/templates/test_simpleform.html new file mode 100644 index 00000000..ba33443c --- /dev/null +++ b/tests/templates/test_simpleform.html @@ -0,0 +1,3 @@ +
+ {{ form }} +
\ No newline at end of file From 1508813a0077423f7f627709d2dad70e34d0206c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gonz=C3=A1lez?= Date: Sun, 13 Mar 2022 07:56:08 +0100 Subject: [PATCH 6/6] flexibly set widget attrs --- django_unicorn/components/mixins.py | 34 ++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/django_unicorn/components/mixins.py b/django_unicorn/components/mixins.py index 068f2f62..c9a762b2 100644 --- a/django_unicorn/components/mixins.py +++ b/django_unicorn/components/mixins.py @@ -38,12 +38,34 @@ def __init__(self, **kwargs): if not hasattr(self, field_name): setattr(self.__class__, field_name, "") - # FIXME: Unicorn attr should be made configurable by the user, and maybe per field? - if isinstance(field, forms.BooleanField): - unicorn_attr = "unicorn:model" - else: - unicorn_attr = "unicorn:model.lazy" - field.widget.attrs.update({unicorn_attr: field_name}) + field.widget.attrs.update(self.get_widget_attr(field_name, field)) + + super().__init__(**kwargs) + + def get_widget_attr(self, field_name: str, field: Field) -> Dict[str, Any]: + """Returns an html attribute for the given field. + + This method can be overridden for setting special attributes to some fields. As default, it returns + "unicorn:model""" + + if isinstance( + field, + ( + forms.BooleanField, + forms.NullBooleanField, + forms.RadioSelect, + forms.NullBooleanSelect, + forms.ChoiceField, + forms.ModelChoiceField, + forms.MultipleChoiceField, + forms.ModelMultipleChoiceField, + forms.TypedChoiceField, + forms.TypedMultipleChoiceField, + ), + ): + return {"unicorn:model": field_name} + else: + return {"unicorn:model.lazy": field_name} super().__init__(**kwargs)