Skip to content

Commit bcc7c84

Browse files
committed
fix #103 fix #104
1 parent b13be16 commit bcc7c84

File tree

8 files changed

+71
-29
lines changed

8 files changed

+71
-29
lines changed

justfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ install-precommit:
3939
@just run pre-commit install
4040

4141
# update and install development dependencies
42-
install *OPTS:
42+
install *OPTS="--all-extras":
4343
uv sync {{ OPTS }}
4444
@just run pre-commit install
4545

pyproject.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ dev = [
8484
"deepdiff>=8.2.0",
8585
"django-extensions>=3.2.3",
8686
"django-stubs[compatible-mypy]>=5.1.3",
87-
"django-test-migrations @ git+https://github.com/bckohan/django-test-migrations.git@issue-503#egg=django-test-migrations",
87+
# todo - update when version > 1.4.0 released
88+
"django-test-migrations @ git+https://github.com/wemake-services/django-test-migrations.git@master#egg=django-test-migrations",
8889
"djlint>=1.36.4",
8990
"ipdb>=0.13.13",
9091
"matplotlib>=3.9.4",

src/django_enum/fields.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ def formfield(self, form_class=None, choices_form_class=None, **kwargs):
674674

675675
is_multi = self.enum and issubclass(self.enum, Flag)
676676
if is_multi:
677-
kwargs["empty_value"] = self.enum(0)
677+
kwargs["empty_value"] = None if self.null else self.enum(0)
678678
# why fail? - does this fail for single select too?
679679
# kwargs['show_hidden_initial'] = True
680680

@@ -1256,6 +1256,10 @@ class EnumExtraBigIntegerField(IntEnumField, BinaryField):
12561256

12571257
description = _("A bit field wider than the standard word size.")
12581258

1259+
def __init__(self, editable=True, **kwargs):
1260+
# Django disables form editing on BinaryFields by default, so we override
1261+
super().__init__(editable=editable, **kwargs)
1262+
12591263
@cached_property
12601264
def signed(self):
12611265
"""True if the enum has negative values"""

src/django_enum/forms.py

+7-10
Original file line numberDiff line numberDiff line change
@@ -333,19 +333,16 @@ class EnumChoiceField(ChoiceFieldMixin, TypedChoiceField): # type: ignore
333333

334334
class EnumFlagField(ChoiceFieldMixin, TypedMultipleChoiceField): # type: ignore
335335
"""
336-
The default ``TypedMultipleChoiceField`` will only accept the base
337-
enumeration values. Use this field on forms to accept any value mappable
338-
to an enumeration including any labels or symmetric properties.
336+
A generic form field for :class:`~enum.Flag` derived enumerations. By default the
337+
:class:`~django_enum.forms.FlagSelectMultiple` widget will be used.
339338
340-
Behavior:
339+
After cleaning the value stored in the cleaned data will be a combined enum instance.
340+
(e.g. all input flags will be or-ed together)
341341
342-
if no select value in post data:
343-
if null=True, no choice is null.
344-
If null=False, no choice is Enum(0)
345-
if select value in post data:
346-
if null=True or False, no choice is Enum(0)
342+
.. note::
347343
348-
if strict=False, values can be outside of the enumerations
344+
The default empty_value is Flag(0) but when used in a ModelForm the empty_value
345+
will be automatically set to None if null=True.
349346
"""
350347

351348
widget = FlagSelectMultiple

tests/enum_prop/models.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,8 @@ class SingleNoCoercePerf(models.Model):
383383
class BitFieldModel(models.Model):
384384
bit_field_small = EnumField(GNSSConstellation)
385385
bit_field_large = EnumField(LargeBitField, null=True, default=None, blank=True)
386-
bit_field_large_neg = EnumField(
386+
bit_field_large_empty_default = EnumField(LargeBitField, blank=True)
387+
large_neg = EnumField(
387388
LargeNegativeField, default=LargeNegativeField.NEG_ONE, null=True
388389
)
389390
no_default = EnumField(LargeBitField)

tests/test_field_types_ep.py

+5-10
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,14 @@ def test_large_bitfields(self):
2323
tester._meta.get_field("bit_field_small"), PositiveSmallIntegerField
2424
)
2525
self.assertIsInstance(tester._meta.get_field("bit_field_large"), BinaryField)
26-
self.assertIsInstance(
27-
tester._meta.get_field("bit_field_large_neg"), BinaryField
28-
)
26+
self.assertIsInstance(tester._meta.get_field("large_neg"), BinaryField)
2927

3028
self.assertEqual(
3129
tester.bit_field_small,
3230
GNSSConstellation.GPS | GNSSConstellation.GLONASS,
3331
)
3432
self.assertEqual(tester.bit_field_large, None)
35-
self.assertEqual(tester.bit_field_large_neg, LargeNegativeField.NEG_ONE)
33+
self.assertEqual(tester.large_neg, LargeNegativeField.NEG_ONE)
3634
self.assertEqual(tester.no_default, LargeBitField(0))
3735

3836
self.assertEqual(
@@ -94,7 +92,7 @@ def test_large_bitfields(self):
9492
tester2 = BitFieldModel.objects.create(
9593
bit_field_small=GNSSConstellation.GPS | GNSSConstellation.GLONASS,
9694
bit_field_large=LargeBitField.ONE | LargeBitField.TWO,
97-
bit_field_large_neg=None,
95+
large_neg=None,
9896
)
9997

10098
# has_any and has_all are not supported on ExtraLarge bit fields
@@ -107,14 +105,11 @@ def test_large_bitfields(self):
107105
)
108106

109107
with self.assertRaises(FieldError):
110-
BitFieldModel.objects.filter(
111-
bit_field_large_neg__has_any=LargeNegativeField.NEG_ONE
112-
)
108+
BitFieldModel.objects.filter(large_neg__has_any=LargeNegativeField.NEG_ONE)
113109

114110
with self.assertRaises(FieldError):
115111
BitFieldModel.objects.filter(
116-
bit_field_large_neg__has_all=LargeNegativeField.NEG_ONE
117-
| LargeNegativeField.ZERO
112+
large_neg__has_all=LargeNegativeField.NEG_ONE | LargeNegativeField.ZERO
118113
)
119114

120115

tests/test_forms_ep.py

+47-3
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,44 @@ def test_flag_choices_admin_form(self):
4646
)
4747

