Django IntegerField को विकल्पों द्वारा सेट करें…… नाम


109

जब आपके पास एक विकल्प विकल्प के साथ एक मॉडल फ़ील्ड होता है, तो आपके पास मानव पठनीय नामों से जुड़े कुछ जादू मूल्य होते हैं। क्या Django में मान के बजाय मानव पठनीय नाम से इन क्षेत्रों को सेट करने का एक सुविधाजनक तरीका है?

इस मॉडल पर विचार करें:

class Thing(models.Model):
  PRIORITIES = (
    (0, 'Low'),
    (1, 'Normal'),
    (2, 'High'),
  )

  priority = models.IntegerField(default=0, choices=PRIORITIES)

कुछ बिंदु पर हमारे पास एक थिंग उदाहरण है और हम इसकी प्राथमिकता निर्धारित करना चाहते हैं। जाहिर है आप कर सकते हैं,

thing.priority = 1

लेकिन यह आपको प्राथमिकताओं के मूल्य-नाम मानचित्रण को याद करने के लिए मजबूर करता है। यह काम नहीं करता है:

thing.priority = 'Normal' # Throws ValueError on .save()

वर्तमान में मेरे पास यह मूर्खतापूर्ण काम है:

thing.priority = dict((key,value) for (value,key) in Thing.PRIORITIES)['Normal']

लेकिन यह क्लूनी है। यह देखते हुए कि यह परिदृश्य कितना सामान्य हो सकता है मैं सोच रहा था कि किसी के पास बेहतर समाधान हो। क्या चुनाव नाम से फ़ील्ड सेट करने के लिए कुछ फ़ील्ड विधि है जिसे मैंने पूरी तरह से अनदेखा कर दिया है?

जवाबों:


164

जैसा कि यहां देखा गया है । फिर आप एक ऐसे शब्द का उपयोग कर सकते हैं जो उचित पूर्णांक का प्रतिनिधित्व करता है।

इस तरह:

LOW = 0
NORMAL = 1
HIGH = 2
STATUS_CHOICES = (
    (LOW, 'Low'),
    (NORMAL, 'Normal'),
    (HIGH, 'High'),
)

फिर वे डीबी में अभी भी पूर्णांक हैं।

उपयोग होगा thing.priority = Thing.NORMAL


2
यह विषय पर एक अच्छी तरह से विस्तृत ब्लॉग पोस्टिंग है। Google के साथ बहुत मुश्किल से धन्यवाद मिला।
अलेक्जेंडर लजबर्गबर्ग

1
एफडब्ल्यूआईडब्ल्यू, अगर आपको इसे एक शाब्दिक स्ट्रिंग (शायद एक फॉर्म, उपयोगकर्ता इनपुट, या समान) से सेट करने की आवश्यकता है, तो आप बस ऐसा कर सकते हैं: thing.priority = getattr (चीज, strvalue.upper ())।
मैरोनी

1
वास्तव में ब्लॉग पर एनकैप्सुलेशन अनुभाग पसंद है।
नाथन केलर

मुझे एक समस्या है: मैं हमेशा व्यवस्थापक पर डिफ़ॉल्ट मान देखता हूं! मैंने परीक्षण किया है कि मूल्य वास्तव में बदलता है! अब मुझे क्या करना चाहिए?
महदी

यह जाने का तरीका है लेकिन सावधान रहें: यदि आप भविष्य में विकल्प जोड़ते या हटाते हैं, तो आपकी संख्या अनुक्रमिक नहीं होगी। आप संभवतः पदावनत विकल्पों पर टिप्पणी कर सकते हैं ताकि भविष्य में भ्रम न हो और आप डीबी टकरावों में न चलें।
grokpot

7

मैं शायद एक बार और सभी के लिए रिवर्स-लुकिंग तानाशाही स्थापित करूंगा, लेकिन अगर मेरे पास ऐसा नहीं होता तो मैं इसका उपयोग कर सकता:

thing.priority = next(value for value, name in Thing.PRIORITIES
                      if name=='Normal')

जो मक्खी पर तानाशाह का निर्माण करने से ज्यादा आसान लगता है, उसे फिर से टॉस करना; ;-)।


