Skip to content

Commit 642208d

Browse files
committed
try fix oracle test case
1 parent 96fae54 commit 642208d

File tree

5 files changed

+204
-60
lines changed

5 files changed

+204
-60
lines changed

doc/source/index.rst

Lines changed: 187 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,87 @@
44
Django Enum
55
===========
66

7-
Full and natural support for enumerations_ as Django model fields.
7+
|MIT license| |Ruff| |PyPI version fury.io| |PyPI pyversions| |PyPi djversions| |PyPI status|
8+
|Documentation Status| |Code Cov| |Test Status|
89

9-
Many packages aim to ease usage of Python enumerations as model fields. Most
10-
were made obsolete when Django provided TextChoices_ and IntegerChoices_
11-
types. The motivation for django-enum was to:
10+
----
1211

13-
* Always automatically coerce fields to instances of the Enum type.
12+
|Postgres| |MySQL| |MariaDB| |SQLite| |Oracle|
13+
14+
.. |MIT license| image:: https://img.shields.io/badge/License-MIT-blue.svg
15+
:target: https://lbesson.mit-license.org/
16+
17+
.. |Ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
18+
:target: https:://github.com/astral-sh/ruff
19+
20+
.. |PyPI version fury.io| image:: https://badge.fury.io/py/django-enum.svg
21+
:target: https://pypi.python.org/pypi/django-enum/
22+
23+
.. |PyPI pyversions| image:: https://img.shields.io/pypi/pyversions/django-enum.svg
24+
:target: https://pypi.python.org/pypi/django-enum/
25+
26+
.. |PyPI djversions| image:: https://img.shields.io/pypi/djversions/django-enum.svg
27+
:target: https://pypi.org/project/django-enum/
28+
29+
.. |PyPI status| image:: https://img.shields.io/pypi/status/django-enum.svg
30+
:target: https://pypi.python.org/pypi/django-enum
31+
32+
.. |Documentation Status| image:: https://readthedocs.org/projects/django-enum/badge/?version=latest
33+
:target: http://django-enum.readthedocs.io/?badge=latest/
34+
35+
.. |Code Cov| image:: https://codecov.io/gh/bckohan/django-enum/branch/main/graph/badge.svg?token=0IZOKN2DYL
36+
:target: https://codecov.io/gh/bckohan/django-enum
37+
38+
.. |Test Status| image:: https://github.com/bckohan/django-enum/workflows/test/badge.svg
39+
:target: https://github.com/bckohan/django-enum/actions/workflows/test.yml
40+
41+
.. |Lint Status| image:: https://github.com/bckohan/django-enum/workflows/lint/badge.svg
42+
:target: https://github.com/bckohan/django-enum/actions/workflows/lint.yml
43+
44+
.. |Postgres| image:: https://img.shields.io/badge/Postgres-9.6%2B-blue
45+
:target: https://www.postgresql.org/
46+
47+
.. |MySQL| image:: https://img.shields.io/badge/MySQL-5.7%2B-blue
48+
:target: https://www.mysql.com/
49+
50+
.. |MariaDB| image:: https://img.shields.io/badge/MariaDB-10.2%2B-blue
51+
:target: https://mariadb.org/
52+
53+
.. |SQLite| image:: https://img.shields.io/badge/SQLite-3.8%2B-blue
54+
:target: https://www.sqlite.org/
55+
56+
.. |Oracle| image:: https://img.shields.io/badge/Oracle-18%2B-blue
57+
:target: https://www.oracle.com/database/
58+
59+
----
60+
61+
Full and natural support for enumerations_ as Django_ model fields.
62+
63+
Many packages aim to ease usage of Python enumerations as model fields. Most were superseded when
64+
Django provided ``TextChoices`` and ``IntegerChoices`` types. The motivation for django-enum_ was
65+
to:
66+
67+
* Work with any Python PEP 435 Enum including those that do not derive from Django's
68+
``TextChoices`` and ``IntegerChoices``.
69+
* Coerce fields to instances of the Enum type by default.
1470
* Allow strict adherence to Enum values to be disabled.
15-
* Be compatible with Enum classes that do not derive from Django's Choices.
16-
* Handle migrations appropriately. (See `migrations <https://django-enum.readthedocs.io/en/latest/usage.html#migrations>`_)
17-
* Integrate as fully as possible with Django_'s existing level of enum support.
18-
* Integrate with enum-properties_ to enable richer enumeration types.
71+
* Handle migrations appropriately. (See :ref:`migrations`)
72+
* Integrate as fully as possible with Django's existing level of enum support.
73+
* Support enum-properties_ to enable richer enumeration types. (A less awkward alternative to
74+
dataclass enumerations with more features)
1975
* Represent enum fields with the smallest possible column type.
20-
* Be as simple and light-weight an extension to core Django as possible.
76+
* Support bit mask queries using standard Python Flag enumerations.
77+
* Be as simple and light-weight an extension to core Django_ as possible.
78+
* Enforce enumeration value consistency at the database level using check constraints by default.
79+
* (TODO) Support native database enumeration column types when available.
2180

