38

最近、新しいDjangoサイトのモデルの基礎として、古いデータベースから新しいPostgresqlデータベースに多くのデータをインポートしました。

古いデータベースのIDを使用しました(さまざまなテーブルの行が相互に参照しているため)が、すべてが連続しているわけではなく、多くの場合、大きなギャップがあります。

Djangoアプリを介して新しいオブジェクトを追加すると、1から始まるIDが使用されていることに気付きました。これは、IDが非常に低い行がない限り問題ではありません。

しかし、レガシーデータの最初の行に到達すると、postgresは明らかに次のように文句を言います。

ERROR:  duplicate key value violates unique constraint "django_comments_pkey"
DETAIL:  Key (id)=(25) already exists.

テーブルの説明を見ると、各テーブルである種のシーケンスをリセットする必要があると思います。

                                      Table "public.django_comments"
     Column      |           Type           |                          Modifiers                           
-----------------+--------------------------+--------------------------------------------------------------
 id              | integer                  | not null default nextval('django_comments_id_seq'::regclass)
...

そのシーケンスをリセットして、現在の最大IDよりも高いIDで新しい行が追加されるようにするには、何をする必要がありますか?

4

9 に答える 9

61

sqlsequenceresetを実行すると、必要なすべてのリセットコマンドが出力されます。

于 2013-01-29T18:27:55.750 に答える
20

「DmitryShevchenko」が提案するように、あなたはsqlsequenceresetあなたの問題を解決するために走ることができます。

また

sqlsequencereset次のように、 Python内から生成されたSQLクエリを実行できます(デフォルトのデータベースを使用)。

from django.core.management.color import no_style
from django.db import connection

from myapps.models import MyModel1, MyModel2


sequence_sql = connection.ops.sequence_reset_sql(no_style(), [MyModel1, MyModel2])
with connection.cursor() as cursor:
    for sql in sequence_sql:
        cursor.execute(sql)

このコードをPython3.6Django 2.0PostgreSQL10でテストしました。

于 2018-05-10T15:03:18.667 に答える
9