हाँ, हुकुम को उछालना थोड़ा मूर्खतापूर्ण है, अब जब आप इसे कहते हैं। :)
अलेक्जेंडर लेजबर्गबर्ग

7

यहाँ एक फ़ील्ड प्रकार है जो मैंने कुछ मिनट पहले लिखा था कि मुझे लगता है कि आप जो चाहते हैं वह करता है। इसके निर्माता को एक तर्क 'विकल्प' की आवश्यकता होती है, जो या तो एक ही प्रारूप में 2-ट्यूपल का एक हिस्सा हो सकता है, जो कि IntegerField के विकल्प के विकल्प के रूप में हो सकता है, या इसके बजाय नामों की एक सरल सूची (यानी ChoiceField ('Low', 'Normal'), 'उच्च'), डिफ़ॉल्ट = 'निम्न'))। क्लास आपके लिए स्ट्रिंग से लेकर मैपिंग तक का ध्यान रखती है, आप कभी भी इंट को नहीं देख पाते हैं।

  class ChoiceField(models.IntegerField):
    def __init__(self, choices, **kwargs):
        if not hasattr(choices[0],'__iter__'):
            choices = zip(range(len(choices)), choices)

        self.val2choice = dict(choices)
        self.choice2val = dict((v,k) for k,v in choices)

        kwargs['choices'] = choices
        super(models.IntegerField, self).__init__(**kwargs)

    def to_python(self, value):
        return self.val2choice[value]

    def get_db_prep_value(self, choice):
        return self.choice2val[choice]

1
यह बुरा नहीं है एलन। धन्यवाद!
अलेक्जेंडर लजबर्गबर्ग

5

मैं लगातार परिभाषित करने के तरीके की सराहना करता हूं लेकिन मेरा मानना ​​है कि एनम प्रकार इस कार्य के लिए सबसे अच्छा है। वे एक ही समय में एक आइटम के लिए पूर्णांक और एक स्ट्रिंग का प्रतिनिधित्व कर सकते हैं, जबकि आपके कोड को अधिक पठनीय रखते हैं।

एन्थेम को 3.4 संस्करण में पायथन में पेश किया गया था। आप उपयोग कर रहे हैं किसी भी कम (v2.x के रूप में इस तरह के) आप अभी भी स्थापित करके यह हो सकता है बैकपोर्टेड पैकेज : pip install enum34

# myapp/fields.py
from enum import Enum    


class ChoiceEnum(Enum):

    @classmethod
    def choices(cls):
        choices = list()

        # Loop thru defined enums
        for item in cls:
            choices.append((item.value, item.name))

        # return as tuple
        return tuple(choices)

    def __str__(self):
        return self.name

    def __int__(self):
        return self.value


class Language(ChoiceEnum):
    Python = 1
    Ruby = 2
    Java = 3
    PHP = 4
    Cpp = 5

# Uh oh
Language.Cpp._name_ = 'C++'

यह सब बहुत सुंदर है। आप ChoiceEnumअपनी खुद की परिभाषा बनाने और उन्हें एक मॉडल परिभाषा में उपयोग करने के लिए इनहेरिट कर सकते हैं जैसे:

from django.db import models
from myapp.fields import Language

class MyModel(models.Model):
    language = models.IntegerField(choices=Language.choices(), default=int(Language.Python))
    # ...

जैसा कि आप अनुमान लगा सकते हैं कि प्रश्न केक पर आइसिंग है:

MyModel.objects.filter(language=int(Language.Ruby))
# or if you don't prefer `__int__` method..
MyModel.objects.filter(language=Language.Ruby.value)

स्ट्रिंग में उनका प्रतिनिधित्व करना भी आसान है:

# Get the enum item
lang = Language(some_instance.language)

print(str(lang))
# or if you don't prefer `__str__` method..
print(lang.name)

# Same as get_FOO_display
lang.name == some_instance.get_language_display()

1
आप की तरह एक आधार वर्ग को लागू नहीं करना चाहते हैं ChoiceEnum, तो आप उपयोग कर सकते हैं .valueऔर .name@kirpit का वर्णन के रूप में और के उपयोग को बदलने के choices()साथ tuple([(x.value, x.name) for x in cls])--either एक समारोह (सूखी) में सीधे क्षेत्र के निर्माता में या।
सेठ

