एक django मॉडल कस्टम सेव () विधि में, आपको एक नई वस्तु की पहचान कैसे करनी चाहिए?


172

जब मैं एक नया रिकॉर्ड सहेज रहा हूं (मौजूदा रिकॉर्ड को अपडेट नहीं कर रहा हूं) तो मैं एक Django मॉडल ऑब्जेक्ट के सहेजें () पद्धति में एक विशेष कार्रवाई को ट्रिगर करना चाहता हूं।

क्या सेल्फ रिकॉर्ड की गारंटी के लिए चेक (सेल्फ! = = कोई नहीं) आवश्यक और पर्याप्त है और अपडेट नहीं किया जा रहा है? किसी विशेष मामले की अनदेखी हो सकती है?


कृपया सही उत्तर के रूप में stackoverflow.com/a/35647389/8893667 का चयन करें । जवाब कई मामलों में काम नहीं करता है जैसेUUIDField pk
कोटलिनबॉय

जवाबों:


204

अपडेट किया गया: स्पष्टीकरण के साथ जो self._stateएक निजी उदाहरण चर नहीं है, लेकिन संघर्षों से बचने के लिए उस तरीके का नाम दिया गया है, self._state.addingअब जाँच करना बेहतर तरीका है।


self.pk is None:

एक नए मॉडल ऑब्जेक्ट के भीतर सही लौटाता है, जब तक कि ऑब्जेक्ट के UUIDFieldरूप में इसके पास नहीं है primary_key

आपको जिस कोने के मामले में चिंता करनी हो सकती है, वह यह है कि आईडी के अलावा अन्य क्षेत्रों पर विशिष्टता की कमी है (उदाहरण के लिए, अन्य क्षेत्रों पर द्वितीयक अद्वितीय अनुक्रमित)। उस स्थिति में, आप अभी भी हाथ में एक नया रिकॉर्ड रख सकते हैं, लेकिन इसे बचाने में असमर्थ हैं।


20
आप का उपयोग करना चाहिए is notबजाय !=जब साथ पहचान के लिए जाँच Noneवस्तु
बेन जेम्स

3
सभी मॉडलों में एक आईडी विशेषता नहीं होती है, अर्थात एक के माध्यम से दूसरे का विस्तार करने वाला मॉडल models.OneToOneField(OtherModel, primary_key=True)। मुझे लगता है कि आपको उपयोग करने की आवश्यकता हैself.pk
AJP

4
यह कुछ मामलों में काम नहीं करता है। कृपया इस उत्तर की जाँच करें: stackoverflow.com/a/940928/145349
fjsj

5
यह सही उत्तर नहीं है। यदि UUIDFieldएक प्राथमिक कुंजी के रूप में उपयोग किया जाता है, self.pkतो कभी नहीं None
डैनियल वैन फ्लाईमेन 22

1
साइड नोट: यह उत्तर पूर्व दिनांकित UUIDField है।
डेव डब्ल्यू। स्मिथ

190

वैकल्पिक तरीके से self.pkहम self._stateमॉडल की जांच कर सकते हैं

self._state.adding is True बनाना

self._state.adding is False अद्यतन करने

मुझे यह इस पृष्ठ से मिला


12
कस्टम प्राथमिक कुंजी फ़ील्ड का उपयोग करते समय यह एकमात्र सही तरीका है।
webtweakers

9
कैसे self._state.addingकाम करता है के सभी विवरण के बारे में निश्चित नहीं है , लेकिन निष्पक्ष चेतावनी है कि यह हमेशा बराबर लगता है Falseयदि आप इसे कॉल करने के बाद जाँच कर रहे हैं super(TheModel, self).save(*args, **kwargs): github.com/django/django/blob/stable/1.10.x/django/db/models/ …
agilgur5

1
यह सही तरीका है और इसे सही उत्तर के रूप में उत्कीर्ण / सेट किया जाना चाहिए।
flungo

7
@guival: _stateनिजी नहीं है; जैसे _meta, यह फ़ील्ड नामों के साथ भ्रम से बचने के लिए एक अंडरस्कोर के साथ उपसर्ग है। (सूचना है कि यह कैसे जुड़ा हुआ दस्तावेज में प्रयोग किया जाता है।)
Ry-

