2012-04-03 9 views
11

हम जानते हैं, अद्यतन - थ्रेड सुरक्षित संचालन है। इसका मतलब यह है कि जब आप कार्य करें:Django। थ्रेड सुरक्षित अद्यतन या बनाएँ।

SomeModel.objects.filter(id=1).update(some_field=100) 

बजाय:

sm = SomeModel.objects.get(id=1) 
sm.some_field=100 
sm.save() 

आपका आवेदन अपेक्षाकृत धागा सुरक्षित है और आपरेशन SomeModel.objects.filter(id=1).update(some_field=100) अन्य मॉडल क्षेत्रों में डेटा को फिर से लिखने नहीं होंगे।

मेरा प्रश्न .. है वहाँ यदि कोई

SomeModel.objects.filter(id=1).update(some_field=100) 

लेकिन वस्तु यदि वह मौजूद नहीं है के निर्माण के साथ क्या करने के लिए तरीका है?

उत्तर

5
from django.db import IntegrityError 

def update_or_create(model, filter_kwargs, update_kwargs) 
    if not model.objects.filter(**filter_kwargs).update(**update_kwargs): 
     kwargs = filter_kwargs.copy() 
     kwargs.update(update_kwargs) 
     try: 
      model.objects.create(**kwargs) 
     except IntegrityError: 
      if not model.objects.filter(**filter_kwargs).update(**update_kwargs): 
       raise # re-raise IntegrityError 

मुझे लगता है कि प्रश्न में प्रदान किया गया कोड बहुत ही प्रदर्शनकारी नहीं है: कौन मॉडल के लिए आईडी सेट करना चाहता है? चलें मान हम इस की जरूरत है, और हम एक साथ ऑपरेशन:

def thread1(): 
    update_or_create(SomeModel, {'some_unique_field':1}, {'some_field': 1}) 

def thread2(): 
    update_or_create(SomeModel, {'some_unique_field':1}, {'some_field': 2}) 

update_or_create के साथ समारोह, निर्भर करता है धागा आता है, जिस पर पहले वस्तु बनाया गया और कोई अपवाद के साथ अद्यतन किया जाएगा। यह धागा सुरक्षित हो जाएगा, लेकिन स्पष्ट रूप से कम इस्तेमाल किया है: SomeModek.objects.get(some__unique_field=1).some_field की रेस स्थिति मूल्य पर निर्भर करता है हो सकता है 1 या 2

Django एफ वस्तुओं प्रदान करता है, इसलिए हम अपने कोड का उन्नयन कर सकते हैं:

from django.db.models import F 

def thread1(): 
    update_or_create(SomeModel, 
        {'some_unique_field':1}, 
        {'some_field': F('some_field') + 1}) 

def thread2(): 
    update_or_create(SomeModel, 
        {'some_unique_field':1}, 
        {'some_field': F('some_field') + 2}) 
+0

यदि कोई अन्य प्रक्रिया ऑब्जेक्ट को दो पंक्तियों के बीच बनाती है, तो create() कॉल एक इंटीग्रिटी एरर बढ़ाएगी। आप आईडी() कॉल में आईडी सेट नहीं कर रहे हैं। – GDorn

+0

ठीक है, आप सही है, इसे IntegrityError के बारे में परवाह करना चाहिए। कोड संपादित करेंगे। – Nik

+0

ध्यान रखें कि आपने जो पोस्ट किया है वह पहले से ही django की क्वेरीसेट के dev संस्करण में है: https://docs.djangoproject.com/en/dev/ref/models/querysets/#update-or-create –

0

आप Django के अंतर्निहित get_or_create का उपयोग कर सकते हैं, लेकिन यह क्वेरीसेट के बजाए मॉडल पर ही चलता है।

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

me = SomeModel.objects.get_or_create(id=1) 
me.some_field = 100 
me.save() 

आप एक से अधिक थ्रेड है, तो आपके एप्लिकेशन निर्धारित करने के लिए मॉडल का जो उदाहरण सही है की आवश्यकता होगी। आम तौर पर मैं जो करता हूं वह डेटाबेस से मॉडल को ताज़ा करता है, परिवर्तन करता है, और फिर इसे सहेजता है, इसलिए आपके पास डिस्कनेक्ट किए गए राज्य में लंबा समय नहीं है।

+0

हाँ, और मैं करता हूं, लेकिन यह थ्रेड सुरक्षित नहीं है। यह 'UPDATE एम SET field_1 = old_value1, field_2 = old_value2, some_field = 100' की बजाय क्वेरी को उत्पन्न करेगा 'UPDATE m SET some_field = 100'। –

+0

मैं देख रहा हूं कि आप क्या कह रहे हैं। ऐसा करने के लिए कोई धागा सुरक्षित तरीका नहीं है। यदि आप एकाधिक धागे का उपयोग कर रहे हैं तो आपको इसे सहेजने से पहले डेटाबेस से नवीनतम प्राप्त करना चाहिए। – Jordan

+0

