Некоторое время тому назад я разработал несколько кастомных полей для моделей, таких как PickleField
и JSONField
и все было в них хорошо. Но время ведь не стоит на месте и мне захотелось окончательно перейти в своих проектах на прекрасную Django 1.2+ и использовать ее бенефиты вместе с использованием моих старых кастомных полей.
Для начала я просто попробовал запустить свой проект под Django 1.2 и сразу же получил вылетающие тесты при попытках считать любые данные из базы данных для моделей, содержащих кастомное поле. Ошибкой был обыкновенный ObjectDoesNotExist
, который сразу натолкнул меня на мысль что проблема где-то в get_db_prep_value
методе.
Открыв документацию, я понял, что был прав. Проблема была в том, что при переходе на поддержку многих БД одновременно был совершен рефакторинг get_db_prep_value
и подобных ему методов. Для них были добавлены аргументы connection, prepared=False
и пользоваться ими рекоммендовалось только в случае БД-зависимого преобразования. А БД-независимые преобразования советовали делать в get_prep_value
и подомных ему методов.
Так как мои поля не требовали БД-зависимого преобразования, я решил просто переименовать свои get_db_prep_value
методы в get_prep_value
. Результат не заставил себя долго ждать и при следующем перезапуске, тесты моделей, содержащие кастомные поля, прошли без ошибок. Все бы хорошо, но не выбрасывать же поддержку кастомных полей для версий Django ниже 1.2.
Посему я принялся думать как бы это реализовать. Первым в голову, почему-то пришел дурной вариант о дублировании функционала get_prep_value
в get_db_prep_value
, следующим образом:
class PickleField(models.Field):
...
def get_db_prep_value(self, value, **kwargs):
return self.get_prep_value(value)
def get_prep_value(self, value):
return pickle.dumps(value)
...
Да, это решило проблему с прохождением тестов для Django < 1.2, но теперь тесты стали валиться на Django 1.2+. Видно что-то было не так. Вооружившись дуростью, я начал копать в сторону неправильного генерирования SQL-запроса. И так бы продолжалось долго, если бы я не остановился и не подумал еще раз посмотреть на отрефакторенный код get_db_prep_value
метода. После этого проблема стала очевидной, мое предыдущее решение просто дублировало преобразование значения и потому Django не могло найти записи в базе данных и возвращало ObjectDoesNotExist
.
Что ж после определении проблемы надо было немного подумать над ее решением, но я не стал себя заморачивать и просто для всех кастомных полей и версии Django меньше 1.2, я переименовал методы get_prep_value
в get_db_prep_value
при помощи следующего кода:
from django import VERSION
if VERSION[0] == 1 and VERSION[1] < 2:
fields = (JSONField, PickleField)
for field in fields:
if not hasattr(field, 'get_prep_value'):
continue
field.get_db_prep_value = field.get_prep_value
del field.get_prep_value
Последующий перезапуск тестов на 1.0.4 и 1.1.2 завершился успехом, впрочем как и для 1.2.3 и версии из транка.
Конечно, мне не особо нравится эта магия, но у разработчиков Django похоже не было другого выбора для реализации рефакторинга, посему имеем то, что имеем :)