Какой самый простой способ заблокировать объект в Django - PullRequest
10 голосов
/ 31 марта 2009

Я хочу вызвать ошибку, когда пользователь пытается удалить объект, когда некоторые другие пользователи активны в представлении update_object. Я чувствую, что для этого нужен какой-то мьютекс-подобный механизм блокировки. У вас есть предложения?

Ответы [ 4 ]

14 голосов
/ 31 марта 2009

Итак, есть несколько способов сделать то, что вы просите. Но многие из них не будут зависеть от реализации: вы можете использовать блокировки или блокировки, но они действительно будут работать только на 100% -ных многопоточных серверах и, вероятно, вообще не будут реализованы в реализации fork / pre-fork.

Это более или менее означает, что реализация блокировки будет зависеть от вас. Две идеи:

  1. .lock файл в вашей файловой системе
  2. locked собственность в вашем классе модели

В обоих случаях необходимо вручную установить объект блокировки при обновлении и проверить его при удалении. Попробуйте что-то вроде:

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

Это действительно неуклюжая реализация, которую вам придется очистить. Возможно, вас не устраивает тот факт, что была создана другая критическая область, но я не понимаю, как вы будете работать намного лучше, если будете использовать базу данных в качестве реализации, не усложняя реализацию. (Одним из вариантов будет сделать блокировки полностью отдельными объектами. Затем вы можете обновить их после вызова метода save (). Но я не чувствую необходимости кодировать это.) Если вы действительно хотите использовать блокировку на основе файлов система, которая также решит проблему. Если вы параноик по базе данных, это может быть для вас. Что-то вроде:

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

В любом случае, может быть, есть какой-то способ смешать и сопоставить эти предложения на ваш вкус?

6 голосов
/ 23 августа 2017

Поскольку добавлено select_for_update , существует простой способ получить блокировку объекта, если ваша база данных поддерживает ее. postgresql, oracle и mysql, по крайней мере, поддерживают его, согласно документации Django.

Пример кода:

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)
1 голос
/ 31 марта 2009

Поскольку ваша область ограничена удалениями, а не обновлениями, одним из вариантов будет переосмысление идеи «удалить» как действия «отменить публикацию». Например, возьмите следующую модель:

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

Таким образом, каждый раз, когда редактирование фиксируется, оно видимо всем пользователям ... но другие по-прежнему могут удалять элементы. Одним из преимуществ этой техники является то, что вам не нужно иметь никакой дополнительной логики для блокировки элементов и представления пользователю другого пользовательского интерфейса. Недостатками являются дополнительное пространство, используемое в таблице БД, и редкие обстоятельства, когда удаленный элемент «волшебным образом» появляется снова.

(Вероятно, это только отправная точка. Если вы выбрали этот путь, вы, вероятно, захотите внести изменения в эту идею в зависимости от вашего варианта использования.)

0 голосов
/ 31 марта 2009

Я бы предложил простую блокировку чтения-записи, поскольку вы не хотите блокировать одновременный доступ пользователей к объекту (только из редактирования).

Общий подход к этому состоит в создании функции, которая поддерживает количество активных читателей. Когда вам нужно написать в этот объект, вы должны создать другую функцию, которая будет препятствовать доступу новых читателей (например, страницы обслуживания) и, возможно, перенаправит существующих читателей прочь. Когда читателей больше не останется, вы завершите запись и разблокируете объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...