2009-03-30 16 views

risposta

13

Quindi, ci sono una manciata di modi per fare quello che stai chiedendo. Ma un buon numero di questi non sarà indipendente dall'implementazione: potresti usare lock o rlocks, ma funzioneranno solo su server con il 100% thread e probabilmente non funzionano affatto in un'implementazione fork/pre-fork.

Che più o meno significa che l'implementazione del blocco dipenderà da voi. Due idee:

  1. .lock file nel file system
  2. locked proprietà nella classe del modello

In entrambi i casi, è necessario impostare manualmente l'oggetto di blocco aggiornata e verificare contro di essa su Elimina . Prova qualcosa del tipo:

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() 

Questa è davvero un'implementazione maldestra che dovrai pulire. Potresti non sentirti a tuo agio con il fatto che sia stata creata una regione critica diversa, ma non vedo come andrai molto meglio se utilizzi il database come un'implementazione senza rendere l'implementazione molto più complicata. (Un'opzione potrebbe essere quella di rendere i blocchi completamente separati da oggetti, quindi è possibile aggiornarli dopo che è stato chiamato il metodo save(). Ma non mi va di codificarlo.) Se si vuole veramente usare un blocco basato su file sistema, che risolverebbe anche il problema. Se sei paranoico al database, questa potrebbe essere la cosa giusta per te. Qualcosa di simile:

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 

In ogni caso, forse c'è un modo per combinare questi suggerimenti secondo i propri gusti?

+1

+1 per esempi dettagliati e descrizione –

+0

Avevo intenzione di fare qualcosa di simile, ma alla ricerca di un built-in modo prima. Dato che non c'è traccia di serratura integrata, accetterò la tua risposta. Grazie! – kokeksibir

0

Suggerirei un semplice blocco di lettura/scrittura, poiché non si desidera impedire agli utenti di accedere all'oggetto contemporaneamente (solo dalla modifica).

Un approccio generale a tale scopo sarebbe quello di creare una funzione che mantenga il conteggio dei lettori attivi. Quando hai bisogno di scrivere su quell'oggetto, crei un'altra funzione che impedisce ai nuovi lettori di accedere (pensa a una pagina di manutenzione) e, eventualmente, reindirizza i lettori esistenti. Una volta che non ci sono più lettori, completerai la scrittura, quindi sbloccherai l'oggetto.

1

Poiché il tuo ambito di applicazione è limitato alle eliminazioni e non anche agli aggiornamenti, un'opzione potrebbe essere quella di ripensare l'idea di una "eliminazione" come un'azione di "non pubblicazione". Per esempio, prendete il seguente modello:

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() 

In questo modo, ogni volta che una modifica è impegnata, è visibile a tutti gli utenti ... ma altri sono ancora liberi di eliminare gli elementi. Un vantaggio di questa tecnica è che non devi avere alcuna logica aggiuntiva per bloccare gli oggetti e presentare una UI diversa all'utente. Gli svantaggi sono lo spazio extra utilizzato nella tabella db e le rare circostanze in cui un elemento eliminato "magicamente" riappare.

(Questo è probabilmente solo un punto di partenza. Se hai preso questa strada, che ci probabilmente vuole fare una variante di questa idea a seconda del caso d'uso.)

+0

Mi piace l'idea di "non pubblicare", ma è un lavoro piuttosto che una soluzione. Lo terrò a mente e provalo se altro non riuscirò a gestirlo con altro. Grazie! – kokeksibir

2

Dal momento che l'aggiunta di select_for_update, c'è un modo semplice per acquisire un blocco su un oggetto, a condizione che il database lo supporti.postgresql, oracle e mysql, almeno, lo supportano, secondo i documenti di Django.

codice Esempio:

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) 
Problemi correlati