2
यह सबसे अच्छा तरीका है। मैंने is_new = self._state.addingतब super(MyModel, self).save(*args, **kwargs)और फिरif is_new: my_custom_logic()
kotrfa

45

जाँच self.idमान लेता है कि idमॉडल के लिए प्राथमिक कुंजी है। एक और अधिक सामान्य तरीका pk शॉर्टकट का उपयोग करना होगा ।

is_new = self.pk is None


15
प्रो युक्ति: इस डाल से पहलेsuper(...).save()
sbdchd

39

के लिए चेक self.pk == Noneहै नहीं करता है, तो वस्तु डाला जाता है या डेटाबेस में अद्यतन किया जा रहा है निर्धारित करने के लिए पर्याप्त है।

Django O / RM में विशेष रूप से नॉटी हैक की सुविधा है जो मूल रूप से यह जांचने के लिए है कि क्या पीके स्थिति में कुछ है और यदि ऐसा कोई अद्यतन करता है, अन्यथा एक INSERT करें (यह PKS नहीं है तो INSERT के लिए अनुकूलित हो जाता है)।

ऐसा करने का कारण यह है कि जब आप एक वस्तु बनाई जाती है तो आपको पीके सेट करने की अनुमति होती है। हालांकि आम नहीं है जहां आपके पास प्राथमिक कुंजी के लिए एक अनुक्रम स्तंभ है, यह अन्य प्रकार के प्राथमिक कुंजी फ़ील्ड के लिए पकड़ नहीं रखता है।

यदि आप वास्तव में जानना चाहते हैं कि आपको ओ / आरएम क्या करना है और डेटाबेस में देखना है।

बेशक आपके पास अपने कोड में एक विशिष्ट मामला है और इसके लिए यह काफी संभावना है self.pk == Noneकि आप सभी को बताता है जो आपको जानना चाहिए, लेकिन यह एक सामान्य समाधान नहीं है।


अच्छी बात! मैं अपने आवेदन में इस से दूर हो सकता हूं (कोई भी प्राथमिक कुंजी की जांच नहीं कर सकता) क्योंकि मैंने कभी भी नई वस्तुओं के लिए पीके निर्धारित नहीं किया है। लेकिन यह निश्चित रूप से पुन: प्रयोज्य प्लगइन या ढांचे के हिस्से के लिए एक अच्छी जांच नहीं होगी।
माइकएन

1
यह विशेष रूप से सच है जब आप प्राथमिक कुंजी स्वयं और डेटाबेस के माध्यम से असाइन करते हैं। उस मामले में सबसे महत्वपूर्ण बात यह है कि db की यात्रा करना है।
कांस्टेंटाइन एम

1
यहां तक ​​कि अगर आपके आवेदन कोड में स्पष्ट रूप से आपके परीक्षण मामलों के लिए जुड़नार निर्दिष्ट नहीं करते हैं। हालांकि, जैसा कि वे आमतौर पर परीक्षणों से पहले लोड किया जाता है, यह एक समस्या नहीं हो सकती है।
रिसादिंह

1
UUIDFieldप्राथमिक कुंजी के रूप में उपयोग करने के मामले में यह विशेष रूप से सच है : कुंजी DB स्तर पर आबादी नहीं है, इसलिए self.pkहमेशा होती है True
डेनियल वैन फ्लाईमेन 22

10

आप बस post_save सिग्नल से कनेक्ट कर सकते हैं जो एक "बनाया" kwargs भेजता है, अगर सही है, तो आपकी ऑब्जेक्ट डाली गई है।

http://docs.djangoproject.com/en/stable/ref/signals/#post-save


8
यदि संभावित रूप से बहुत अधिक लोड हो तो वह दौड़ की स्थिति पैदा कर सकता है। ऐसा इसलिए है क्योंकि पोस्ट_सैव सिग्नल सेव पर भेजा जाता है, लेकिन इससे पहले कि ट्रांजेक्शन हो सके। यह समस्याग्रस्त हो सकता है और चीजों को डिबग करना बहुत मुश्किल हो सकता है।
हाबिल मोहलर

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