22-
django-enum works in concert with Django_'s built in TextChoices_ and
23-
IntegerChoices_ to provide a new model field type, ``EnumField``, that
24-
resolves the correct native Django_ field type for the given enumeration based
25-
on its value type and range. For example, IntegerChoices_ that contain
26-
values between 0 and 32767 become `PositiveSmallIntegerField <https://docs.djangoproject.com/en/stable/ref/models/fields/#positivesmallintegerfield>`_.
81+
django-enum_ provides a new model field type, ``EnumField``, that allows you to treat almost any
82+
PEP 435 enumeration as a database column. ``EnumField`` resolves the correct native Django_ field
83+
type for the given enumeration based on its value type and range. For example, ``IntegerChoices``
84+
that contain values between 0 and 32767 become
85+
`PositiveSmallIntegerField <https://docs.djangoproject.com/en/stable/ref/models/fields/#positivesmallintegerfield>`_.
2786

28-
.. code:: python
87+
.. code-block:: python
2988
3089
from django.db import models
3190
from django_enum import EnumField
@@ -49,14 +108,14 @@ values between 0 and 32767 become `PositiveSmallIntegerField <https://docs.djang
49108
txt_enum = EnumField(TextEnum, null=True, blank=True)
50109
51110
# this is equivalent to
52-
# PositiveSmallIntegerField(choices=IntEnum.choices)
53-
int_enum = EnumField(IntEnum)
111+
# PositiveSmallIntegerField(choices=IntEnum.choices, default=IntEnum.ONE.value)
112+
int_enum = EnumField(IntEnum, default=IntEnum.ONE)
54113
55114
56-
``EnumField`` **is more than just an alias. The fields are now assignable and
57-
accessible as their enumeration type rather than by-value:**
115+
``EnumField`` **is more than just an alias. The fields are now assignable and accessible as their
116+
enumeration type rather than by-value:**
58117

59-
.. code:: python
118+
.. code-block:: python
60119
61120
instance = MyModel.objects.create(
62121
txt_enum=MyModel.TextEnum.VALUE1,
@@ -70,24 +129,64 @@ accessible as their enumeration type rather than by-value:**
70129
assert instance.int_enum.value == 3
71130
72131
73-
`django-enum <https://django-enum.readthedocs.io/en/latest/>`_ also provides
74-
IntegerChoices_ and TextChoices_ types that extend from
75-
enum-properties_ which makes possible very rich enumeration fields.
132+
Flag Support
133+
============
134+
135+
Flag_ types are also seamlessly supported! This allows a database column to behave like a bit mask
136+
and is an alternative to multiple boolean columns. There are mostly positive performance
137+
implications for using a bit mask instead of booleans depending on the size of the bit mask and the
138+
types of queries you will run against it. For bit masks more than a few bits long the size
139+
reduction both speeds up queries and reduces the required storage space. See the documentation for
140+
:ref:`discussion and benchmarks <flag_performance>`.
141+
142+
.. code-block:: python
143+
144+
class Permissions(IntFlag):
145+
146+
READ = 1**2
147+
WRITE = 2**2
148+
EXECUTE = 3**2
149+
150+
151+
class FlagExample(models.Model):
76152
77-
.. code:: python
153+
permissions = EnumField(Permissions)
78154
79-
from enum_properties import s
80-
from django_enum import TextChoices # use instead of Django's TextChoices
155+
156+
FlagExample.objects.create(permissions=Permissions.READ | Permissions.WRITE)
157+
158+
# get all models with RW:
159+
FlagExample.objects.filter(permissions__has_all=Permissions.READ | Permissions.WRITE)
160+
161+
162+
Complex Enumerations
163+
====================
164+
165+
django-enum_ supports enum types that do not derive from Django's ``IntegerChoices`` and
166+
``TextChoices``. This allows us to use other libs like enum-properties_ which makes possible very
167+
rich enumeration fields:
168+
169+
.. code-block:: console
170+
171+
?> pip install enum-properties
172+
173+
.. code-block:: python
174+
175+
from enum_properties import StrEnumProperties
81176
from django.db import models
82177
83178
class TextChoicesExample(models.Model):
84179
85-
class Color(TextChoices, s('rgb'), s('hex', case_fold=True)):
180+
class Color(StrEnumProperties):
181+
182+
label: Annotated[str, Symmetric()]
183+
rgb: Annotated[t.Tuple[int, int, int], Symmetric()]
184+
hex: Annotated[str, Symmetric(case_fold=True)]
86185
87-
# name value label rgb hex
88-
RED = 'R', 'Red', (1, 0, 0), 'ff0000'
89-
GREEN = 'G', 'Green', (0, 1, 0), '00ff00'
90-
BLUE = 'B', 'Blue', (0, 0, 1), '0000ff'
186+
# name value label rgb hex
187+
RED = "R", "Red", (1, 0, 0), "ff0000"
188+
GREEN = "G", "Green", (0, 1, 0), "00ff00"
189+
BLUE = "B", "Blue", (0, 0, 1), "0000ff"
91190
92191
# any named s() values in the Enum's inheritance become properties on
93192
# each value, and the enumeration value may be instantiated from the
@@ -130,49 +229,78 @@ enum-properties_ which makes possible very rich enumeration fields.
130229
131230
assert TextChoicesExample.objects.filter(color='FF0000').first() == instance
132231
133-
.. note::
134232
135-
Consider using
136-
`django-render-static <https://pypi.org/project/django-render-static/>`_
137-
to make your enumerations DRY_ across the full stack!
233+
While they should be unnecessary if you need to integrate with code that expects an interface fully
234+
compatible with Django's ``TextChoices`` and ``IntegerChoices`` django-enum_ provides
235+
``TextChoices``, ``IntegerChoices``, ``FlagChoices`` and ``FloatChoices`` types that derive from
236+
enum-properties_ and Django's ``Choices``. So the above enumeration could also be written:
138237

