Skip to content
Advertisement

Inherit choice class to extend it?

I have a field in my models.py that accepts choices determined in a class:

from apps.users.constants import UserChoices


class User(models.Model):
    choices = models.CharField(max_length=10, blank=True, choices=UserChoices.choices(), default=UserChoices.PUBLIC_USER)

The choice class is this:

from django.utils.translation import ugettext_lazy as _


class UserChoices:
    PRIVATE_USER = "private_user"
    PUBLIC_USER = "public_user"

    @classmethod
    def choices(cls):
        return (
            (cls.PRIVATE_USER, _("Private User")),
            (cls.PUBLIC_USER, _("Public User")),
        )

My doubt is how can I inherit this UserChoices class to another choice class, in order to extend it with another options.

I tried the following:

class ExtendedChoices(UserChoices):

    OTHER_CHOICE = "other_choice"

    @classmethod
    def choices(cls):
        return (
            UserChoices.choices(),
            (cls.OTHER_CHOICE, _("Other choice")),
        )

But it gives me a migration error:

users.OtherModel.other_choice: (fields.E005) 'choices' must be an iterable containing (actual value, human readable name) tuples.

Obviously this example is simplified, the actual code has 40+ choices on the original class and 20+ in the extended one.

Advertisement

Answer

You need to unpack the ones from the parent. You do that with an asterisk (*):

class ExtendedChoices(UserChoices):

    OTHER_CHOICE = "other_choice"

    @classmethod
    def choices(cls):
        return (
            *UserChoices.choices(),  # ← an asterisk to unpack the tuple
            (cls.OTHER_CHOICE, _("Other choice")),
        )

If we unpack a tuple in another tuple, we construct a tuple that contains all the items of the unpacked tuple as elements of the new tuple. For example:

>>> x = (1,4,2)
>>> (x, 5)
((1, 4, 2), 5)
>>> (*x, 5)
(1, 4, 2, 5)

If we thus do not use an asterisk, it will simply see x as a tuple, and we thus construct a 2-tuple with as first element the tuple x.

If we unpack the first tuple, we obtain a 4-tuple where the first three elements originate from x followed by 5.

Advertisement