9

मैं Django का उपयोग करके संग्रहीत उपयोगकर्ताओं द्वारा संपादित किए गए बड़े टेक्स्ट फ़ील्ड का पूरा इतिहास प्राप्त करना चाहता हूं।Django में टेक्स्ट पूर्ण इतिहास कैसे करें?

मैंने देखा है परियोजनाओं:

मैं एक विशेष यूज-केस कि शायद के दायरे से बाहर गिर जाता है क्या ये परियोजनाएं प्रदान करती हैं। इसके अलावा, मैं इस परियोजना से कितनी अच्छी तरह से दस्तावेज, परीक्षण और अद्यतन किया गया है, इस बारे में सावधान हूं। किसी भी घटना में, यहाँ समस्या मैं का सामना करना पड़ता है:

मैंने एक मॉडल, likeso:

from django.db import models 

class Document(models.Model): 
    text_field = models.TextField() 

यह पाठ क्षेत्र बड़ा हो सकता है - 40k से अधिक - और मैं एक स्वतः सहेजना विशेषता यह है कि बचाता है करना चाहते हैं क्षेत्र हर 30 सेकंड या तो। यह डाटाबेस को अनावश्यक रूप से बड़ा कर सकता है, जाहिर है, अगर 40k प्रत्येक पर बहुत से बचाता है (शायद ज़ेड होने पर शायद 10k)। सबसे अच्छा समाधान जो मैं सोच सकता हूं वह सबसे हालिया सहेजे गए संस्करण और नए संस्करण के बीच अंतर रखना है।

हालांकि, मैं समांतर अद्यतनों वाली दौड़ स्थितियों के बारे में चिंतित हूं।

  1. HTTP लेन-देन रेस स्थिति:: वहाँ दो अलग-अलग जाति की स्थिति है कि (दूसरा और अधिक गंभीर पहले की तुलना में) मन में आते हैं उपयोगकर्ता A और उपयोगकर्ता B अनुरोध दस्तावेज़ X 0 और व्यक्तिगत रूप से उत्पादन परिवर्तन करते हैं, एक्सए और एक्सबी। एक्सए बचाया गया है, एक्स 0 और एक्सए के बीच का अंतर "एक्सए -0" ("कम नहीं") है, एक्सए अब डेटाबेस में आधिकारिक संस्करण के रूप में संग्रहीत किया जा रहा है। यदि एक्सबी बाद में बचाता है, तो यह एक्सए को ओवरराइट करता है, एक्सबी-ए ("बी कम ए") होता है।

    आदर्श नहीं होने पर, मैं इस व्यवहार से अत्यधिक चिंतित नहीं हूं। दस्तावेज़ एक-दूसरे को ओवरराइट कर रहे हैं, और उपयोगकर्ता ए और बी एक-दूसरे से अनजान हो सकते हैं (प्रत्येक दस्तावेज़ X0 से शुरू हुआ है), लेकिन इतिहास अखंडता को बरकरार रखता है।

  2. डाटाबेस पढ़ने की स्थिति/अद्यतन स्थिति: समस्याग्रस्त दौड़ की स्थिति तब होती है जब Xa और Xb X0 पर एक ही समय में सहेजते हैं। वहाँ हो जाएगा (छद्म) कोड कुछ की तरह:

    def save_history(orig_doc, new_doc): 
        text_field_diff = diff(orig_doc.text_field, new_doc.text_field) 
        save_diff(text_field_diff) 
    

    Xa हैं और Xb दोनों (यानी orig_doc X 0 है) डेटाबेस से X 0 पढ़ते हैं, अपने मतभेदों Xa -0 और XB-0 (के रूप में करने का विरोध किया हो जाएगा धारावाहिक Xa-0 तब एक्सबी-ए, या समकक्ष एक्सबी -0 फिर एक्सए-बी)। जब आप इतिहास का उत्पादन करने के लिए अलग-अलग पैच को पैच करने का प्रयास करते हैं, तो यह पैच Xa-0 या Xb-0 (जो दोनों X0 पर लागू होते हैं) पर असफल हो जाएंगे। इतिहास की अखंडता से समझौता किया गया है (या यह है?)।

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

मुझे इस समस्या से निपटने के तरीके के बारे में कुछ प्रतिक्रिया और सुझाव मिलने से प्रसन्नता होगी।