वैसे, जो कोड मैंने ऊपर पोस्ट किया है वह "थ्रेड सुरक्षित" होगा क्योंकि जब तक आप अपडेट करना चाहते हैं, हर बार get_or_create करते हैं, तो यह डेटाबेस से रीफ्रेश होगा। – Jordan

0

डीजेंगो में अपडेट करना असंभव है, अद्यतन के साथ। लेकिन क्वेरीसमूह अद्यतन फ़िल्टर किए गए क्षेत्रों की विधि वापसी संख्या तो आप कर सकते हैं:

from django.db import router, connections, transaction 

class MySuperManager(models.Manager): 
    def _lock_table(self, lock='ACCESS EXCLUSIVE'): 
     cursor = connections[router.db_for_write(self.model)] 
     cursor.execute(
      'LOCK TABLE %s IN %s MODE' % (self.model._meta.db_table, lock) 
     ) 

    def create_or_update(self, id, **update_fields): 
     with transaction.commit_on_success():    
      self.lock_table() 
      if not self.get_query_set().filter(id=id).update(**update_fields): 
       self.model(id=id, **update_fields).save() 

इस उदाहरण यदि postgres के लिए, आप इसे एसक्यूएल कोड के बिना उपयोग कर सकते हैं, लेकिन अद्यतन या ऑपरेशन सम्मिलित परमाणु नहीं होगा। यदि आप टेबल पर लॉक बनाते हैं तो आप सुनिश्चित होंगे कि दो ऑब्जेक्ट्स दो अन्य धागे में नहीं बनाए जाएंगे।

+0

यह अभी भी 'INSERT INTO VALUES जैसे क्वेरी जेनरेट करेगा '। इसका मतलब है, कि अगर हम एक थ्रेड 'एोन_फील्ड' में अपडेट करेंगे और दूसरे थ्रेड में हम 'एसेकंड_फील्ड' अपडेट करेंगे - हमें परेशानी होगी। अंतिम अद्यतनकर्ता पुराने डेटा के साथ सभी अद्यतन फ़ील्ड मिटा देगा। ** टेबल लॉक - यहां विरोधी समाधान। ** यह समय-समय पर अपवाद उठाएगा, लेकिन मुसीबत की जड़ हल नहीं करेगा। –

1

आप मैन्युअल लेनदेन प्रबंधन के साथ संयोजन में django की select_for_update() विधि (और बैकएंड जो पंक्ति-स्तर लॉकिंग का समर्थन करता है, जैसे PostgreSQL)।

try: 
    with transaction.commit_on_success(): 
     SomeModel.objects.create(pk=1, some_field=100) 
except IntegrityError: #unique id already exists, so update instead 
    with transaction.commit_on_success(): 
     object = SomeModel.objects.select_for_update().get(pk=1) 
     object.some_field=100 
     object.save() 

ध्यान दें कि यदि किसी अन्य प्रक्रिया दो प्रश्नों के बीच वस्तु को हटा देता है, तो आप एक SomeModel.DoesNotExist अपवाद मिल जाएगा।

Django 1.7 और ऊपर भी परमाणु संचालन समर्थन और अंतर्निहित update_or_create() विधि है।

+0

'update_or_create'' केवल Django> = 1.7 – chaim

0

मुझे लगता है कि अगर आपके परमाणु संचालन पर महत्वपूर्ण मांग है।आप इसे Django ORM स्तर के बजाय डेटाबेस स्तर में बेहतर ढंग से डिज़ाइन करेंगे।

Django ORM सिस्टम प्रदर्शन और सुरक्षा के बजाय सुविधा पर ध्यान केंद्रित कर रहा है। आपको स्वचालित रूप से स्वचालित जेनरेट किए गए SQL को अनुकूलित करना होगा।

अधिकांश उत्पादक डेटाबेस में "लेनदेन" डेटाबेस लॉक और रोलबैक अच्छी तरह से प्रदान करता है।

मैशप (हाइब्रिड) सिस्टम में, या कहें कि आपके सिस्टम ने लॉगिंग, आंकड़ों जैसे कुछ तीसरे भाग घटक जोड़े हैं। विभिन्न ढांचे या यहां तक ​​कि भाषा में आवेदन एक ही समय में डेटाबेस तक पहुंच सकता है, इस मामले में डीजेगो में सुरक्षित धागा पर्याप्त नहीं है।

+0

में देखें। मैं सिर्फ डीजेंगो को मॉडल सेव पर सभी फ़ील्ड अपडेट नहीं करना चाहता हूं। सभी डेटाबेस स्तर समाधान यहां काम नहीं करेंगे। चूंकि django खुद को मॉडल उदाहरण से ** पुराने मान ** लेता है, और उनके साथ मॉडल अपडेट करता है, भले ही हम ** कोड में केवल एक फ़ील्ड ** बदल दें। –

+0

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

+0

सेलेरी यहां अधिक है। :) पहले से django dev शाखा में 'update_or_create', इसलिए सवाल वास्तविक नहीं है। –

-3
SomeModel.objects.filter(id=1).update(set__some_field=100) 
+0

कृपया अपना कोड समझाएं! आपका जवाब वर्तमान में बंद होने के लिए मतदान किया जा रहा है। –

संबंधित मुद्दे