Skip to content

Commit

Permalink
[#9] Replace model form fields with regular form fields
Browse files Browse the repository at this point in the history
  • Loading branch information
pi-sigma committed Apr 25, 2024
1 parent 6c79b55 commit 2896d94
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 111 deletions.
100 changes: 100 additions & 0 deletions openformsclient/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import logging

from django import forms
from django.core.cache import cache
from django.db.models.fields import BLANK_CHOICE_DASH
from django.forms.fields import TypedChoiceField
from django.forms.widgets import Select
from django.utils.translation import gettext_lazy as _

from .utils import get_form_choices

logger = logging.getLogger(__name__)


class OpenFormsBaseField(TypedChoiceField):
"""
Basic field for use in Django forms to render a Select widget filled with
the available forms (uuid, name) or (slug, name) in Open Forms.
This form records the form's UUID or slug, depending on what concrete model
class is used.
"""

description = _("Open Forms form")
use_uuids = None
widget = Select
include_blank = True

def get_choices(
self,
blank_choice=BLANK_CHOICE_DASH,
limit_choices_to=None,
ordering=(),
):
cache_key = f"openformsclient.models.OpenFormsFieldMixin.get_choices__use_uuids_{self.use_uuids}"

choices = cache.get(cache_key)
if choices is None:
try:
choices = get_form_choices(use_uuids=self.use_uuids)
except Exception as e:
logger.exception(e)
choices = []
else:
cache.set(cache_key, choices, timeout=60)

if choices:
if self.include_blank:
blank_defined = any(choice in ("", None) for choice, _ in choices)
if not blank_defined:
choices = blank_choice + choices

return choices


class OpenFormsUUIDField(OpenFormsBaseField, forms.UUIDField):
"""
Basic field for use in Django forms to render a Select widget filled with
the available forms (uuid, name) in Open Forms.
This field records the form's UUID. This makes the choice really specific.
Note that to allow empty records, you will need to set ``null=True`` and
``blank=True``.
"""

use_uuids = True

def get_db_prep_value(self, value, connection, prepared=False):
# A Select widget always returns a string. If an empty string is
# returned, we need to force it to be None since an empty string is not
# valid UUID nor is it empty.
if not value:
return None
return super().get_db_prep_value(value, connection, prepared)


class OpenFormsSlugField(OpenFormsBaseField, forms.SlugField):
"""
Basic field for use in Django forms to render a Select widget filled with
the available forms (slug, name) in Open Forms.
This field records the form's slug. This allows an Open Forms user to
gracefully change the form without the need to change the reference
everywhere.
"""

use_uuids = False

def __init__(
self, *args, max_length=100, required=False, allow_unicode=False, include_blank=True, **kwargs
):
super().__init__(
*args,
max_length=max_length,
required=False,
allow_unicode=allow_unicode,
**kwargs,
)
self.include_blank = include_blank
self.coerce = self.to_python
111 changes: 0 additions & 111 deletions openformsclient/models.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import logging

from django.core.cache import cache
from django.db import models
from django.db.models.fields import BLANK_CHOICE_DASH
from django.forms.fields import TypedChoiceField
from django.forms.widgets import Select
from django.utils.functional import cached_property
from django.utils.text import capfirst
from django.utils.translation import gettext_lazy as _

from solo.models import SingletonModel

from .client import Client
from .utils import get_form_choices

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -79,108 +73,3 @@ def save(self, *args, **kwargs):
@cached_property
def client(self):
return Client(self.api_root, self.api_token, self.client_timeout)


class OpenFormsBaseField:
"""
Basic field for use in Django models to render a Select widget filled with
the available forms (uuid, name) or (slug, name) in Open Forms.
This form records the form's UUID or slug, depending on what concrete model
class is used.
"""

description = _("Open Forms form")
use_uuids = None

def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
# We do not exclude max_length if it matches default as we want to change
# the default in future.
return name, path, args, kwargs

def formfield(self, **kwargs):
defaults = {
"required": not self.blank,
"label": capfirst(self.verbose_name),
"help_text": self.help_text,
"widget": Select,
}

if self.choices is None:
defaults["choices"] = self.get_choices(include_blank=self.blank)
defaults["coerce"] = self.to_python

return TypedChoiceField(**defaults)

def get_choices(
self,
include_blank=True,
blank_choice=BLANK_CHOICE_DASH,
limit_choices_to=None,
ordering=(),
):
cache_key = f"openformsclient.models.OpenFormsFieldMixin.get_choices__use_uuids_{self.use_uuids}"

choices = cache.get(cache_key)
if choices is None:
try:
choices = get_form_choices(use_uuids=self.use_uuids)
except Exception as e:
logger.exception(e)
choices = []
else:
cache.set(cache_key, choices, timeout=60)

if choices:
if include_blank:
blank_defined = any(choice in ("", None) for choice, _ in choices)
if not blank_defined:
choices = blank_choice + choices

return choices


class OpenFormsUUIDField(OpenFormsBaseField, models.UUIDField):
"""
Basic field for use in Django models to render a Select widget filled with
the available forms (uuid, name) in Open Forms.
This field records the form's UUID. This makes the choice really specific.
Note that to allow empty records, you will need to set ``null=True`` and
``blank=True``.
"""

use_uuids = True

def get_db_prep_value(self, value, connection, prepared=False):
# A Select widget always returns a string. If an empty string is
# returned, we need to force it to be None since an empty string is not
# valid UUID nor is it empty.
if not value:
return None
return super().get_db_prep_value(value, connection, prepared)


class OpenFormsSlugField(OpenFormsBaseField, models.SlugField):
"""
Basic field for use in Django models to render a Select widget filled with
the available forms (slug, name) in Open Forms.
This field records the form's slug. This allows an Open Forms user to
gracefully change the form without the need to change the reference
everywhere.
"""

use_uuids = False

def __init__(
self, *args, max_length=100, db_index=False, allow_unicode=False, **kwargs
):
super().__init__(
*args,
max_length=max_length,
db_index=db_index,
allow_unicode=allow_unicode,
**kwargs,
)

0 comments on commit 2896d94

Please sign in to comment.