Skip to content

Commit

Permalink
Merge pull request #5 from luisiacc/custom-choices
Browse files Browse the repository at this point in the history
Allow for registering external choices
  • Loading branch information
lorinkoz committed Jul 31, 2022
2 parents f6235b0 + 60a2593 commit c68563d
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 7 deletions.
32 changes: 32 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,38 @@ If any of these names conflict with other model fields,
the conflicting names won't be accessible to prevent ambiguity.


Register other choices not in models
------------------------------------

If you have some choices you want to expose to javascript, and they don't fit into any
model, lets say for example, a list of actions, you can add those too:

.. code-block:: python
from django_js_choices.core import register_choice
POSSIBLE_ACTIONS = (
("go_down", "Go down"),
("go_top", "Go top"),
("nothing", "Stay")
)
...
# register_choice(name: string, choices: list)
register_choice("possible_actions", POSSIBLE_ACTIONS)
- If any of the names of a manually registered choice conflicts with some model fields, the one you manually
registered will be the one you'll access.
- The 2nd argument is the same type that you'd pass to a CharField `choices` argument.

You can only access the ``POSSIBLE_ACTIONS`` choices through the name ``possible_actions``

PLEASE NOTE: You must ensure the file where you are registering your choice is been processed by django

.. code-block:: javascript
Choices.pairs("possible_actions")
Options
-------

Expand Down
41 changes: 36 additions & 5 deletions django_js_choices/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,40 @@ def prepare_choices(choices):
return new_choices


class ExternalChoices:
def __init__(self):
self.choices = []

def __iter__(self):
return iter(self.choices)


external_choices = ExternalChoices()


def register_choice(name, choices):
choices_names = [choice[0] for choice in external_choices]
if name not in choices_names:
external_choices.choices.append((name, choices))


def generate_choices(locale=None):
raw_choices = []
named_choices = {}
conflicting_names = set()

if locale:
activate(locale)

def save_choices(value):
"""Saves the value if it's not in the list and returns it's index"""
try:
return raw_choices.index(value)
except ValueError:
index = len(raw_choices)
raw_choices.append(value)
return index

for app_config in apps.get_app_configs():
for model in app_config.get_models():
for field in model._meta.get_fields():
Expand All @@ -40,18 +68,21 @@ def generate_choices(locale=None):
medium_name = "{}_{}".format(model._meta.model_name.lower(), field.name)
full_name = "{}_{}".format(model._meta.label_lower.replace(".", "_"), field.name)
value = prepare_choices(choices)
try:
index = raw_choices.index(value)
except ValueError:
index = len(raw_choices)
raw_choices.append(value)
index = save_choices(value)
for name in [short_name, medium_name, full_name]:
if name not in named_choices:
named_choices[name] = index
elif raw_choices[named_choices[name]] != value:
conflicting_names.add(name)

for name in conflicting_names:
del named_choices[name]

for choice_name, choices in external_choices:
value = prepare_choices(choices)
index = save_choices(value)
named_choices[choice_name] = index

if locale:
deactivate()
return raw_choices, named_choices
Expand Down
Empty file modified djsc_sandbox/manage.py
100644 → 100755
Empty file.
10 changes: 10 additions & 0 deletions djsc_sandbox/myapp/choices.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django.utils.translation import gettext_lazy as _

from django_js_choices.core import register_choice

YEAR_IN_SCHOOL_CHOICES = [
("FR", _("Freshman")),
("SO", _("Sophomore")),
Expand Down Expand Up @@ -31,3 +33,11 @@
("SILVER", _("Silver")),
("BRONZE", _("Bronze")),
]

FRUITS_CUSTOM_CHOICES = [
("banana", _("Banana")),
("apple", _("Apple")),
("orange", _("Orange")),
]

register_choice("fruits", FRUITS_CUSTOM_CHOICES)
14 changes: 12 additions & 2 deletions djsc_sandbox/tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from django.utils.translation import gettext_lazy as _, override
from dukpy import evaljs

from django_js_choices.core import generate_choices, generate_js, prepare_choices
from djsc_sandbox.myapp.choices import MEDAL_TYPES, MEDIA_CHOICES, YEAR_IN_SCHOOL_CHOICES
from django_js_choices.core import external_choices, generate_choices, generate_js, prepare_choices, register_choice
from djsc_sandbox.myapp.choices import FRUITS_CUSTOM_CHOICES, MEDAL_TYPES, MEDIA_CHOICES, YEAR_IN_SCHOOL_CHOICES


class PrepareChoicesTestCase(SimpleTestCase):
Expand Down Expand Up @@ -64,6 +64,16 @@ def test_present_short_names(self):
self.assertIn("media", named_choices) # Not excluded because it's the same on all models
self.assertNotIn("year_in_school", named_choices) # Excluded due to conflict between models

def test_external_choices(self):
new_medals_choices = [(1, "1"), (2, "2")]
register_choice("medals", new_medals_choices)
raw_choices, named_choices = generate_choices()
self.assertIn("fruits", named_choices)
# "medals" has a name clash, but it should be present because manually registered names should not change
self.assertIn("medals", named_choices)
medals_index = named_choices["medals"]
self.assertEqual(raw_choices[medals_index], new_medals_choices)

def test_locale_translation(self):
raw_choices, named_choices = generate_choices("es")
self.assertEqual(
Expand Down

0 comments on commit c68563d

Please sign in to comment.