संयोग से, यह पता उपयोगी होता है insofar के रूप में, मैं पाया है कि Django atomicity यहाँ पर चर्चा की है:

तुम कृपया धन्यवाद।

+0

कोई पूरा उत्तर नहीं है इसलिए मैं इसे टिप्पणियों में डाल दूंगा। Django RCS फ़ील्ड को देखने की कोशिश करें: http://code.google.com/p/django-rcsfield/ फ़ील्ड को प्रबंधित करने के लिए यह संस्करण नियंत्रण प्रणाली है। इसे काम करने पर आलेख: http://lethain.com/entry/2008/oct/15/setting-up-django-rcsfield/ –

उत्तर

1

diffs के प्रबंधन के लिए, शायद आप पाइथन के difflib की जांच करना चाहेंगे।

परमाणुता के संबंध में, मैं शायद इसे विकिस (ट्रैक इत्यादि) के समान ही संभालूंगा। यदि उपयोगकर्ता ने पिछली बार इसे पुनर्प्राप्त करने के बाद से सामग्री बदल दी है, तो अनुरोध करें कि वे नए संस्करण के साथ ओवरराइड करें। यदि आप एक ही रिकॉर्ड में टेक्स्ट और diffs संग्रहित कर रहे हैं, तो आपके द्वारा पोस्ट किए गए लिंक में तकनीकों का उपयोग करके डेटाबेस रेस स्थितियों से बचना मुश्किल नहीं होना चाहिए।

+0

डिफ्लिब बहुत अच्छा है, धन्यवाद। मैंने अभी भी परमाणुता का काम नहीं किया है, लेकिन मुझे लगता है कि यह करने योग्य है। –

2

स्टोरेज समस्या: मुझे लगता है कि आपको केवल दस्तावेज़ के लगातार दो मान्य संस्करणों के अंतर को स्टोर करना चाहिए। जैसा कि आप इंगित करते हैं, समवर्ती संपादन होने पर समस्या को वैध संस्करण मिल रहा है।

संगामिति मुद्दा:

  1. आप उन सभी Jeff suggests की तरह या दस्तावेज़ को लॉक करके एक साथ से बचने के सकता है?
  2. यदि नहीं, तो मुझे लगता है कि आप अंततः ऑनलाइन सहयोगी रीयल-टाइम संपादकों जैसे Google डॉक्स के प्रतिमान में हैं।

कीड़े आप पकड़ खोल रहे हैं के कर सकते हैं की एक सचित्र दृश्य प्राप्त करने के लिए this google tech-talk at 9m21s (यह ग्रहण सहयोगी वास्तविक समय संपादन के बारे में है)

वैकल्पिक रूप से, वहाँ पेटेंट कि विस्तार तरीकों से कर रहे हैं Wikipedia article on collaborative real-time editors पर इन सहमतिओं से निपटने का।

+0

बहुत उपयोगी लिंक, धन्यवाद। बहुत दिलचस्प समस्या है। मैं शायद मध्य-जमीन की तलाश में हूं: सहयोगी रीयल-टाइम संपादन की जटिलता के बिना समवर्ती संपादन। –

1

उपयोगकर्ता को वास्तव में सहेजें बटन दबाए जाने से पहले, मेरा ऑटो सेव, ड्राफ्ट संस्करण सहेजता है, है ना?

यदि ऐसा है, तो आपको ड्राफ्ट को सहेजने की ज़रूरत नहीं है, उपयोगकर्ता को असली के लिए सहेजने का निर्णय लेने के बाद बस उन्हें निपटाना होगा, और केवल वास्तविक/स्पष्ट सहेजने का इतिहास रखें।

+0

अच्छा सुझाव। मुझे एक अंतर्निहित इतिहास रखने का विचार पसंद है - तो आप वापस जा सकते हैं और "ओह, दाएं" जा सकते हैं। हालांकि, यह कीमत पर आता है। :) –

3

यहाँ मैं एक वस्तु के इतिहास को बचाने के लिए क्या किया है:

Django आवेदन इतिहास के लिए:

इतिहास/__ init__.py:

""" 
history/__init__.py 
""" 
from django.core import serializers 
from django.utils import simplejson as json 
from django.db.models.signals import pre_save, post_save 

# from http://code.google.com/p/google-diff-match-patch/ 
from contrib.diff_match_patch import diff_match_patch 

from history.models import History 

