Предоставляет ли Django какой-либо встроенный способ обновления счетчиков автоинкрементов PostgreSQL? - PullRequest
0 голосов
/ 01 ноября 2018

Я перевожу сайт Django с MySQL на PostgreSQL. Количество данных невелико, поэтому я выбрал очень простой подход: я просто использовал встроенные подпрограммы Django serialize и десериализованно для создания записей JSON, а затем загружал их в новый экземпляр, переберите объекты и сохраните каждый из них в новой базе данных.

Это работает очень хорошо, с одним отклонением: после загрузки всех записей я сталкиваюсь с IntegrityError, когда пытаюсь добавить новые данные после загрузки старых записей. Postgres-эквивалент поля идентификатора автоинкремента MySQL является последовательным полем, но внутренний счетчик для последовательных полей не увеличивается, если значения идентификатора указаны явно. В результате Postgres пытается начать нумерацию записей с 1 - уже используется - что вызывает нарушение ограничения. (Это известная проблема в Django, помеченная wontfix .)

Есть довольно много вопросов и ответов, связанных с этим, но, похоже, ни один из ответов не касается проблемы непосредственно в контексте Django. В этом ответе приведен пример запроса, который необходимо выполнить для обновления счетчика, но я стараюсь по возможности избегать явных запросов. Я мог бы просто удалить поле ID перед сохранением и позволить Postgres самому выполнить нумерацию, но в этом случае есть ссылки ForeignKey, которые будут нарушены. А все остальное работает прекрасно!

Было бы неплохо, если бы Джанго предоставил для этого рутину, которая разумно обрабатывает любые крайние случаи. (Это не исправит ошибку, но позволит разработчикам работать с ней последовательно и корректно.) Нужно ли нам просто использовать необработанный запрос, чтобы это исправить? Это кажется таким варварским.

Если на самом деле такой подпрограммы не существует, я просто сделаю что-то вроде нижеприведенного, что напрямую запускает запрос, предложенный в ответе, связанном выше. (Или я верю - это не проверено. Я отредактирую это после тестирования.) Но в этом случае мне было бы интересно услышать о любых потенциальных проблемах с этим подходом или любой другой информации о том, что я могу делать неправильно , Например, я должен просто изменить записи, чтобы использовать вместо них UUID, так как предполагает ?

Вот грубый подход.

SomeTable.objects.raw(
    "SELECT setval('your_sequence_name', (SELECT max(id) FROM some_table));"
)

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

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

    def synchronize_last_sequence(model):
        #  Postgresql aut-increments (called sequences) don't update the 'last_id' value if you manually specify an ID.
        #  This sets the last incremented number to the last id
        sequence_name = model._meta.db_table+"_"+model._meta.pk.name+"_seq"
        with connections['default'].cursor() as cursor:
            cursor.execute(
                "SELECT setval('" + sequence_name + "', (SELECT max(" + model._meta.pk.name + ") FROM " +
                model._meta.db_table + "))"
            )
        print("Last auto-incremental number for sequence "+sequence_name+" synchronized.")

Что я и сделал, используя SQL-запрос, который вы предложили в своем вопросе. Было очень полезно найти ваш пост. Спасибо!

Он должен работать с пользовательскими PK, но не с многопольными PK.

0 голосов
/ 01 ноября 2018

Один из вариантов - использовать естественные ключи во время сериализации и десериализации. Таким образом, когда вы вставляете его в PostgreSQL, он автоматически увеличивает поле первичного ключа и сохраняет все в строке.

Недостатком этого подхода является то, что вам нужно иметь набор уникальных полей для каждой модели, которые не содержат идентификатор.

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