Skip to main content

Better choices library for Django web framework

Project description

Django Better Choices

PyPI PyPI - Python Version Build Status codecov License

Better choices library for Django web framework.

Requirements

This library was written for Python 3.7+ and will not work in any earlier versions.

Install

pip install django-better-choices

Usage

To start defining better choices, you need first to import the Choices class.

from django_better_choices import Choices

Class definition

The choices can be defined with overriding Choices class.

class PageStatus(Choices):
    CREATED = "Created"
    PENDING = Choices.Value("Pending", help_text="This set status to pending")
    ON_HOLD = Choices.Value("On Hold", value="custom_on_hold")

    VALID = Choices.Subset("CREATED", "ON_HOLD")
    INVISIBLE = Choices.Subset("PENDING", "ON_HOLD")

    class InternalStatus(Choices):
        REVIEW = _("On Review")

    @classmethod
    def get_help_text(cls):
        return tuple(
            value.help_text
            for value in cls.values()
            if hasattr(value, "help_text")
        )

Choices class key can be any public identifier (i.e. not starting with underscore _). Overridden choices classes cannot be initialised to obtain a new instance, calling the instance will return a tuple of choice entries.

Inline definition

Alternatively, the choices can be defined dynamically by creating a new Choices instance.

PageStatus = Choices("PageStatus", SUCCESS="Success", FAIL="Error", VALID=Choices.Subset("SUCCESS"))

The first name parameter of Choices constructor is optional and required only for better representation of the returned instance.

Value accessors

You can access choices values using dot notation and with getattr().

value_created = PageStatus.CREATED
value_review = PageStatus.InternalStatus.REVIEW
value_on_hold = getattr(PageStatus, "ON_HOLD")

Values and value parameters

Choices.Value can hold any typing.Hashable value and once compiled equals to this value. In addition to display parameter, other optional parameters can be specified in Choices.Value constructor (see class definition example).

print( PageStatus.CREATED )                # 'created'
print( PageStatus.ON_HOLD )                # 'custom_on_hold'
print( PageStatus.PENDING.display )        # 'Pending'
print( PageStatus.PENDING.help_text )      # 'This set status to pending'

PageStatus.ON_HOLD == "custom_on_hold"     # True
PageStatus.CREATED == PageStatus.CREATED   # True


class Rating(Choices):
    VERY_POOR = Choices.Value("Very poor", value=1)
    POOR = Choices.Value("Poor", value=2)
    OKAY = Choices.Value("Okay", value=3, alt="Not great, not terrible")
    GOOD = Choices.Value("Good", value=4)
    VERY_GOOD = Choices.Value("Very good", value=5)

print( Rating.VERY_GOOD )                  # 5
print( Rating.OKAY.alt )                   # 'Not great, not terrible'
print( {4: "Alright"}[Rating.GOOD] )       # 'Alright'

Instance of Choices.Value class cannot be modified after initialisation. All native non-magic methods can be overridden in Choices.Value custom parameters.

Search in choices

Search in choices is performed by value.

"created" in PageStatus                    # True
"custom_on_hold" in PageStatus             # True
"on_hold" in PageStatus                    # False
value = PageStatus["custom_on_hold"]       # ValueType('custom_on_hold')
value = PageStatus.get("on_hold", 123.45)  # 123.45
key = PageStatus.get_key("created")        # 'CREATED'

Search in subsets

Subsets are used to group several values together (see class definition example) and search by a specific value.

"custom_on_hold" in PageStatus.VALID       # True
PageStatus.CREATED in PageStatus.VALID     # True

Choices.Subset is a subclass of tuple, which is compiled to inner choices class after its definition. All internal or custom choices class methods or properties will be available in a subset class (see "Custom methods" section).

Extract subset

Subsets of choices can be dynamically extracted with extract() method.

PageStatus.extract("CREATED", "ON_HOLD")   # Choices('PageStatus.Subset', CREATED, ON_HOLD)
PageStatus.VALID.extract("ON_HOLD")        # Choices('PageStatus.VALID.Subset', ON_HOLD)

Exclude values

The opposite action to extract() is exclude(). It is used to exclude values from choices class or a subset and return remaining values as a new subset.

PageStatus.exclude("CREATED", "ON_HOLD")   # Choices('PageStatus.Subset', PENDING)
PageStatus.VALID.exclude("ON_HOLD")        # Choices('PageStatus.VALID.Subset', CREATED)