4
class Sequence(object):
    def __init__(self, func, *opts):
        keys = func(len(opts))
        self.attrs = dict(zip([t[0] for t in opts], keys))
        self.choices = zip(keys, [t[1] for t in opts])
        self.labels = dict(self.choices)
    def __getattr__(self, a):
        return self.attrs[a]
    def __getitem__(self, k):
        return self.labels[k]
    def __len__(self):
        return len(self.choices)
    def __iter__(self):
        return iter(self.choices)
    def __deepcopy__(self, memo):
        return self

class Enum(Sequence):
    def __init__(self, *opts):
        return super(Enum, self).__init__(range, *opts)

class Flags(Sequence):
    def __init__(self, *opts):
        return super(Flags, self).__init__(lambda l: [1<<i for i in xrange(l)], *opts)

इसे इस तरह उपयोग करें:

Priorities = Enum(
    ('LOW', 'Low'),
    ('NORMAL', 'Normal'),
    ('HIGH', 'High')
)

priority = models.IntegerField(default=Priorities.LOW, choices=Priorities)

3

Django 3.0 के रूप में, आप उपयोग कर सकते हैं:

class ThingPriority(models.IntegerChoices):
    LOW = 0, 'Low'
    NORMAL = 1, 'Normal'
    HIGH = 2, 'High'


class Thing(models.Model):
    priority = models.IntegerField(default=ThingPriority.LOW, choices=ThingPriority.choices)

# then in your code
thing = get_my_thing()
thing.priority = ThingPriority.HIGH

1

बस अपने नंबरों को उन मानव पठनीय मानों से बदलें, जिन्हें आप पसंद करते हैं। जैसे की:

PRIORITIES = (
('LOW', 'Low'),
('NORMAL', 'Normal'),
('HIGH', 'High'),
)

यह मानव को पठनीय बनाता है, हालाँकि, आपको अपने स्वयं के आदेश को परिभाषित करना होगा।


1

मेरा उत्तर बहुत देर से आया है और आजकल के Django विशेषज्ञों को स्पष्ट लग सकता है, लेकिन जो भी यहाँ भूमि पर है, मैंने हाल ही में django-model-utils द्वारा लाया गया एक बहुत ही सुंदर समाधान खोजा है: https://django-model-utils.readtholocs.io/ एन / नवीनतम / utilities.html # विकल्प

यह पैकेज आपको तीन-नलियों वाले विकल्पों को परिभाषित करने की अनुमति देता है:

  • पहला आइटम डेटाबेस मान है
  • दूसरा आइटम एक कोड-पठनीय मूल्य है
  • तीसरा आइटम मानव-पठनीय मूल्य है

तो यहाँ आप क्या कर सकते हैं:

from model_utils import Choices

class Thing(models.Model):
    PRIORITIES = Choices(
        (0, 'low', 'Low'),
        (1, 'normal', 'Normal'),
        (2, 'high', 'High'),
      )

    priority = models.IntegerField(default=PRIORITIES.normal, choices=PRIORITIES)

thing.priority = getattr(Thing.PRIORITIES.Normal)

इस तरफ:

  • आप वास्तव में अपने क्षेत्र के मूल्य को चुनने के लिए अपने मानव-पठनीय मूल्य का उपयोग कर सकते हैं (मेरे मामले में, यह उपयोगी है क्योंकि मैं जंगली सामग्री को स्क्रैप कर रहा हूं और इसे सामान्य तरीके से संग्रहीत कर रहा हूं)
  • आपके डेटाबेस में एक साफ मान संग्रहीत किया जाता है
  • आपके पास करने के लिए कुछ भी नहीं है गैर;

का आनंद लें :)