139-
Please report bugs and discuss features on the
140-
`issues page <https://github.com/bckohan/django-enum/issues>`_.
238+
.. code-block:: python
239+
240+
from django_enum.choices import TextChoices
141241
142-
`Contributions <https://github.com/bckohan/django-enum/blob/main/CONTRIBUTING.rst>`_
143-
are encouraged!
242+
class Color(TextChoices):
243+
244+
# label is added as a symmetric property by the base class
245+
246+
rgb: Annotated[t.Tuple[int, int, int], Symmetric()]
247+
hex: Annotated[str, Symmetric(case_fold=True)]
248+
249+
# name value label rgb hex
250+
RED = "R", "Red", (1, 0, 0), "ff0000"
251+
GREEN = "G", "Green", (0, 1, 0), "00ff00"
252+
BLUE = "B", "Blue", (0, 0, 1), "0000ff"
144253
145-
`Full documentation at read the docs. <https://django-enum.readthedocs.io/en/latest/>`_
146254
147255
Installation
148-
------------
256+
============
257+
258+
1. Clone django-enum from GitHub_ or install a release off PyPI_:
259+
260+
.. code-block:: console
261+
262+
?> pip install django-enum
149263
150-
1. Clone django-enum from GitHub_ or install a release off PyPI_ :
151264
152-
.. code:: bash
265+
django-enum_ has several optional dependencies that are not pulled in by default. ``EnumFields``
266+
work seamlessly with all Django apps that work with model fields with choices without any
267+
additional work. Optional integrations are provided with several popular libraries to extend this
268+
basic functionality.
153269

154-
pip install django-enum
270+
Integrations are provided that leverage enum-properties_ to make enumerations do more work and to
271+
provide extended functionality for django-filter_ and djangorestframework_.
155272

273+
.. code-block:: console
156274
157-
.. note::
275+
?> pip install enum-properties
276+
?> pip install django-filter
277+
?> pip install djangorestframework
158278
159-
``django-enum`` has several optional dependencies that are not pulled in
160-
by default. ``EnumFields`` work seamlessly with all Django apps that
161-
work with model fields with choices without any additional work. Optional
162-
integrations are provided with several popular libraries to extend this
163-
basic functionality.
164279
165-
Integrations are provided that leverage enum-properties_ to make enumerations
166-
do more work and to provide extended functionality for django-filter_ and DRF_.
280+
Continuous Integration
281+
======================
167282