Choices iteration

Choices class implements __iter__ magic method, hence choices are iterable that yield choice entries (i.e. (value, display)). Methods items(), keys() and values() can be used to return tuples of keys and values combinations.

for value, display in PageStatus:  # can also be used as callable, i.e. PageStatus()
    print( value, display )

for key, value in PageStatus.items():
    print( key, value, value.display )

for key in PageStatus.keys():
    print( key )

for value in PageStatus.values():
    print( value, value.display, value.__choice_entry__ )

Additional displays() method is provided for choices and subsets to extract values display strings.

for display in PageStatus.displays():
    print( display )

for display in PageStatus.SUBSET.displays():
    print( display )

Iteration methods items(), keys(), values(), displays(), as well as class constructor can accept keyword arguments to filter collections based on custom parameters, e.g. PageStatus.values(help_text="Some", special=123).

Set operations

Choices class and subsets support standard set operations: union (|), intersection (&), difference (-), and symmetric difference (^).

PageStatus.VALID | PageStatus.INVISIBLE     # Choices(CREATED, ON_HOLD, PENDING)
PageStatus.VALID & PageStatus.INVISIBLE     # Choices(ON_HOLD)
PageStatus.VALID - PageStatus.INVISIBLE     # Choices(CREATED)
PageStatus.VALID ^ PageStatus.INVISIBLE     # Choices(CREATED, PENDING)

Custom methods

All custom choices class methods or properties (non-values) will be available in all subsets.

PageStatus.get_help_text()
PageStatus.VALID.get_help_text()
PageStatus.extract("PENDING", "ON_HOLD").get_help_text()
PageStatus.VALID.extract("ON_HOLD").get_help_text()

Inheritance

Choices fully support class inheritance. All child choices classes have access to parent, grandparent, etc. values and custom methods.

class NewPageStatus(PageStatus):
    ARCHIVED = "Archived"
    ON_HOLD = Choices.Value("On Hold", value="on-hold")  # override parent value

    INACTIVE = Choices.Subset("ON_HOLD", "ARCHIVED")

print( NewPageStatus.CREATED )              # 'created'
print( NewPageStatus.ARCHIVED )             # 'archived'
print( NewPageStatus.ON_HOLD )              # 'on-hold'

Django model fields

Better choices are not different from the original Django choices in terms of usage in models.

class Page(models.Model):
    status = models.CharField(choices=PageStatus, default=PageStatus.CREATED)

Better choices are fully supported by Django migrations and debug toolbar.

Saving choices on models

Better choices are compatible with standard Django models manipulation.

page = Page.objects.get(pk=1)
page.status = PageStatus.PENDING
page.save()

Tests

Run python tests.py for testing.

License

Library is available under the MIT license. The included LICENSE file describes this in detail.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-better-choices-1.17.tar.gz (8.4 kB view details)

Uploaded Source

Built Distribution

django_better_choices-1.17-py3-none-any.whl (8.3 kB view details)

Uploaded Python 3

File details

Details for the file django-better-choices-1.17.tar.gz.

File metadata

  • Download URL: django-better-choices-1.17.tar.gz
  • Upload date:
  • Size: 8.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.54.0 CPython/3.8.2

File hashes

Hashes for django-better-choices-1.17.tar.gz
Algorithm Hash digest
SHA256 e67be6e8a63ba4e9741c97e576b6d9b42a3381a96c712851ffb0b17ab18bcff8
MD5 fb30ae84efbe21df703544e94371cd91
BLAKE2b-256 df13b9940aba2fe222159ea656dcb88a5ce5bc56c71faf4ba4959e27f8e119ef

See more details on using hashes here.

File details

Details for the file django_better_choices-1.17-py3-none-any.whl.

File metadata

  • Download URL: django_better_choices-1.17-py3-none-any.whl
  • Upload date:
  • Size: 8.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.54.0 CPython/3.8.2

File hashes

Hashes for django_better_choices-1.17-py3-none-any.whl
Algorithm Hash digest
SHA256 301acc43b5a9c56ba97812a97a78517f60aaabe419b6e566bd10a2721ef39255
MD5 c2c124e63f0ad1302fa7c3353a7a6c24
BLAKE2b-256 75729382d0c10d82918ff9464a9eca13270cd4ab46758a281a0921fcb05c3845

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page