1
Django-model-utils को लाने के लिए पूरी तरह से मान्य है। पठनीयता बढ़ाने के लिए एक छोटा सुझाव; डिफ़ॉल्ट पैरामीटर पर डॉट-नोटेशन का भी उपयोग करें: priority = models.IntegerField(default=PRIORITIES.Low, choices=PRIORITIES)(कहने की जरूरत नहीं है, प्राथमिकता असाइनमेंट को ऐसा करने के लिए थिंग-क्लास के अंदर होने का संकेत देना होगा)। अजगर पहचानकर्ता के लिए लोअरकेस शब्दों / वर्णों का उपयोग करने पर भी विचार करें, क्योंकि आप एक वर्ग का उल्लेख नहीं कर रहे हैं, लेकिन इसके बजाय एक पैरामीटर (आपकी पसंद तब बन जाएगा: (0, 'low', 'Low'),और इसके बाद )।
किम

0

मूल रूप से मैंने @ एलन के उत्तर के संशोधित संस्करण का उपयोग किया:

from enum import IntEnum, EnumMeta

class IntegerChoiceField(models.IntegerField):
    def __init__(self, choices, **kwargs):
        if hasattr(choices, '__iter__') and isinstance(choices, EnumMeta):
            choices = list(zip(range(1, len(choices) + 1), [member.name for member in list(choices)]))

        kwargs['choices'] = choices
        super(models.IntegerField, self).__init__(**kwargs)

    def to_python(self, value):
        return self.choices(value)

    def get_db_prep_value(self, choice):
        return self.choices[choice]

models.IntegerChoiceField = IntegerChoiceField

GEAR = IntEnum('GEAR', 'HEAD BODY FEET HANDS SHIELD NECK UNKNOWN')

class Gear(Item, models.Model):
    # Safe to assume last element is largest value member of an enum?
    #type = models.IntegerChoiceField(GEAR, default=list(GEAR)[-1].name)
    largest_member = GEAR(max([member.value for member in list(GEAR)]))
    type = models.IntegerChoiceField(GEAR, default=largest_member)

    def __init__(self, *args, **kwargs):
        super(Gear, self).__init__(*args, **kwargs)

        for member in GEAR:
            setattr(self, member.name, member.value)

print(Gear().HEAD, (Gear().HEAD == GEAR.HEAD.value))

django-enumfieldsपैकेज पैकेज के साथ सरलीकृत जो अब मैं उपयोग करता हूं:

from enumfields import EnumIntegerField, IntEnum

GEAR = IntEnum('GEAR', 'HEAD BODY FEET HANDS SHIELD NECK UNKNOWN')

class Gear(Item, models.Model):
    # Safe to assume last element is largest value member of an enum?
    type = EnumIntegerField(GEAR, default=list(GEAR)[-1])
    #largest_member = GEAR(max([member.value for member in list(GEAR)]))
    #type = EnumIntegerField(GEAR, default=largest_member)

    def __init__(self, *args, **kwargs):
        super(Gear, self).__init__(*args, **kwargs)

        for member in GEAR:
            setattr(self, member.name, member.value)

0

मॉडल का विकल्प विकल्प एक अनुक्रम को स्वीकार करता है जिसमें इस क्षेत्र के लिए विकल्पों के रूप में उपयोग करने के लिए वास्तव में दो वस्तुओं (जैसे [(ए, बी), (ए, बी) ...] के पुनरावृत्तियों को शामिल किया गया है।

इसके अलावा, Django गणन प्रकार प्रदान करता है जिसे आप संक्षिप्त रूप में विकल्पों को परिभाषित करने के लिए उप-वर्ग कर सकते हैं:

class ThingPriority(models.IntegerChoices):
    LOW = 0, _('Low')
    NORMAL = 1, _('Normal')
    HIGH = 2, _('High')

class Thing(models.Model):
    priority = models.IntegerField(default=ThingPriority.NORMAL, choices=ThingPriority.choices)

Django इस टपल के अंत में एक अतिरिक्त स्ट्रिंग मान जोड़ने का समर्थन करता है जिसका उपयोग मानव-पठनीय नाम, या लेबल के रूप में किया जाता है। लेबल एक आलसी अनुवाद योग्य स्ट्रिंग हो सकता है।

   # in your code 
   thing = get_thing() # instance of Thing
   thing.priority = ThingPriority.LOW

नोट: का उपयोग कर आप उपयोग कर सकते हैं ThingPriority.HIGH, ThingPriority.['HIGH']या ThingPriority(0)पहुँच या देखने enum सदस्यों के लिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.