7

self.idऔर force_insertध्वज की जाँच करें ।

if not self.pk or kwargs.get('force_insert', False):
    self.created = True

# call save method.
super(self.__class__, self).save(*args, **kwargs)

#Do all your post save actions in the if block.
if getattr(self, 'created', False):
    # So something
    # Do something else

यह आसान है क्योंकि आपके नए बनाए गए ऑब्जेक्ट (स्वयं) का pkमूल्य है


5

मुझे इस वार्तालाप में बहुत देर हो गई है, लेकिन मैं स्वयं के साथ एक समस्या में भाग गया। जब यह डिफ़ॉल्ट रूप से जुड़ा हुआ है, तो इसे पॉप्युलेट किया जा रहा है।

जिस तरह से मैं इसके आसपास गया वह मॉडल में एक date_created फ़ील्ड जोड़ रहा है

date_created = models.DateTimeField(auto_now_add=True)

यहां से आप जा सकते हैं

created = self.date_created is None


4

एक समाधान के लिए भी काम करता है जब आपके पास एक UUIDFieldप्राथमिक कुंजी के रूप में होता है (जो कि अन्य ने नोट किया है कि Noneअगर आप अभी ओवरराइड नहीं करते हैं save), तो आप Django के post_save सिग्नल में प्लग कर सकते हैं । इसे अपने मॉडल में जोड़ें :

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

@receiver(post_save, sender=MyModel)
def mymodel_saved(sender, instance, created, **kwargs):
    if created:
        # do extra work on your instance, e.g.
        # instance.generate_avatar()
        # instance.send_email_notification()
        pass

यह कॉलबैक saveविधि को अवरुद्ध कर देगा , जिससे आप ट्रिगर नोटिफिकेशन जैसी चीजों को कर सकते हैं या अपनी प्रतिक्रिया वापस तार पर भेजे जाने से पहले मॉडल को अपडेट कर सकते हैं, चाहे आप फॉर्म का उपयोग कर रहे हों या AJAX कॉल के लिए Django REST फ्रेमवर्क। बेशक, अपने उपयोगकर्ताओं को प्रतीक्षा में रखने के बजाय नौकरी की कतार में जिम्मेदारी से और भारी कार्यों का उपयोग करें :)


3

आईडी के बजाय पीके का उपयोग करें :

if not self.pk:
  do_something()

1

यह ऐसा करने का सामान्य तरीका है।

पहली बार db में सेव करते समय id दी जाएगी


0

क्या यह उपरोक्त सभी परिदृश्यों के लिए काम करेगा?

if self.pk is not None and <ModelName>.objects.filter(pk=self.pk).exists():
...

यह एक अतिरिक्त डेटाबेस हिट का कारण होगा।
डेविड शुमान

0
> def save_model(self, request, obj, form, change):
>         if form.instance._state.adding:
>             form.instance.author = request.user
>             super().save_model(request, obj, form, change)
>         else:
>             obj.updated_by = request.user.username
> 
>             super().save_model(request, obj, form, change)

Cleaned_data.get () का उपयोग करते हुए, मैं यह निर्धारित करने में सक्षम था कि क्या मेरे पास कोई उदाहरण है, मेरे पास एक CharField भी था जहां अशक्त और रिक्त जहां सत्य है। यह लॉग इन यूजर के अनुसार प्रत्येक अपडेट पर अपडेट होगा
स्वालान अगस्टे

-3

यह जानने के लिए कि आप ऑब्जेक्ट (डेटा) को अपडेट कर रहे हैं या डाल रहे हैं, self.instance.fieldnameअपने फॉर्म में उपयोग करें। अपने रूप में एक साफ फ़ंक्शन को परिभाषित करें और जांचें कि क्या वर्तमान मूल्य प्रविष्टि पहले की तरह है, यदि नहीं तो आप इसे अपडेट कर रहे हैं।

self.instanceऔर self.instance.fieldnameनए मूल्य के साथ तुलना करें

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