2009-03-30 12 views

Respuesta

13

Entonces, hay varias formas de hacer lo que está pidiendo. Pero un buen número de ellos no será independiente de la implementación: puede usar bloqueos o bloqueos, pero en realidad solo funcionarán en servidores con un 100% de subprocesos y probablemente no en una implementación de tenedor/precorte.

Eso significa más o menos que la implementación del bloqueo dependerá de usted. Dos ideas:

  1. .lock archivo en el sistema de archivos
  2. locked propiedad en su clase del modelo

En ambos casos, se tienen que ajustar manualmente el objeto de bloqueo en la actualización y comprobar contra el borrado . Pruebe algo como:

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

Esto es de hecho una implementación torpe que tendrá que limpiar. Puede que no se sienta cómodo con el hecho de que se haya creado una región crítica diferente, pero no veo cómo lo hará mucho mejor si utiliza la base de datos como una implementación sin complicar mucho más la implementación. (Una opción sería hacer que los bloqueos separen por completo los objetos. Luego, podría actualizarlos después de que se llame al método save(). Pero no tengo ganas de codificarlo). Si realmente desea usar un bloqueo basado en archivos sistema, eso también resolvería el problema. Si eres una base de datos paranoica, esto podría ser lo que necesites. Algo como:

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 

De todos modos, tal vez haya alguna manera de mezclar y combinar estas sugerencias a su gusto?

+1

+1 para ejemplos detallados y descripción –

+0

que estaba planeando hacer algo por el estilo, pero en busca de un built-in primera forma. Como no hay ninguna pista de bloqueo incorporado, aceptaré tu respuesta. ¡Gracias! – kokeksibir

0

Sugeriría un simple bloqueo de lectura y escritura, ya que no desea bloquear el acceso simultáneo de los usuarios (solo desde la edición).

Un enfoque general para hacer esto sería crear una función que mantenga un recuento de lectores activos. Cuando necesite escribir en ese objeto, crearía otra función que evitaría que los nuevos lectores tengan acceso (piense en una página de mantenimiento), y posiblemente redirija a los lectores existentes. Una vez que ya no quedan más lectores, completarás tu escritura y luego desbloquearás el objeto.

1

Dado que su alcance está limitado a las eliminaciones y no a las actualizaciones, una opción sería volver a pensar en la idea de una acción de "eliminar" como "no publicar". Por ejemplo, tomemos el siguiente modelo:

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

De esta manera, cada vez que se comete una edición, es visible para todos los usuarios ... pero otros siguen siendo libres para eliminar elementos. Una ventaja de esta técnica es que no tiene que tener ninguna lógica adicional para bloquear elementos y presentar una interfaz de usuario diferente para el usuario. Las desventajas son el espacio adicional utilizado en la tabla db y las raras circunstancias en las que un elemento eliminado "mágicamente" reaparece.

(Esto es probablemente sólo un punto de partida. Si tomó este camino, es probable que gustaría hacer una variación en esta idea dependiendo de su caso de uso.)

+0

Me gusta la idea de "no publicar", pero es una solución en lugar de una solución. Lo tendré en cuenta y lo intentaré si no pudiera manejarlo de otra manera. ¡Gracias! – kokeksibir

2

Dado que la adición de select_for_update, hay una forma simple de adquirir un candado en un objeto, siempre que su base de datos lo admita.postgresql, oracle y mysql, al menos, lo soportan, de acuerdo con los documentos de Django.

código Ejemplo:

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) 
Cuestiones relacionadas