【问题标题】:Why does Django set a shorter maximum length of foreign keys to auth.User.username?为什么 Django 为 auth.User.username 设置较短的最大外键长度?
【发布时间】:2018-02-05 06:39:12
【问题描述】:

我有一个模型,其外键引用 auth.Userusername 字段。原始字段最大长度为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 字段的源,但这会增加复杂性。

标签: python sql django orm


【解决方案1】:

建议使用短标识符,varchar(30) 是一个长数字,类似于 999999999999999999999999999999,当 Django 使标识符始终使用相同的数字时。我认为如果您达到该数量,您将不会使用这么多用户,您应该创建另一种类型的标识符。记住 user_id 字段的 long 是用户名的 id 而不是字符串

【讨论】:

  • 父用户已经有一个用户名 varchar(150)。即使我有一个用户名长度为 31 个字符的用户,由于外键约束,应用程序也无法插入引用它的资源。
  • 类配置文件(models.Model): user = models.ForeignKey('auth.User', to_field='username')
  • 尝试删除 to_field=username 并告诉我
【解决方案2】:

您可以使用this SO answer 中描述的这个技巧, 但要非常小心!

或者你可以使用this package

但是,我认为,如 this discussion 中所述,最好的方法是创建一个 custom User model 并在那里做任何你想做的事情。

希望对您有所帮助!

【讨论】:

  • 好吧,在这种情况下,他们正在修改用户列,在这种情况下,Victor 使用关系,但用户名的长度为 150
【解决方案3】:

您必须使用自定义用户模型。取自 django 文档。

150 个字符或更少。用户名可以包含字母数字、_、@、+、。和 - 字符。

对于许多用例来说,max_length 应该足够了。如果您需要更长的长度,请使用自定义用户模型。如果您使用带有 utf8mb4 编码的 MySQL(推荐正确的 Unicode 支持),最多指定 max_length=191,因为默认情况下 MySQL 只能创建具有 191 个字符的唯一索引。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-15
    • 1970-01-01
    相关资源
    最近更新 更多