Обработка модели django двоичным полем в cacheops

#django #pickle

#django #pickle

Вопрос:

У меня есть простая модель Django с двоичным полем, которую я хотел бы обработать.

 class MyModel(models.Model):
    bin_data = models.BinaryField()
  

Исходя из контекста моих unittests, я делаю следующее:

 import pickle
tmp_obj = MyModel.objects.create(bin_data="12345")
obj = MyModel.objects.get(pk=tmp_obj.pk)  # load from DB
data = pickle.dumps(obj)
obj2 = pickle.loads(data)
  

Однако исправление.сбой dumps() с:

 TypeError: can't pickle buffer objects
  

Когда я использую следующую команду для обработки:

 data = pickle.dumps(obj, protocol=-1)
  

Дамп выполняется успешно, но выполняется обработка.loads() завершается с ошибкой:

 TypeError: buffer() takes at least 1 argument (0 given)
  

На самом деле это связано с проблемой, с которой я сталкиваюсь с библиотекой django-cacheops, которую я использую для кэширования моего набора запросов.

В основе django-cacheops лежит использование pickle.выполняется сброс (obj, protocol = -1), и я получаю ту же ошибку, что и описанная выше для pickle.загружает()

Я был бы признателен за ответ как на проблему с pickle, так и на проблему django-cacheops.

Спасибо

Ответ №1:

Мне удалось разгадать тайну, поэтому я мог бы также помочь любому другому, кто мог бы столкнуться с этим.

Эта проблема, по-видимому, связана с ошибкой в модуле pickle в python 2.7, которая не будет исправлена … http://bugs.python.org/issue8323

В двух словах, библиотека pickle (при использовании последнего протокола) способна обрабатывать типы буферов, но не удалять их.

При использовании BinaryField в модели django тип поля в экземпляре модели при загрузке из базы данных — ‘buffer’, что вызывает проблему.

Простым обходным решением было бы преобразовать поле ‘buffer’ в ‘str’.

Что касается моего примера, это можно легко сделать, используя сигнал post_init:

 class MyModel(models.Model):
    bin_data = models.BinaryField()


from django.db.models.signals import post_init

def on_model_load(sender, **kwargs):
    model_obj = kwargs.get('instance', None)
    if model_obj and model_obj.bin_data is not None:
        model_obj.bin_data = str(model_obj.bin_data)

post_init.connect(on_model_load, sender=MyModel)
  

Обходной путь позволит обработать экземпляр модели, а также исправить поведение модуля django-cacheops.

Комментарии:

1. Была такая же проблема в Python 3.4.1, django-cache-machine 0.9.1 и Django 1.85. Это исправлено! Я не использую cacheops.

Ответ №2:

Исправлено в cacheops 2.1.1.

Используя внешнюю функцию обработки для буфера таким образом:

 import copy_reg
copy_reg.pickle(buffer, lambda b: (buffer, (bytes(b),)))
  

Комментарии:

1. Еще лучше. Исправление в библиотеке cacheops! Спасибо за это 🙂