Django मॉडल फ़ील्ड डिफ़ॉल्ट उसी मॉडल में एक और फ़ील्ड आधारित है


91

मेरे पास एक मॉडल है जिसमें मैं एक विषय का नाम और उनके नाम शामिल करना चाहूंगा (वह डेटा कुछ हद तक अनाम है और आद्याक्षर द्वारा ट्रैक किया गया है)।

अभी, मैंने लिखा है

class Subject(models.Model):

    name = models.CharField("Name", max_length=30)
    def subject_initials(self):
        return ''.join(map(lambda x: '' if len(x)==0 else x[0],
                           self.name.split(' ')))
    # Next line is what I want to do (or something equivalent), but doesn't work with
    # NameError: name 'self' is not defined
    subject_init = models.CharField("Subject Initials", max_length=5, default=self.subject_initials)

जैसा कि अंतिम पंक्ति द्वारा इंगित किया गया है, मैं वास्तव में डेटाबेस में एक क्षेत्र (नाम से स्वतंत्र) के रूप में संग्रहीत प्रारंभिक प्राप्त करने में सक्षम होना पसंद करूंगा, लेकिन नाम फ़ील्ड के आधार पर डिफ़ॉल्ट मान के साथ आरंभ किया जाता है। हालाँकि, मैं मुद्दों के रूप में django मॉडल एक 'स्व' है प्रतीत नहीं कर रहा हूँ।

अगर मैं लाइन को बदल देता subject_init = models.CharField("Subject initials", max_length=2, default=subject_initials)हूं, तो मैं सिंकबब कर सकता हूं, लेकिन नए विषय नहीं बना सकता।

क्या Django में यह संभव है, एक कॉल करने योग्य फ़ंक्शन किसी अन्य फ़ील्ड के मान के आधार पर कुछ फ़ील्ड को डिफ़ॉल्ट देता है?

(जिज्ञासु के लिए, मैं अपने स्टोर इनीशियल्स को अलग से अलग करना चाहता हूं, यह दुर्लभ मामलों में है जहां अजीब अंतिम नाम मेरे द्वारा ट्रैक किए जा रहे लोगों की तुलना में अलग हो सकते हैं। उदाहरण के लिए, किसी और ने फैसला किया कि सब्जेक्ट 1 नाम "जॉन ओ'मालरी" इनिशियल है। "JM" के बजाय "JO" और इसे व्यवस्थापक के रूप में संपादित करना ठीक करना चाहता है।

जवाबों:


88

मॉडल निश्चित रूप से एक "स्व" है! यह सिर्फ इतना है कि आप मॉडल उदाहरण पर निर्भर होने के नाते एक मॉडल वर्ग की विशेषता को परिभाषित करने की कोशिश कर रहे हैं; यह संभव नहीं है, क्योंकि आपके वर्ग और उसकी विशेषताओं को परिभाषित करने से पहले उदाहरण मौजूद नहीं है (और नहीं कर सकते हैं)।

आप जो प्रभाव चाहते हैं, उसे पाने के लिए मॉडल वर्ग की सेव () विधि को ओवरराइड करें। उदाहरण के लिए आवश्यक परिवर्तन करें, फिर वास्तविक बचत करने के लिए सुपरक्लास पद्धति को कॉल करें। यहाँ एक त्वरित उदाहरण है।

def save(self, *args, **kwargs):
    if not self.subject_init:
        self.subject_init = self.subject_initials()
    super(Subject, self).save(*args, **kwargs)

यह प्रलेखन में ओवरराइडिंग मॉडल के तरीकों में शामिल है।


4
ध्यान दें कि पायथन 3 में आप केवल संदर्भित दलीलों में उदाहरण के रूप में super().save(*args, **kwargs)( Subject, selfतर्कों के बिना ) कॉल कर सकते हैं ।
कर्ट पीक

1
बहुत बुरा यह एक डिफ़ॉल्ट मॉडल विशेषता मान को दूसरे के आधार पर परिभाषित करने की अनुमति नहीं देता है, जो विशेष रूप से व्यवस्थापक पक्ष में (उदाहरण के लिए, एक स्व-प्रतिरक्षित क्षेत्र के लिए) उपयोगी है, क्योंकि यह इसे सहेजते समय इसे संभालता है। या मैं इसे गलत समझ रहा हूं?
वडकोरकेस्ट

18

अगर वहाँ ऐसा करने का एक बेहतर तरीका है मैं नहीं जानता, लेकिन आप कर सकते हैं एक संकेत हैंडलर का उपयोग के लिए संकेत :pre_save

from django.db.models.signals import pre_save

def default_subject(sender, instance, using):
    if not instance.subject_init:
        instance.subject_init = instance.subject_initials()

pre_save.connect(default_subject, sender=Subject)

1
आप के post_saveबजाय आयात किया pre_save
अली रसीम कोकल

1
@arkocal: सिर के लिए धन्यवाद, बस इसे ठीक कर दिया। आप ऐसे मामलों में खुद को संपादित करने का सुझाव दे सकते हैं, यह इस तरह से सामान को ठीक करने में मदद करता है :)
गाबी पुरकारू

