[PYTHON] Créer un champ personnalisé où enum peut être spécifié dans les choix

Puisque enum34, qui devrait être introduit dans python 3.4 et rétroporté vers python 2.4, est pratique, j'ai essayé de le spécifier dans l'option de choix de CharField, mais je ne pouvais pas l'utiliser tel quel, alors je l'ai fait.

URL de référence

Environnement d'exploitation

choicefield.py


# -*- encoding: utf-8 -*-
from django.db import models
from django.utils.functional import curry
import enum


class ChoiceField(models.CharField):
    u"""énumération dans les choix.Un champ personnalisé qui vous permet de spécifier une sous-classe d'Enum.
    """
    __metaclass__ = models.SubfieldBase

    def __init__(self, *args, **kwargs):
        self.enum = kwargs.get("choices", None)
        if self.enum and issubclass(self.enum, enum.Enum):
            kwargs["choices"] = self._from_enum()
            kwargs["max_length"] = self._calc_max_length()
        super(ChoiceField, self).__init__(*args, **kwargs)

    def _calc_max_length(self):
        u"""Recherchez la longueur de chaîne de caractères requise pour l'enregistrement dans le DB.
        """
        return max([len(unicode(item)) for item in self.enum])

    def _from_enum(self):
        u"""Convertissez en un taple qui peut être spécifié dans les choix.
        """
        return [(item, item.value) for item in self.enum]

    @staticmethod
    def _get_display(self, field):
        u"""Une méthode qui est adaptée au modèle pour l'affichage
        """
        return getattr(self, field.attname).value

    def contribute_to_class(self, cls, name, virtual_only=False):
        super(ChoiceField, self).contribute_to_class(cls, name, virtual_only)
        setattr(cls, 'get_%s_display' % self.name, curry(self._get_display, field=self))
        #Rénovation en classe Enum comme requis par MaxLengthValidator
        setattr(self.enum, '__len__', lambda x: len(unicode(x)))

    def get_prep_value(self, value):
        if isinstance(value, basestring):
            return value
        return None if value is None else unicode(value)

    def to_python(self, value):
        if not value or not isinstance(value, basestring):
            return value

        try:
            return self.enum[value]
        except KeyError:
            for m in self.enum:
                if value == m:
                    return value
                if value == m.value:
                    return m
                if value == m.name:
                    return m
                if value.endswith(m.name):
                    return m
            raise Exception('%s is not a valid value for enum %s' % (value, self.enum))

Le test ressemble à ceci

tests/tests_choicefield.py


# -*- encoding: utf-8 -*-
from django import test
from django.db import models
import enum
import os


class Gender(enum.Enum):
    __order__ = "Male Female"
    Male = u"Masculin"
    Female = u"Femme"


class Person(models.Model):
    name = models.CharField(u"Nom", max_length=255)
    gender = choices.ChoiceField(u"sexe", choices=Gender, max_length=255)

    class Meta:
        app_label = os.path.basename(os.path.abspath(os.path.join(os.path.split(__file__)[0], os.pardir)))


class PersonTest(test.TestCase):
    def test_choices(self):
        obj = Person.objects.create(name="John", gender=Gender.Male)
        self.assertEqual(Gender.Male, obj.gender)
        self.assertEqual(u"Masculin", obj.get_gender_display())

    def test_get(self):
        obj1 = Person.objects.create(name="John", gender=Gender.Male)
        obj2 = Person.objects.get(name="John", gender=Gender.Male)
        self.assertEqual(obj1, obj2)

    def test_filter_in(self):
        obj1 = Person.objects.create(name="John", gender=Gender.Male)
        obj2 = Person.objects.create(name="Jane", gender=Gender.Female)
        actual = Person.objects.filter(gender__in=[Gender.Male, Gender.Female])
        self.assertItemsEqual([obj1, obj2], actual)

Recommended Posts

Créer un champ personnalisé où enum peut être spécifié dans les choix
[Peut être fait en 10 minutes] Créez rapidement un site Web local avec Django
Je souhaite créer une file d'attente prioritaire pouvant être mise à jour avec Python (2.7)
Goroutine (contrôle parallèle) utilisable sur le terrain
Goroutine utilisable sur le terrain (édition errgroup.Group)
Index d'évaluation pouvant être spécifié pour GridSearchCV de sklearn
Créer une fonction en Python
Créer un dictionnaire en Python
Créez une Spinbox qui peut être affichée en binaire avec Tkinter
Créez une Spinbox pouvant être affichée dans HEX avec Tkinter
Puis-je être un data scientist?
Créer un lecteur CSV avec Flask
Créer un conteneur DI avec Python
Créer un fichier binaire en Python
Créer une chaîne aléatoire en Python
Créer un bot LINE avec Django
[Python] Un programme qui trouve une paire qui peut être divisée par une valeur spécifiée
Créez une application Web qui peut être facilement visualisée avec Plotly Dash
Une histoire que heroku, qui peut se faire en 5 minutes, a en fait duré 3 jours
Je voulais faire un programme de notation polonaise inversée en Python (détermination de savoir si une chaîne de caractères peut être convertie en valeur numérique)