Use Flags (BitFields)

Python supports bit fields through the enum.Flag extension to enum.Enum.

These enumerations are fully supported and will render as multi select form fields by default. For example:

from enum import IntFlag
from django_enum import EnumField
from django.db import models


class Group(models.Model):

    class Permissions(IntFlag):

        # fmt: off
        READ    = 1 << 0
        WRITE   = 1 << 1
        EXECUTE = 1 << 2

        # IntFlags can have composite values!
        RWX     = READ | WRITE | EXECUTE
        # fmt: on

    permissions = EnumField(Permissions)
assert Group.Permissions.READ in group1.permissions
assert Group.Permissions.WRITE not in group1.permissions
assert Group.Permissions.EXECUTE in group1.permissions

assert Group.Permissions.READ in group2.permissions
assert Group.Permissions.WRITE in group2.permissions
assert Group.Permissions.EXECUTE in group2.permissions
assert group2.permissions is Group.Permissions.RWX

Two new field lookups are provided for flag enumerations: has_any and has_all.

has_any

The has_any lookup will return any object that has at least one of the flags in the referenced enumeration. For example:

# this will return both group1 and group2
read_or_write = Group.objects.filter(
    permissions__has_any=Group.Permissions.READ | Group.Permissions.WRITE
)
assert (
    group1 in read_or_write and
    group2 in read_or_write
)

has_all

The has_all lookup will return any object that has at least all of the flags in the referenced enumeration. For example:

# this will return only group2
read_and_write = Group.objects.filter(
    permissions__has_all=Group.Permissions.READ | Group.Permissions.WRITE
)
assert (
    group1 not in read_and_write and
    group2 in read_and_write
)

There are performance considerations when using a bit mask like a Flag enumeration instead of multiple boolean columns. See flag performance for discussion and benchmarks.

Flags with more than 64 bits

Flag enumerations of arbitrary size are supported, however if the enum has more than 64 flags it will be stored as a BinaryField. It is therefore strongly recommended to keep your enum.IntFlag enumerations at 64 bits or less.

Warning

Support for extra large flag fields is experimental. has_any and has_all do not work. Most RDBMS systems do not support bitwise operations on binary fields. Future work may involve exploring support for this as a Postgres extension.