为啥 Django 不在 SQLite3 中重置序列?
Posted
技术标签:
【中文标题】为啥 Django 不在 SQLite3 中重置序列?【英文标题】:Why Doesn't Django Reset Sequences in SQLite3?为什么 Django 不在 SQLite3 中重置序列? 【发布时间】:2014-07-28 17:19:06 【问题描述】:为什么 Django 允许您重置 postgres 和其他 DBMS 上的序列 (AutoID) 字段,而不是 SQLite3?
查看django/db/backends/sqlite3/base.py
中sql_flush
方法的源码,有注释说:
注意:不需要重置自动递增索引(参见其他 sql_flush() 实现)。此时只需返回 SQL
我有一些测试在其中加载依赖于绝对主键 ID 的夹具文件。因为 Django 没有为 SQLite 重置 auto id 字段,所以这些设备不能正确加载。
在sqlite中重置自动id列似乎有些微不足道:How can I reset a autoincrement sequence number in sqlite
【问题讨论】:
听起来你的测试坏了,那么。什么都不应该依赖于绝对 ID。 @DanielRoseman 是的,每个人都知道这不是最佳实践。事实上,很容易质疑在测试中使用加载夹具。这就是存在像 FactoryGirl 这样的东西的原因。但是,在这种情况下,这是一个完美的解决方案。 Django 文档指定在测试用例docs.djangoproject.com/en/1.5/topics/testing/overview/… 中使用重置序列。即使他们提到这是一个坏主意。但是,我的问题是:即使这是一个坏主意,为什么他们不支持 sqlite? 我不知道 sqlite 是否像 Oracle 那样支持序列休息。例如,我发现当我清空相关表时,sqlite 自动整数主键会重置。我知道在 Oracle 下,Auto ID 序列作为一个完全独立的实体存在,可以独立重置。 【参考方案1】:您可以按以下方式对sql_flush
进行猴子补丁以重置 SQLite 序列:
from django.db.backends.sqlite3.operations import DatabaseOperations
from django.db import connection
def _monkey_patch_sqlite_sql_flush_with_sequence_reset():
original_sql_flush = DatabaseOperations.sql_flush
def sql_flush_with_sequence_reset(self, style, tables, sequences, allow_cascade=False):
sql_statement_list = original_sql_flush(self, style, tables, sequences, allow_cascade)
if tables:
# DELETE FROM sqlite_sequence WHERE name IN ($tables)
sql = '%s %s %s %s %s %s (%s);' % (
style.SQL_KEYWORD('DELETE'),
style.SQL_KEYWORD('FROM'),
style.SQL_TABLE(self.quote_name('sqlite_sequence')),
style.SQL_KEYWORD('WHERE'),
style.SQL_FIELD(self.quote_name('name')),
style.SQL_KEYWORD('IN'),
', '.join(style.SQL_FIELD(f"'table'") for table in tables)
)
sql_statement_list.append(sql)
return sql_statement_list
DatabaseOperations.sql_flush = sql_flush_with_sequence_reset
您可以在TransactionTestCase
中按如下方式使用它:
from django.test import TransactionTestCase
class TransactionTestCaseWithSQLiteSequenceReset(TransactionTestCase):
reset_sequences = True
@classmethod
def setUpClass(cls):
super().setUpClass()
if connection.vendor == 'sqlite':
_monkey_patch_sqlite_sql_flush_with_sequence_reset()
这可确保依赖于固定主键的测试可与 SQLite 和其他数据库后端(如 PostgreSQL)一起使用。但是,请参阅 Django documentation 以了解有关 reset_sequences
的警告。一方面,它使测试变慢。
【讨论】:
【参考方案2】:也许这个 sn-p 会有所帮助:
import os
from django.core.management import call_command
from django.db import connection
from django.utils.six import StringIO
def reset_sequences(app_name):
os.environ['DJANGO_COLORS'] = 'nocolor'
buf = StringIO()
call_command('sqlsequencereset', app_name, stdout=buf)
buf.seek(0)
sql = "".join(buf.readlines())
with connection.cursor() as cursor:
cursor.execute(sql)
print("Sequences for app '' reset".format(app_name))
【讨论】:
Django 3.0 不再有django.utils.six
;见***.com/a/59420098/5139284。我们或许可以用from io import StringIO
替换该行。
确保你没有将reset_sequences
定义为TestCase的类方法,否则你会得到“assert not self.reset_sequences, 'reset_sequences cannot be used on TestCase instances' AssertionError: reset_sequences cannot用于 TestCase 实例”
对我来说,这总是打印“未找到序列”。以上是关于为啥 Django 不在 SQLite3 中重置序列?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 django sqlite3 数据库在一台机器上与另一台机器上的工作方式不同?
为啥 Django 不在 Varnish 代理后面生成 CSRF 或会话 Cookie?