def register_history(M): 
    """ 
    Register Django model M for keeping its history 

    e.g. register_history(Document) - every time Document is saved, 
    its history (i.e. the differences) is saved. 
    """ 
    pre_save.connect(_pre_handler, sender=M) 
    post_save.connect(_post_handler, sender=M) 

def _pre_handler(signal, sender, instance, **kwargs): 
    """ 
    Save objects that have been changed. 
    """ 
    if not instance.pk: 
    return 

    # there must be a before, if there's a pk, since 
    # this is before the saving of this object. 
    before = sender.objects.get(pk=instance.pk) 

    _save_history(instance, _serialize(before).get('fields')) 

def _post_handler(signal, sender, instance, created, **kwargs): 
    """ 
    Save objects that are being created (otherwise we wouldn't have a pk!) 
    """ 
    if not created: 
    return 

    _save_history(instance, {}) 

def _serialize(instance): 
    """ 
    Given a Django model instance, return it as serialized data 
    """ 
    return serializers.serialize("python", [instance])[0] 

def _save_history(instance, before): 
    """ 
    Save two serialized objects 
    """ 
    after = _serialize(instance).get('fields',{}) 

    # All fields. 
    fields = set.union(set(before.keys()),set(after.keys())) 

    dmp = diff_match_patch() 

    diff = {} 

    for field in fields: 
    field_before = before.get(field,False) 
    field_after = after.get(field,False) 

    if field_before != field_after: 
     if isinstance(field_before, unicode) or isinstance(field_before, str): 
     # a patch 
     diff[field] = dmp.diff_main(field_before,field_after) 
     else: 
     diff[field] = field_before 

    history = History(history_for=instance, diff=json.dumps(diff)) 
    history.save() 

इतिहास/models.py

""" 
history/models.py 
""" 

from django.db import models 

from django.contrib.contenttypes.models import ContentType 
from django.contrib.contenttypes import generic 

from contrib import diff_match_patch as diff 

class History(models.Model): 
    """ 
    Retain the history of generic objects, e.g. documents, people, etc.. 
    """ 

    content_type = models.ForeignKey(ContentType, null=True) 

    object_id = models.PositiveIntegerField(null=True) 

    history_for = generic.GenericForeignKey('content_type', 'object_id') 

    diff = models.TextField() 

    def __unicode__(self): 
     return "<History (%s:%d):%d>" % (self.content_type, self. object_id, self.pk) 

उम्मीद है कि किसी की मदद करता है, और टिप्पणियों की सराहना की जाएगी।

ध्यान दें कि यह मेरी सबसे बड़ी चिंता की दौड़ स्थिति को संबोधित करता है। अगर, _pre_handler "पहले = sender.objects.get (pk = instance.pk)" को किसी अन्य उदाहरण से पहले बुलाया जाता है, लेकिन उस अन्य उदाहरण के बाद इतिहास को अद्यतन किया गया है, और वर्तमान उदाहरण पहले सहेजा गया है, तो 'टूटा' होगा इतिहास '(यानी आउट ऑफ़ ऑर्डर)। शुक्र है कि diff_match_patch "गैर-घातक" ब्रेक को गहन रूप से संभालने का प्रयास करता है, लेकिन सफलता की कोई गारंटी नहीं है।

एक समाधान परमाणुता है। मुझे यकीन नहीं है कि उपरोक्त दौड़ की स्थिति (यानी _pre_handler) को Django के सभी उदाहरणों में एक परमाणु संचालन करने के बारे में कैसे जाना है। एक इतिहास लॉक तालिका, या स्मृति में एक साझा हैश (memcached?) ठीक होगा - सुझाव?

जैसा कि बताया गया है, दूसरा समाधान एक सुलह एल्गोरिदम है। हालांकि, समवर्ती बचत में "वास्तविक" संघर्ष हो सकते हैं और सही सुलह निर्धारित करने के लिए उपयोगकर्ता हस्तक्षेप की आवश्यकता होती है।

जाहिर है, इतिहास को एक साथ वापस लाकर उपरोक्त स्निपेट का हिस्सा नहीं है।

1

मैंने तब से django-reversion खोजा है, जो कि अच्छी तरह से काम करता है और सक्रिय रूप से बनाए रखा जाता है, हालांकि यह टेक्स्ट के बड़े टुकड़ों को कुशलतापूर्वक छोटे अंतरों को स्टोर करने के लिए भिन्न नहीं होता है।

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