【问题标题】:Django reset auto-increment pk/id field for productionDjango 为生产重置自动增量 pk/id 字段
【发布时间】:2015-12-08 11:59:21
【问题描述】:

(我是 Django、Python 和 Postgresql 的新手)我在开发过程中一直在添加和删除数据,并注意到 pk 不断增加,即使我删除所有模型也不会重置为 1。在我将它推到生产之前,是否可以将 pk 重置为从 1 开始?这样做是个好主意吗?

【问题讨论】:

标签: python django postgresql django-models


【解决方案1】:

替代 sqlsequencereset:直接使用 SQLite 更新

使用默认 db.sqlite3 数据库开发环境

我一直在努力尝试这里给出的答案,但我一直收到:

python manage.py sqlsequencereset AppName
>> No sequences found.

对我来说最简单的解决方法是直接更新我的 SQLite 数据库(我在本地运行我的应用程序):

# Open your database
sqlite3 db.sqlite3

并且,在 SQLite 提示符中:

UPDATE sqlite_sequence SET seq = 0 WHERE sqlite_sequence.name = "<AppName_ModelName>";

我将值设置为零,因此它以 id = 1 开头。

编辑:这是我的第一篇文章,如果我应该改进格式,请告诉我!

【讨论】:

  • 效果很好,"&lt;AppName_ModelName&gt;" 不应该是数据库表名吗?
  • 确实如此。表名,在我的 Django (3.1.7) 版本上,至少在 SQLite 中,是通过连接你的应用程序的名称和你的目标模型的名称来命名的。例如,我为“引擎”创建了一个模型来使用我的“仪表板”应用程序进行监控,所以我的目标表的名称是“dashboard_engine”。
【解决方案2】:

您可以使用sqlsequencereset 命令重置模型ID序列

python manage.py sqlsequencereset myapp1 myapp2 myapp3| psql

如果您想读取生成的 sql 命令,只需执行该命令而不将其通过管道传输到 psql。

python manage.py sqlsequencereset myapp1 myapp2 myapp3

您需要在生产数据库上使用此命令。但是,正如@knbk 提到的,如果您的生产数据库是新的,则不需要重置 id 序列。

【讨论】:

【解决方案3】:

您可以生成一个命令来收集系统中的所有应用程序并为每个表运行动态生成的重置语句,同时还可以动态收集您在未命名的情况下循环遍历的每个表的 PK 列名称它们都是相同的值。

运行: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)



【讨论】:

    猜你喜欢
    • 2022-11-01
    • 1970-01-01
    • 2011-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-02
    相关资源
    最近更新 更多