31

我一直在 Django 中开发一个应用程序。首先,为简单起见,我一直使用 sqlite3 作为数据库。

但是,一旦我迁移到 PostgreSQL,我就遇到了一个问题:一旦我清除了一个表,主键就不会重置。

这个应用程序是一个长时间(几周)玩的游戏。因此,每次新游戏开始时,都会从数据库中清除所有数据,然后添加新的随机数据。

我希望能够在1每次清理/重建游戏时使用主键“重新开始”。

代码仍然按原样工作,但整数是描述游戏中对象的一种非常自然的方式。我希望每场新游戏都从 1 开始,而不是从上一场比赛结束的地方开始。

如何重置 PostgreSQL 中的主键计数器?请记住,我不需要保留表中的数据,因为无论如何我都会将其清除。

4

6 回答 6

38

在您的应用程序目录中尝试以下操作:

python manage.py help sqlsequencereset

像这样将它通过管道传输到 psql 中以实际运行重置:

python manage.py sqlsequencereset myapp1 myapp2 | psql

编辑:这是我的一张表上此命令的输出示例:

BEGIN;
SELECT setval('"project_row_id_seq"', coalesce(max("id"), 1), max("id") IS NOT null) FROM "project_row";
COMMIT;
于 2009-02-13T07:04:30.870 回答
18

正如“Van Gale”所建议的那样,您可以获得解决问题的命令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.0PostgreSQL 10测试了这段代码。

于 2018-05-10T15:20:56.613 回答
6

如果您执行原始 sql,可以这样做:

ALTER SEQUENCE youApp_id_seq RESTART WITH 1;

文档: http ://www.postgresql.org/docs/8.2/static/sql-altersequence.html

于 2011-12-20T19:44:58.847 回答
1

我将自动增量主键视为数据库记录的纯粹内部标识符,我不喜欢将它们暴露给用户。诚然,将它们用作 URL 的一部分是一种常见的设计,但即使存在 slug 或其他标识符也感觉更合适。

于 2009-02-14T11:57:03.997 回答
1

如果您不想手动抓取您需要的应​​用程序,或者如果您有一系列不同的数据库,此命令将从settings.py动态收集所有连接并重置序列。

要运行使用:python manage.py reset_sequences

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:37:41.937 回答
0

您需要截断表格。见http://www.postgresql.org/docs/8.1/static/sql-truncate.html

于 2009-02-13T05:28:25.640 回答