1
ऐसा लगता है कि यह क्रियान्वयन उस **kwargsतर्क को याद कर रहा है जो सभी रिसीवर फ़ंक्शंस docs.djangoproject.com/en/2.0/topics/signals/… के अनुसार होना चाहिए ?
कर्ट पीक

दस्तावेज़ आपके द्वारा नियंत्रित किए जाने वाले कोड रास्तों के संकेतों के विरुद्ध अनुशंसा करता है । ओवरराइडिंग का स्वीकृत उत्तर save(), शायद ओवरराइडिंग में बदल गया है __init__, बेहतर है क्योंकि यह अधिक स्पष्ट है।
एडम जॉनसन

7

का उपयोग करते हुए Django संकेतों , यह काफी जल्दी किया जा सकता है, प्राप्त करने से संकेत मॉडल से।post_init

from django.db import models
import django.dispatch

class LoremIpsum(models.Model):
    name = models.CharField(
        "Name",
        max_length=30,
    )
    subject_initials = models.CharField(
        "Subject Initials",
        max_length=5,
    )

@django.dispatch.receiver(models.signals.post_init, sender=LoremIpsum)
def set_default_loremipsum_initials(sender, instance, *args, **kwargs):
    """
    Set the default value for `subject_initials` on the `instance`.

    :param sender: The `LoremIpsum` class that sent the signal.
    :param instance: The `LoremIpsum` instance that is being
        initialised.
    :return: None.
    """
    if not instance.subject_initials:
        instance.subject_initials = "".join(map(
                (lambda x: x[0] if x else ""),
                instance.name.split(" ")))

post_initसंकेत वर्ग द्वारा भेजे गए एक बार यह उदाहरण पर initialisation किया है है। इस तरह, उदाहरण nameसे पहले परीक्षण के लिए एक मान प्राप्त होता है कि क्या उसके गैर-अशक्त क्षेत्र सेट किए गए हैं।


दस्तावेज़ आपके द्वारा नियंत्रित किए जाने वाले कोड रास्तों के संकेतों के विरुद्ध अनुशंसा करता है । ओवरराइडिंग का स्वीकृत उत्तर save(), शायद ओवरराइडिंग में बदल गया है __init__, बेहतर है क्योंकि यह अधिक स्पष्ट है।
एडम जॉनसन

2

गेबी पुरकारू के उत्तर के वैकल्पिक कार्यान्वयन के रूप में , आप डेकोरेटरpre_save का उपयोग करके सिग्नल से कनेक्ट कर सकते हैं :receiver

from django.db.models.signals import pre_save
from django.dispatch import receiver


@receiver(pre_save, sender=Subject)
def default_subject(sender, instance, **kwargs):
    if not instance.subject_init:
        instance.subject_init = instance.subject_initials()

यह रिसीवर फ़ंक्शन **kwargsवाइल्डकार्ड कीवर्ड तर्क भी लेता है जो सभी सिग्नल हैंडलर को https://docs.djangoproject.com/en/2.0/topics/signals/#receiver-functions के अनुसार लेना होगा ।

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