168-
.. code:: bash
283+
Like with Django, Postgres is the preferred database for support. The full test suite is run
284+
against all combinations of currently supported versions of Django, Python, and Postgres as well as
285+
psycopg3 and psycopg2. The other RDBMS supported by Django are also tested including SQLite, MySQL,
286+
MariaDB and Oracle. For these RDBMS (with the exception of Oracle), tests are run against the
287+
minimum and maximum supported version combinations to maximize coverage breadth.
169288

170-
pip install enum-properties
171-
pip install django-filter
172-
pip install djangorestframework
289+
**See the** `latest test runs <https://github.com/bckohan/django-enum/actions/workflows/test.yml>`_
290+
**for our current test matrix**
291+
292+
*For Oracle, only the latest version of the free database is tested against the minimum and
293+
maximum supported versions of Python, Django and the cx-Oracle driver.*
294+
295+
Further Reading
296+
===============
297+
298+
Consider using django-render-static_ to make your enumerations DRY_ across the full stack!
299+
300+
Please report bugs and discuss features on the
301+
`issues page <https://github.com/bckohan/django-enum/issues>`_.
173302

174-
If features are utilized that require a missing optional dependency an
175-
exception will be thrown.
303+
`Contributions <https://github.com/bckohan/django-enum/blob/main/CONTRIBUTING.md>`_ are encouraged!
176304

177305

178306
.. toctree::

doc/source/performance.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ or eccentric enumeration cases.
2828
not recommended - but may be appropriate if the dominate use case involves
2929
high volume serialization to a raw value instead.
3030

31+
32+
.. _flag_performance:
33+
3134
Flags
3235
=====
3336

doc/source/refs.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
.. _GitHub: https://github.com/bckohan/django-enum
44
.. _PyPI: https://pypi.python.org/pypi/django-enum
55
.. _Enum: https://docs.python.org/3/library/enum.html#enum.Enum
6+
.. _Flag: https://docs.python.org/3/library/enum.html#enum.Flag
67
.. _enumerations: https://docs.python.org/3/library/enum.html#enum.Enum
78
.. _ValueError: https://docs.python.org/3/library/exceptions.html#ValueError
89
.. _get_db_prep_value: https://docs.djangoproject.com/en/stable/ref/models/fields/#django.db.models.Field.get_db_prep_value
@@ -13,7 +14,10 @@
1314
.. _full_clean: https://docs.djangoproject.com/en/stable/ref/models/instances/#django.db.models.Model.full_clean
1415
.. _DRY: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
1516
.. _enum-properties: https://pypi.org/project/enum-properties
17+
.. _django-enum: https://pypi.org/project/django-enum
1618
.. _django-filter: https://pypi.org/project/django-filter
19+
.. _djangorestframework: https://pypi.org/project/djangorestframework
20+
.. _django-render-static: https://pypi.org/project/django-render-static
1721
.. _DRF: https://www.django-rest-framework.org
1822
.. _Choices: https://docs.djangoproject.com/en/4.1/ref/models/fields/#enumeration-types
1923
.. _TextChoices: https://docs.djangoproject.com/en/4.1/ref/models/fields/#enumeration-types

doc/source/usage.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,8 @@ by default. So the above is also equivalent to:
416416
filterset_class = TextChoicesExampleFilter
417417
model = TextChoicesExample
418418
419+
.. _migrations:
420+
419421
Migrations
420422
##########
421423

tests/test_flags.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,9 +451,16 @@ def test_unsupported_flags(self):
451451

452452
def test_extra_big_flags(self):
453453
obj = self.MODEL_CLASS.objects.create()
454-
obj.refresh_from_db()
455454
self.assertTrue(obj.extra_big_neg is None)
456455
self.assertEqual(obj.extra_big_pos, 0)
456+
obj.refresh_from_db()
457+
458+
if connection.vendor == "oracle":
459+
# TODO - possible to fix this?
460+
self.assertEqual(obj.extra_big_neg, 0)
461+
else:
462+
self.assertTrue(obj.extra_big_neg is None)
463+
self.assertEqual(obj.extra_big_pos, 0)
457464

458465
self.assertEqual(obj, self.MODEL_CLASS.objects.get(extra_big_pos=0))
459466
self.assertEqual(obj, self.MODEL_CLASS.objects.get(extra_big_neg__isnull=True))

0 commit comments

Comments
 (0)