代码之家  ›  专栏  ›  技术社区  ›  TM. Randy Simon

Django+PostgreSQL:如何重置主键?

  •  33
  • TM. Randy Simon  · 技术社区  · 16 年前

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

    然而,一旦我转到PostgreSQL,我遇到了一个问题:一旦我清空一个表,主键就不会重置。

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

    1

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

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

    6 回复  |  直到 8 年前
        1
  •  38
  •   Van Gale    16 年前

    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;
    
        2
  •  18
  •   Paolo Melchiorre    7 年前

    正如“Van Gale”所建议的,您可以运行命令来解决您的问题 sqlsequencereset .

    从python内部以这种方式( 使用默认数据库

    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)
    

    Python 3.6 , Django 2.0 .

        3
  •  5
  •   Lauro Oliveira    13 年前

    如果你执行一个原始sql,可以这样做:

    ALTER SEQUENCE youApp_id_seq RESTART WITH 1;
    

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

        4
  •  1
  •   akaihola dnlcrl    16 年前

        5
  •  1
  •   ViaTech    5 年前

    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)