Django测试在迁移到1.2.5后失败 - 子模型主键问题

3 投票
3 回答
628 浏览
提问于 2025-04-16 15:07

我有一个叫做ThreadedComment的模型,它是从Object模型继承来的。ThreadedComment没有自己独特的主键,而是依赖于Object的主键("ID")。这是这个模型的构建方式:

class Object(models.Model):
    permalink = models.CharField(max_length=128)
    status = models.IntegerField()
    version = models.IntegerField()

class ThreadedComment(Object):
    parent = models.ForeignKey('self', null=True, blank=True, default=None, related_name='children')
    parent_object = models.OneToOneField(Object, parent_link=True)
    # other fields follow

在django 1.2.3版本时,这个设置运行得很好,但当我升级到django 1.2.5(1.3也有同样的问题)时,每当我尝试运行任何测试时,就会出现问题:

Error: Database test_db couldn't be flushed. Possible reasons:
  * The database isn't running or isn't configured correctly.
  * At least one of the expected database tables doesn't exist.
  * The SQL was invalid.
Hint: Look at the output of 'django-admin.py sqlflush'. That's the SQL this command wasn't able to run.
The full error: relation "threadedcomments_threadedcomment_id_seq" does not exist
LINE 1: SELECT setval('"threadedcomments_threadedcomment_id_seq"', 1...

问题出在"sqlflush"命令上,这个命令会生成并执行一个SQL文件。出问题的那一行是:

SELECT setval(pg_get_serial_sequence('"threadedcomments_threadedcomment"','id'), 1, false);

显然,这个操作失败了,因为那个表里没有"id"这一列。

有趣的是,django 1.2.3版本也会产生类似的输出:

SELECT setval('"threadedcomments_threadedcomment_id_seq"', 1, false);

但测试还是继续进行,所以我之前没有注意到这个问题。

我在这里做错了什么?我的模型定义不正确吗?也就是说,即使我和Object有一对一的关系,我是否也需要在ThreadedComment中有一个主键?为什么从1.0到1.1再到1.2.3都能正常工作,而在1.2.5时就出问题了呢?

3 个回答

0

你提到,“ThreadedComment没有自己独特的主键,而是依赖于对象的主键(‘ID’)。”Django要求至少有一个字段被标识为主键。你可以查看自动主键字段的相关内容。

更多细节……

函数pg_get_serial_sequence()会返回某个特定列所使用的序列的名称。所以,下面这条语句的意思是:“获取表'threadedcomments_threadedcomment'中'id'列所使用的序列的名称,将这个序列的last_value字段设置为1,并在下次调用nextval()时返回1。”

SELECT setval(pg_get_serial_sequence('"threadedcomments_threadedcomment"','id'), 1, false);

但是这条语句

SELECT setval('"threadedcomments_threadedcomment_id_seq"', 1, false);

的意思是:“将名为'threadedcomments_threadedcomment_id_seq'的序列的last_value字段设置为1,并在下次调用nextval()时返回1。”

我猜Django用来命名序列的算法在不同版本之间发生了变化。我见过其他系统也做过类似的调整,因为通过连接表名和列名生成的标识符变得太长了。我认为Oracle的标识符有30个字符的限制,而你的标识符有39个字符。我还见过Rails生成超过70个字符的标识符,这在PostgreSQL中会出问题(限制在63或64个字符)。

0

我在最新版本和1.2.3版本上都无法重现这个问题;我新建了一个项目,使用了'app 'threadedcomments''和你提供的模型,结果如下:

BEGIN;
TRUNCATE "threadedcomments_threadedcomment", "auth_permission", "auth_group", "auth_group_permissions", "django_session", "auth_user_groups", "auth_user_user_permissions", "threadedcomments_object", "auth_message", "django_site", "auth_user", "django_content_type";
SELECT setval(pg_get_serial_sequence('"auth_permission"','id'), 1, false);
SELECT setval(pg_get_serial_sequence('"auth_group"','id'), 1, false);
SELECT setval(pg_get_serial_sequence('"auth_user"','id'), 1, false);
SELECT setval(pg_get_serial_sequence('"auth_message"','id'), 1, false);
SELECT setval(pg_get_serial_sequence('"django_content_type"','id'), 1, false);
SELECT setval(pg_get_serial_sequence('"django_site"','id'), 1, false);
SELECT setval(pg_get_serial_sequence('"threadedcomments_object"','id'), 1, false);
COMMIT;

序列threadedcomments_threadedcomment_id_seq从来没有被创建,Django也没有尝试去刷新它。我觉得这不是因为版本变化导致的错误(更像是一直存在但没有被注意到的问题)。

4

这原来是Django的一个bug。详细信息可以在这里找到:http://code.djangoproject.com/ticket/12728

这是我们临时的解决办法:https://bitbucket.org/filmaster/filmaster-test/changeset/afbac905cf63

撰写回答