2009-03-30 2 views

Antwort

13

So, es gibt eine Handvoll Möglichkeiten zu tun, was Sie fragen. Aber eine gute Anzahl von ihnen wird nicht implementierungsunabhängig sein: Sie könnten Sperren oder Rlocks verwenden, aber sie werden wirklich nur auf Servern mit 100% Threads funktionieren und wahrscheinlich überhaupt nicht in einer Fork/Pre-Fork-Implementierung.

Das bedeutet mehr oder weniger, dass die Sperrimplementierung Ihnen überlassen wird. Zwei Ideen:

  1. .lock Datei auf dem Dateisystem
  2. locked Eigenschaft in Ihrer Modellklasse

In beiden Fällen müssen Sie manuell das Sperrobjekt auf Update eingestellt und überprüfen dagegen auf Löschen . Versuchen Sie etwas wie:

def safe_update(request,model,id): 
    obj = model.objects.get(id) 
    if obj.locked: 
     raise SimultaneousUpdateError #Define this somewhere 
    else: 
     obj.lock() 
     return update_object(request,model,id) 

# In models file 
class SomeModel(models.Model): 
    locked = models.BooleanField(default = False) 
    def lock(self): 
     self.locked = True 
     super(models.Model,self).save() 
    def save(self): 
     # overriding save because you want to use generic views 
     # probably not the best idea to rework model code to accomodate view shortcuts 
     # but I like to give examples. 
     self.locked = False 
     # THIS CREATES A DIFFERENT CRITICAL REGION! 
     super(models.Model,self).save() 

Dies ist in der Tat eine plumpe Implementierung, die Sie aufräumen müssen. Sie sind vielleicht nicht mit der Tatsache vertraut, dass eine andere kritische Region erstellt wurde, aber ich sehe nicht, wie Sie viel besser machen würden, wenn Sie die Datenbank als Implementierung verwenden, ohne die Implementierung viel komplizierter zu machen. (Eine Option wäre, die Sperren zu getrennten Objekten zu machen. Dann könnten Sie sie aktualisieren, nachdem die Methode save() aufgerufen wurde. Aber ich habe keine Lust, das zu programmieren.) Wenn Sie wirklich eine dateibasierte Sperre verwenden wollen System, das würde auch das Problem lösen. Wenn Sie eine Datenbank-Treffer-Paranoid sind, könnte dies die Sache für Sie sein. Etwas wie:

class FileLock(object): 
    def __get__(self,obj): 
     return os.access(obj.__class__+"_"+obj.id+".lock",os.F_OK) 
    def __set__(self,obj,value): 
     if not isinstance(value,bool): 
      raise AttributeError 
     if value: 
      f = open(obj.__class__+"_"+obj.id+".lock") 
      f.close() 
     else: 
      os.remove(obj.__class__+"_"+obj.id+".lock") 
    def __delete__(self,obj): 
     raise AttributeError 

class SomeModel(models.Model): 
    locked = FileLock() 
    def save(self): 
     super(models.Model,self).save() 
     self.locked = False 

Wie auch immer, vielleicht gibt es eine Möglichkeit, diese Vorschläge nach Ihrem Geschmack zu mischen und anzupassen?

+1

+1 für ausführliche Beispiele und Beschreibung –

+0

ich vorhatte, so etwas zu machen, aber auf der Suche nach einem integrierten Weg zuerst. Da es keinen Hinweis auf ein eingebautes Schloss gibt, akzeptiere ich deine Antwort. Vielen Dank! – kokeksibir

0

Ich würde vorschlagen, eine einfache Schreib-Lese-Sperre, wie Sie Benutzer nicht gleichzeitig Zugriff auf das Objekt (nur aus der Bearbeitung) blockieren wollen.

Eine allgemeine Vorgehensweise dabei wäre, eine Funktion zu erstellen, die die Anzahl der aktiven Leser beibehält. Wenn Sie in dieses Objekt schreiben müssen, würden Sie eine weitere Funktion erstellen, die verhindert, dass neue Leser Zugriff erhalten (denken Sie an eine Wartungsseite), und möglicherweise vorhandene Leser umleiten. Sobald keine weiteren Leser mehr vorhanden sind, können Sie den Schreibvorgang abschließen und das Objekt entsperren.

1

Da Ihr Bereich auf Löschvorgänge beschränkt ist, und nicht auch auf Aktualisierungen, wäre eine Option, die Idee eines "Löschens" als Aktion "Nicht veröffentlichen" zu überdenken. Nehmen Sie zum Beispiel das folgende Modell:

class MyManager(models.Manager): 
    def get_query_set(self): 
     super(MyManager, self).get_query_set().filter(published=True) 

class MyModel(models.Model): 
    objects = MyManager() 
    published = models.BooleanField(default=True) 
    ... your fields ... 

    def my_delete(self): 
     self.published = False 
     super(MyModel, self).save() 

    def save(self): 
     self.published = True 
     super(MyModel, self).save() 

Auf diese Weise, wenn ein Bearbeitungs begangen wird, ist es für alle Benutzer sichtbar ... aber andere sind noch freie Elemente zu löschen. Ein Vorteil dieser Technik besteht darin, dass Sie keine zusätzliche Logik zum Sperren von Elementen und zum Anzeigen einer anderen Benutzeroberfläche für den Benutzer benötigen. Nachteile sind zusätzlicher Speicherplatz in der db-Tabelle und die seltenen Umstände, unter denen ein gelöschtes Objekt "magisch" wieder erscheint.

(Dies ist wahrscheinlich nur ein Ausgangspunkt. Wenn Sie diesen Weg nehmen, dann würden Sie wahrscheinlich eine Variante dieser Idee je nach Anwendungsfall. Tun wollen)

+0

Ich mag die "Unpublish" Idee, aber es ist eine Arbeit statt eine Lösung. Ich werde es im Hinterkopf behalten und es versuchen, wenn ich mit etwas anderem nicht zurechtkomme. Vielen Dank! – kokeksibir

2

Da die Zugabe von select_for_update gibt es eine einfache Möglichkeit, eine Sperre für ein Objekt zu erhalten, sofern Ihre Datenbank dies unterstützt.Postgresql, Oracle und Mysql, zumindest, unterstützen es, nach den Django-Dokumenten.

Beispielcode:

import time 

from django.contrib.auth import get_user_model 
from django.db import transaction 


User = get_user_model() 

target_user_pk = User.objects.all()[0].pk 


with transaction.atomic(): 
    print "Acquiring lock..." 
    to_lock = User.objects.filter(pk=target_user_pk).select_for_update() 
    # Important! Queryset evaluation required to actually acquire the lock. 
    locked = to_lock[0] 
    print locked 

    while True: 
     print "sleeping {}".format(time.time()) 
     time.sleep(5) 
Verwandte Themen