これは、Django 1.9以降( http://djangosnippets.org/snippets/2774/に基づく)でPython3と互換性のあるすべてのシーケンスをリセットするための短いスニペットです。

import os
from io import StringIO

os.environ['DJANGO_COLORS'] = 'nocolor'

from django.core.management import call_command
from django.apps import apps
from django.db import connection

commands = StringIO()
cursor = connection.cursor()

for app in apps.get_app_configs():
    label = app.label
    call_command('sqlsequencereset', label, stdout=commands)

cursor.execute(commands.getvalue())
于 2016-08-17T06:03:50.147 に答える
7

したがって、私の意見では、これを行うための最も速く、最も簡単で、最も「Django」の方法は、次の管理コマンドを使用することです。

python manage.py sqlsequencereset app_name

この後、次のようなものが表示されます。

BEGIN;
SELECT setval(pg_get_serial_sequence('"measurements_quantity"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "measurements.Quantities";
SELECT setval(pg_get_serial_sequence('"measurements.Prefixes"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "measurements.Prefixes";
COMMIT;

次のステップは、python manage.py dbshell管理コマンドでこれを実行することです。これを実行すると、ターミナルにインタラクションデータベースシェルが表示されます。

psql (11.7 (Debian 11.7-0+deb10u1), server 11.5 (Debian 11.5-1.pgdg90+1))
Type "help" for help.

postgres=# BEGIN;
BEGIN
postgres=# SELECT setval(pg_get_serial_sequence('"measurements.Quantities"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "measurements.Quantities";
 setval 
--------
      1
(1 row)

postgres=# SELECT setval(pg_get_serial_sequence('"measurements.Prefixes"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "measurements.Prefixes";
 setval 
--------
      1
(1 row)

postgres=# COMMIT;
COMMIT
postgres=# exit

そのような単純な。このpython manage.py sqlsequencereset app_nameコマンドは、実行する必要のあるSQLを提供し、で実行しますdbshell

独自のカスタムSQLやカスタムコードを作成する必要はありません。必要なものを正しい形式と選択したdbエンジンで提供します。

于 2020-09-04T09:56:37.517 に答える
4

PostgreSQLコマンド: ALTER SEQUENCE app_model_id_seq RESTART WITH 1

于 2019-03-16T13:16:18.933 に答える
1
select setval('django_comments_id_seq', 12345);
于 2013-01-29T18:26:22.973 に答える
1

このスニペットは、すべてのアプリでsqlsequenceresetを実行すると、すべての空のモデルのすべてのIDがリセットされます

于 2013-05-14T15:06:49.273 に答える
1

これは、設定にある接続パラメータに基づいて主キーを収集するため、リセットしようとしている主キーの名前に制限のない管理コマンドで実装した、多かれ少なかれ完全に動的なソリューションです。

リセットできなかった唯一のシーケンスには、整数ではないPKが含まれていました。これはdjango.contrib.sessionsのPKで明らかですが、シーケンスエラーが発生したことは一度もないので、問題ではないと思います。

python manage.py reset_sequencesこれがコマンドです。(ファイル/コマンドの名前がreset_sequences.pyである限り)を使用して実行します。

import psycopg2
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db import connections


def dictfetchall(cursor):
    """Return all rows from a cursor as a dict"""
    columns = [col[0] for col in cursor.description]
    return [
        dict(zip(columns, row))
        for row in cursor.fetchall()
    ]


class Command(BaseCommand):
    help = "Resets sequencing errors in Postgres which normally occur due to importing/restoring a DB"

    def handle(self, *args, **options):
        # loop over all databases in system to figure out the tables that need to be reset
        for name_to_use_for_connection, connection_settings in settings.DATABASES.items():
            db_name = connection_settings['NAME']
            host = connection_settings['HOST']
            user = connection_settings['USER']
            port = connection_settings['PORT']
            password = connection_settings['PASSWORD']

            # connect to this specific DB
            conn_str = f"host={host} port={port} user={user} password={password}"

            conn = psycopg2.connect(conn_str)
            conn.autocommit = True

            select_all_table_statement = f"""SELECT *
                                    FROM information_schema.tables
                                    WHERE table_schema = 'public'
                                    ORDER BY table_name;
                                """
            # just a visual representation of where we are
            print('-' * 20, db_name)
            try:
                not_reset_tables = list()
                # use the specific name for the DB
                with connections[name_to_use_for_connection].cursor() as cursor:
                    # using the current db as the cursor connection
                    cursor.execute(select_all_table_statement)
                    rows = dictfetchall(cursor)
                    # will loop over table names in the connected DB
                    for row in rows:
                        find_pk_statement = f"""
                            SELECT k.COLUMN_NAME
                            FROM information_schema.table_constraints t
                            LEFT JOIN information_schema.key_column_usage k
                            USING(constraint_name,table_schema,table_name)
                            WHERE t.constraint_type='PRIMARY KEY'
                                AND t.table_name='{row['table_name']}';
                        """
                        cursor.execute(find_pk_statement)
                        pk_column_names = dictfetchall(cursor)
                        for pk_dict in pk_column_names:
                            column_name = pk_dict['column_name']

                        # time to build the reset sequence command for each table
                        # taken from django: https://docs.djangoproject.com/en/3.0/ref/django-admin/#sqlsequencereset
                        # example: SELECT setval(pg_get_serial_sequence('"[TABLE]"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "[TABLE]";
                        try:
                            reset_statement = f"""SELECT setval(pg_get_serial_sequence('"{row['table_name']}"','{column_name}'), 
                                                    coalesce(max("{column_name}"), 1), max("{column_name}") IS NOT null) FROM "{row['table_name']}" """
                            cursor.execute(reset_statement)
                            return_values = dictfetchall(cursor)
                            # will be 1 row
                            for value in return_values:
                                print(f"Sequence reset to {value['setval']} for {row['table_name']}")
                        except Exception as ex:
                            # will only fail if PK is not an integer...
                            # currently in my system this is from django.contrib.sessions
                            not_reset_tables.append(f"{row['table_name']} not reset")

            except psycopg2.Error as ex:
                raise SystemExit(f'Error: {ex}')

            conn.close()
            print('-' * 5, ' ALL ERRORS ', '-' * 5)
            for item_statement in not_reset_tables:
                # shows which tables produced errors, so far I have only
                # seen this with PK's that are not integers because of the MAX() method
                print(item_statement)

            # just a visual representation of where we are
            print('-' * 20, db_name)

于 2020-08-11T18:31:42.000 に答える
0

@Paolo Melchiorreに基づいて、選択したアプリからすべてのモデルにデータを入力するカスタム管理コマンドを作成しました。

from django.core.management.base import BaseCommand
from django.apps import apps
from django.core.management.color import no_style
from django.db import connection

class Command(BaseCommand):
    def handle(self, *args, **kwargs):
        self.stdout.write('Reset AutoFields ...')
        APPS = ['app1', 'app2']

        APPS = [apps.get_app_config(app) for app in APPS]
        models = []
        for app in APPS:
            models.extend(list(app.get_models()))

        sequence_sql = connection.ops.sequence_reset_sql(no_style(), models)
        with connection.cursor() as cursor:
            for sql in sequence_sql:
                self.stdout.write(sql)
                cursor.execute(sql)
        self.stdout.write(self.style.SUCCESS('Reset AutoField complete.'))

python 3.7およびを使用してテスト済みdjango 2.2

于 2019-08-21T14:31:53.123 に答える