Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#9] Make retrieval of Open Forms choices lazy #11

Merged
merged 2 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 23 additions & 22 deletions openformsclient/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
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.functional import cached_property, lazy
from django.utils.text import capfirst
from django.utils.translation import gettext_lazy as _

Expand Down Expand Up @@ -107,8 +107,7 @@ def formfield(self, **kwargs):
"widget": Select,
}

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

return TypedChoiceField(**defaults)
Expand All @@ -120,25 +119,27 @@ def get_choices(
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
def _fetch():
cache_key = f"openformsclient.models.OpenFormsBaseField.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 exc:
logger.exception(exc)
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

return lazy(_fetch, list)


class OpenFormsUUIDField(OpenFormsBaseField, models.UUIDField):
Expand Down
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ tests_require =
isort
black
flake8
time-machine

[options.packages.find]
include =
Expand All @@ -56,6 +57,7 @@ tests =
isort
black
flake8
time-machine
pep8 = flake8
coverage = pytest-cov
docs =
Expand Down
24 changes: 22 additions & 2 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import datetime
from uuid import UUID

from django.forms import modelform_factory
from django.test import TestCase

import requests_mock
import time_machine

from openformsclient.models import Configuration
from testapp.models import Page
Expand Down Expand Up @@ -41,7 +43,7 @@ def test_values_in_slug_form_field(self, m):
page_form = PageForm()

self.assertListEqual(
page_form.fields["form_slug"].choices,
list(page_form.fields["form_slug"].choices),
[
("", "---------"),
("test-1", "Test 1"),
Expand All @@ -56,7 +58,7 @@ def test_values_in_uuid_form_field(self, m):
page_form = PageForm()

self.assertListEqual(
page_form.fields["form_uuid"].choices,
list(page_form.fields["form_uuid"].choices),
[
("", "---------"),
("f4423c99-6341-442e-aedc-b47779579f4d", "Test 1"),
Expand Down Expand Up @@ -129,3 +131,21 @@ def test_slug_form_field_blank(self, m):

self.assertEqual(Page.objects.count(), 1)
self.assertEqual(Page.objects.get().form_slug, "")

def test_form_retrieval_cache(self, m):
self._prepare_mock(m)

PageForm = modelform_factory(Page, fields=["form_slug"])
page_form = PageForm()

with time_machine.travel(0) as traveller:
list(page_form.fields["form_slug"].choices)
list(page_form.fields["form_slug"].choices)

self.assertEqual(m.call_count, 1)

traveller.shift(datetime.timedelta(seconds=60))

list(page_form.fields["form_slug"].choices)

self.assertEqual(m.call_count, 2)
Loading