4848
def test_flag_choices_model_form(self):
49+
from django.forms.models import fields_for_model
50+
from django_enum.forms import EnumChoiceField, EnumFlagField
4951
from tests.examples.models.flag import Permissions
50-
from tests.enum_prop.enums import GNSSConstellation
52+
from tests.enum_prop.enums import (
53+
GNSSConstellation,
54+
LargeBitField,
55+
LargeNegativeField,
56+
)
5157

5258
class FlagChoicesModelForm(ModelForm):
53-
class Meta(EnumTesterForm.Meta):
59+
class Meta:
60+
fields = "__all__"
5461
model = BitFieldModel
5562

63+
fields = fields_for_model(BitFieldModel)
64+
65+
self.assertEqual(len(fields), 5)
66+
67+
expected_types = {
68+
"bit_field_small": EnumFlagField,
69+
"bit_field_large": EnumFlagField,
70+
"bit_field_large_empty_default": EnumFlagField,
71+
"large_neg": EnumChoiceField,
72+
"no_default": EnumFlagField,
73+
}
74+
75+
for field, inst in fields.items():
76+
self.assertIsInstance(inst, expected_types[field])
77+
5678
form = FlagChoicesModelForm(
57-
data={"bit_field_small": [GNSSConstellation.GPS, GNSSConstellation.GLONASS]}
79+
data={
80+
"bit_field_small": [
81+
GNSSConstellation.GPS.value,
82+
GNSSConstellation.GLONASS,
83+
],
84+
"large_neg": LargeNegativeField.NEG_ONE.value,
85+
"no_default": LargeBitField.TWO,
86+
}
5887
)
5988

6089
form.full_clean()
@@ -63,6 +92,21 @@ class Meta(EnumTesterForm.Meta):
6392
form.cleaned_data["bit_field_small"],
6493
GNSSConstellation.GPS | GNSSConstellation.GLONASS,
6594
)
95+
self.assertIsInstance(form.cleaned_data["bit_field_small"], GNSSConstellation)
96+
self.assertEqual(
97+
form.cleaned_data["large_neg"],
98+
LargeNegativeField.NEG_ONE,
99+
)
100+
self.assertIsInstance(form.cleaned_data["large_neg"], LargeNegativeField)
101+
self.assertEqual(form.cleaned_data["no_default"], LargeBitField.TWO)
102+
self.assertIsInstance(form.cleaned_data["no_default"], LargeBitField)
103+
self.assertEqual(form.cleaned_data["bit_field_large"], None)
104+
self.assertEqual(
105+
form.cleaned_data["bit_field_large_empty_default"], LargeBitField(0)
106+
)
107+
self.assertIsInstance(
108+
form.cleaned_data["bit_field_large_empty_default"], LargeBitField
109+
)
66110
self.assertIsInstance(form.base_fields["bit_field_small"], EnumFlagField)
67111

68112
def test_extern_flag_admin_form(self):

uv.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)