|
1 | 1 | import logging
|
2 | 2 |
|
| 3 | +from django.core.cache import cache |
3 | 4 | from django.db import models
|
| 5 | +from django.db.models.fields import BLANK_CHOICE_DASH |
| 6 | +from django.forms.fields import TypedChoiceField |
| 7 | +from django.forms.widgets import Select |
4 | 8 | from django.utils.functional import cached_property
|
| 9 | +from django.utils.text import capfirst |
5 | 10 | from django.utils.translation import gettext_lazy as _
|
6 | 11 |
|
7 | 12 | from solo.models import SingletonModel
|
8 | 13 |
|
9 | 14 | from .client import Client
|
| 15 | +from .utils import get_form_choices |
10 | 16 |
|
11 | 17 | logger = logging.getLogger(__name__)
|
12 | 18 |
|
@@ -73,3 +79,108 @@ def save(self, *args, **kwargs):
|
73 | 79 | @cached_property
|
74 | 80 | def client(self):
|
75 | 81 | return Client(self.api_root, self.api_token, self.client_timeout)
|
| 82 | + |
| 83 | + |
| 84 | +class OpenFormsBaseField: |
| 85 | + """ |
| 86 | + Basic field for use in Django models to render a Select widget filled with |
| 87 | + the available forms (uuid, name) or (slug, name) in Open Forms. |
| 88 | + |
| 89 | + This form records the form's UUID or slug, depending on what concrete model |
| 90 | + class is used. |
| 91 | + """ |
| 92 | + |
| 93 | + description = _("Open Forms form") |
| 94 | + use_uuids = None |
| 95 | + |
| 96 | + def deconstruct(self): |
| 97 | + name, path, args, kwargs = super().deconstruct() |
| 98 | + # We do not exclude max_length if it matches default as we want to change |
| 99 | + # the default in future. |
| 100 | + return name, path, args, kwargs |
| 101 | + |
| 102 | + def formfield(self, **kwargs): |
| 103 | + defaults = { |
| 104 | + "required": not self.blank, |
| 105 | + "label": capfirst(self.verbose_name), |
| 106 | + "help_text": self.help_text, |
| 107 | + "widget": Select, |
| 108 | + } |
| 109 | + |
| 110 | + if self.choices is None: |
| 111 | + defaults["choices"] = self.get_choices(include_blank=self.blank) |
| 112 | + defaults["coerce"] = self.to_python |
| 113 | + |
| 114 | + return TypedChoiceField(**defaults) |
| 115 | + |
| 116 | + def get_choices( |
| 117 | + self, |
| 118 | + include_blank=True, |
| 119 | + blank_choice=BLANK_CHOICE_DASH, |
| 120 | + limit_choices_to=None, |
| 121 | + ordering=(), |
| 122 | + ): |
| 123 | + cache_key = f"openformsclient.models.OpenFormsFieldMixin.get_choices__use_uuids_{self.use_uuids}" |
| 124 | + |
| 125 | + choices = cache.get(cache_key) |
| 126 | + if choices is None: |
| 127 | + try: |
| 128 | + choices = get_form_choices(use_uuids=self.use_uuids) |
| 129 | + except Exception as e: |
| 130 | + logger.exception(e) |
| 131 | + choices = [] |
| 132 | + else: |
| 133 | + cache.set(cache_key, choices, timeout=60) |
| 134 | + |
| 135 | + if choices: |
| 136 | + if include_blank: |
| 137 | + blank_defined = any(choice in ("", None) for choice, _ in choices) |
| 138 | + if not blank_defined: |
| 139 | + choices = blank_choice + choices |
| 140 | + |
| 141 | + return choices |
| 142 | + |
| 143 | + |
| 144 | +class OpenFormsUUIDField(OpenFormsBaseField, models.UUIDField): |
| 145 | + """ |
| 146 | + Basic field for use in Django models to render a Select widget filled with |
| 147 | + the available forms (uuid, name) in Open Forms. |
| 148 | + |
| 149 | + This field records the form's UUID. This makes the choice really specific. |
| 150 | + Note that to allow empty records, you will need to set ``null=True`` and |
| 151 | + ``blank=True``. |
| 152 | + """ |
| 153 | + |
| 154 | + use_uuids = True |
| 155 | + |
| 156 | + def get_db_prep_value(self, value, connection, prepared=False): |
| 157 | + # A Select widget always returns a string. If an empty string is |
| 158 | + # returned, we need to force it to be None since an empty string is not |
| 159 | + # valid UUID nor is it empty. |
| 160 | + if not value: |
| 161 | + return None |
| 162 | + return super().get_db_prep_value(value, connection, prepared) |
| 163 | + |
| 164 | + |
| 165 | +class OpenFormsSlugField(OpenFormsBaseField, models.SlugField): |
| 166 | + """ |
| 167 | + Basic field for use in Django models to render a Select widget filled with |
| 168 | + the available forms (slug, name) in Open Forms. |
| 169 | + |
| 170 | + This field records the form's slug. This allows an Open Forms user to |
| 171 | + gracefully change the form without the need to change the reference |
| 172 | + everywhere. |
| 173 | + """ |
| 174 | + |
| 175 | + use_uuids = False |
| 176 | + |
| 177 | + def __init__( |
| 178 | + self, *args, max_length=100, db_index=False, allow_unicode=False, **kwargs |
| 179 | + ): |
| 180 | + super().__init__( |
| 181 | + *args, |
| 182 | + max_length=max_length, |
| 183 | + db_index=db_index, |
| 184 | + allow_unicode=allow_unicode, |
| 185 | + **kwargs, |
| 186 | + ) |
0 commit comments