【发布时间】:2018-02-05 06:39:12
【问题描述】:
我有一个模型,其外键引用 auth.User 的 username 字段。原始字段最大长度为150。但是Django生成的外键最大长度为30。
在我应用的 models.py 中:
class Profile(models.Model):
user = models.ForeignKey('auth.User', to_field='username')
在 django.contrib.auth.models 中:
username = models.CharField(
_('username'),
max_length=150,
生成的 SQL:
CREATE TABLE "myapp_profile" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"user_id" varchar(30) NOT NULL REFERENCES "auth_user" ("username")
);
这仅在引用 auth.User.username 时发生。如果我在自己的模型中引用了一个长字段,则可以正常生成外键。
这是为什么呢?我该如何克服它?
使用 Django 1.11.4 和 Python 3.6.2。我尝试了 PostgreSQL 和 SQLite,两者都出现了问题。
澄清:
从到目前为止的答案来看,我认为我的问题被误解了。我不是在寻找一种拥有长用户名的方法。我的问题是 Django 自带的 stock User 模型有一个 max_length (150),但是当你的模型引用它时,外国 hey 的 max_length 较短,为 30。因此,如果用户使用 31 个字符的用户名注册,我将无法创建该用户的子对象,因为将违反外键约束。我需要这个,因为我有一个 REST API,其 URL 嵌套使用的资源,由用户名而不是 ID 引用。例如:/users/<username>/profiles/...
更新: 我认为这种行为的原因是用户模型的未记录的 swappable 属性。它被设计成可以被定制模型替换。但是,配置的模型必须在定义模型的应用程序的初始迁移中具有其数据。迁移代码似乎生成了对可交换模型的初始迁移的引用。我使用的是默认用户模型,它的初始迁移将用户名设置为 30 个字符。因此我的用户名 FKs 有 30 个字符长。我可以通过 RunSQL 迁移将 FK 数据类型更改为 varchar(15) 来解决此问题,但我怀疑这样做是否正确。
【问题讨论】:
-
你为什么要这样做?通常,外键指向外表的主键。为什么你需要改变它?
-
我有一个 REST API,其中的对象嵌套在用户对象下。 URL 的格式为 /users/
/resources/。在这种情况下,找到父用户的正确外键是用户名。 -
那不符合,那些东西不相关。正如我所描述的那样,与 fks 建立嵌套关系是完全可能的——实际上是正常的。
-
这是一个非常好的观点,它让我想到了我很久以前做出的设计决定。我通过 URL 中给出的字段而不是整数主键来引用父对象的原因是为了查询优化。假设您收到一个对 /users/
/resources/ 的请求,并且您必须返回一个列表。如果您的资源模型通过用户名引用用户,则查询将只是 select * from resources where username = <username>。但如果 FK 是整数 id,则必须与 users 表进行连接。 -
同样在我的 API 中,我希望返回对象的
user字段包含用户名,因此它可以用于构造 URL。由于 djangorestframework 的特殊性,在序列化返回的模型实例时,如果模型中不存在user字段的源,它必须在父对象中查找它,每个对象一次。 200 个返回的对象意味着每个请求 200 个查询。解决方法是使用用户名注释resource对象,并在序列化结果时将其用作user字段的源,但这会增加复杂性。