【问题标题】:Can django's auth_user.username be varchar(75)? How could that be done?django 的 auth_user.username 可以是 varchar(75) 吗?那怎么可能呢?
【发布时间】:2011-02-06 07:04:17
【问题描述】:

auth_user 上运行alter table 以使username 成为varchar(75) 有什么问题吗?如果有的话,那会破坏什么?

如果您要将auth_user.username 更改为varchar(75),您需要在哪里修改django?难道只是把源码中的30改成75的问题吗?

username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))

或者在此字段上是否有其他验证需要更改或这样做会产生任何其他影响?

请参阅下面与 bartek 的评论讨论,了解这样做的原因。

编辑:数月后回顾这一点。对于不知道前提的人:一些应用程序没有要求或不希望使用用户名,它们仅使用电子邮件进行注册和身份验证。不幸的是,在 django auth.contrib 中,用户名是必需的。您可以开始将电子邮件放在用户名字段中,但该字段只有 30 个字符,并且电子邮件在现实世界中可能很长。可能比这里建议的 75 字符更长,但 75 字符可容纳大多数正常的电子邮件地址。该问题针对的是基于电子邮件身份验证的应用程序遇到的这种情况。

【问题讨论】:

  • 在编辑之前,对auth.contrib 的长时间投诉并不是问题。您对其进行了编辑以使其更加文明,但 SO 声称“投票太旧而无法更改”。
  • 我把它编辑得更简洁,我很讨厌你暗示它不那么文明(?)。我正在解释询问 auth_user.username = varchar(75) 的逻辑。这是我看到很多其他人的情况,所以值得解释一下。但如果它导致人们不花时间解决问题,则不会。
  • 后人注意:Django 1.5 有一些特性可以更优雅地解决这些问题。见procrastinatingdev.com/django/…

标签: django django-models django-authentication


【解决方案1】:

有一种方法可以在不触及核心模型和继承的情况下实现这一目标,但这绝对是 hackish,我会格外小心地使用它。

如果您查看 Django 的文档 on signals,您会看到一个名为 class_prepared 的文档,它基本上是在元类创建任何实际模型类后发送的。那一刻是您在任何魔法发生之前修改任何模型的最后机会(即:ModelFormModelAdminsyncdb 等...)。

所以计划很简单,您只需将该信号注册到一个处理程序,该处理程序将检测何时为User 模型调用它,然后更改username 字段的max_length 属性。

现在的问题是,这段代码应该放在哪里?它必须在User 模型加载之前执行,因此这通常意味着非常早。不幸的是,您不能(django 1.1.1,尚未检查其他版本)将其放入settings,因为导入signals 会破坏事情。

更好的选择是将它放在一个虚拟应用的模型模块中,然后将该应用放在INSTALLED_APPS 列表/元组的顶部(这样它就会在其他任何东西之前被导入)。这是您可以在myhackishfix_app/models.py 中拥有的示例:

from django.db.models.signals import class_prepared

def longer_username(sender, *args, **kwargs):
    # You can't just do `if sender == django.contrib.auth.models.User`
    # because you would have to import the model
    # You have to test using __name__ and __module__
    if sender.__name__ == "User" and sender.__module__ == "django.contrib.auth.models":
        sender._meta.get_field("username").max_length = 75

class_prepared.connect(longer_username)

这样就可以了。

不过有几点说明:

  • 您可能还想更改字段的 help_text,以反映新的最大长度
  • 如果你想使用自动管理,你必须继承UserChangeFormUserCreationFormAuthenticationForm,因为最大长度不是从模型字段中推断出来的,而是直接在表单字段声明中。李>

如果您使用South,您可以创建以下迁移来更改底层数据库中的列:

import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models

class Migration(SchemaMigration):

    def forwards(self, orm):

        # Changing field 'User.username'
        db.alter_column('auth_user', 'username', models.CharField(max_length=75))


    def backwards(self, orm):

        # Changing field 'User.username'
        db.alter_column('auth_user', 'username', models.CharField(max_length=35))


    models = { 

# ... Copy the remainder of the file from the previous migration, being sure 
# to change the value for auth.user / usename / maxlength

【讨论】:

  • @perrierism: 是的,你必须自己alter table,因为 django 不会为你做这件事(你可能想试试south)。但是从头开始的syncdb 将创建具有正确类型的字段 (varchar(75))。
  • 我建议对答案进行编辑,以包括向南方迁移,因为我需要进行一些研究才能得出结论,而且评论时间太长。
  • 仅供参考,SQL仅供参考(比写南迁移短):ALTER TABLE auth_user ALTER COLUMN username TYPE varchar(75);
  • 对于 django1.3,我需要在调用 setup_environ 之后,在 execute_manager 之前将其放入 manage.py。我还需要添加sender._meta.get_field('username').validators[0].limit_value = 75 有没有人有更好的方法来在 django1.3 上完成这个?
  • 这可以通过 1.7 迁移来完成吗?我不确定如何将应用标签指定为“auth”。
【解决方案2】:

根据上面 Clément 和 Matt Miller 的出色组合答案,我整理了一个实现它的快速应用程序。 Pip 安装、迁移和运行。会将此作为评论,但还没有信誉!

https://github.com/GoodCloud/django-longer-username

编辑 2014-12-08

上述模块现已弃用,取而代之的是 https://github.com/madssj/django-longer-username-and-email

【讨论】:

  • 谢谢@skoczen!到目前为止对我来说效果很好
  • 我试过这个,但我收到错误“您的数据库没有 South 数据库模块 'south.db.mysql'。请选择支持的数据库,检查 SOUTH_DATABASE_ADAPTER[S] 设置,或者从 INSTALLED_APPS 中删除 South。"
【解决方案3】:

Django 1.3版本的更新解决方案(不修改manage.py):

创建新的 django-app:

monkey_patch/
    __init__.py
    models.py

先安装它:(settings.py)

INSTALLED_APPS = (
    'monkey_patch', 
    #...
)

这里是models.py:

from django.contrib.auth.models import User
from django.core.validators import MaxLengthValidator

NEW_USERNAME_LENGTH = 300

def monkey_patch_username():
    username = User._meta.get_field("username")
    username.max_length = NEW_USERNAME_LENGTH
    for v in username.validators:
        if isinstance(v, MaxLengthValidator):
            v.limit_value = NEW_USERNAME_LENGTH

monkey_patch_username()

【讨论】:

  • 我尝试了这个解决方案,因为它看起来更简单,但我有相同的结果“对于类型字符变化(30)的值太长”。我完全按照你说的做了一切。我在 django 1.3.3
【解决方案4】:

上述解决方案似乎确实更新了模型长度。但是,要在 admin 中反映您的自定义长度,您还需要覆盖 admin 表单(令人沮丧的是,它们不会简单地从模型中继承长度)。

from django.contrib.auth.forms import UserChangeForm, UserCreationForm

UserChangeForm.base_fields['username'].max_length = NEW_USERNAME_LENGTH
UserChangeForm.base_fields['username'].widget.attrs['maxlength'] = NEW_USERNAME_LENGTH
UserChangeForm.base_fields['username'].validators[0].limit_value = NEW_USERNAME_LENGTH
UserChangeForm.base_fields['username'].help_text = UserChangeForm.base_fields['username'].help_text.replace('30', str(NEW_USERNAME_LENGTH))

UserCreationForm.base_fields['username'].max_length = NEW_USERNAME_LENGTH
UserCreationForm.base_fields['username'].widget.attrs['maxlength'] = NEW_USERNAME_LENGTH
UserCreationForm.base_fields['username'].validators[0].limit_value = NEW_USERNAME_LENGTH
UserCreationForm.base_fields['username'].help_text = UserChangeForm.base_fields['username'].help_text.replace('30', str(NEW_USERNAME_LENGTH))

【讨论】:

  • 您可能还想在 django.contrib.auth.forms 中对 AuthenicationForm 应用相同的逻辑
【解决方案5】:

据我所知,从 Django 1.5 开始,可以override user model 解决问题。简单例子here

【讨论】:

    【解决方案6】:

    如果你只是简单地修改数据库表,你仍然需要处理 Django 的验证,所以无论如何它不会让你超过 30 个字符。 此外,用户名经过验证,因此它不能包含像 @ 这样的特殊字符,因此简单地修改字段的长度无论如何都行不通。我的错,看起来它可以处理这个问题。这是 django.contrib.auth 中 models.py 的用户名字段:

    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
    

    创建电子邮件身份验证并不难。这是您可以使用的super simple email auth backend。之后您需要做的就是添加一些验证,以确保电子邮件地址是唯一的,然后您就完成了。就这么简单。

    【讨论】:

    • 不完全。您如何处理用户名字段?它仍然是一个主键。
    • 您对 @ 登录用户名也有误。它确实允许@。事实上,我不太清楚它在查看代码的用户名上强制
    • 它不是主键。用户名很简单。您有他们的电子邮件和您以及数据库中的主键,也许还有他们的姓氏。哦,你还有一个内置在 python 中的随机数生成器……等等。只需使用户名独一无二。在我的一个应用程序中,我只使用了电子邮件的第一部分加上他们的用户 ID。简单的基本应用程序。
    • 是的,如果您考虑一下,这还不是一个足够的解决方案。你如何保证用户名是唯一的。请注意,实际上您不能在用户名中使用 id,因为当您创建用户对象时,您实际上还没有 id,并且用户名是必需的。也许那不是一个 django 应用程序。所以如果你真的想让它变得健壮,你就剩下哈希了。但我想知道的是,如果您可以修改 django 以接受用户名中的电子邮件地址,为什么还要经历所有这些麻烦?
    • 它对我来说很好用。有一些简单的方法可以检查某些内容是否独一无二(将您的值与数据库进行比较,如果需要,请重试。)您是否正在运行企业应用程序?然后,您可能需要研究其他解决方案。如果您正在运行一个拥有几千名用户的应用程序,我认为您不会有任何问题。修改 django 代码和表本身的问题很好......想象一下当你必须更新 django 时。现在已经坏了,从长远来看,您将面临维护噩梦
    【解决方案7】:

    是的,可以做到。至少我认为这应该可行;我最终更换了整个 auth 模型,所以如果这不起作用,我准备更正...

    如果您没有关心的用户记录:

    1. 删除 auth_user 表
    2. 在模型中将用户名更改为 max_length=75
    3. 同步数据库

    如果您有需要保留的用户记录,那么它会更加复杂,因为您需要以某种方式迁移它们。最简单的是将数据从旧表备份和恢复到新表,例如:

    1. 备份用户表数据
    2. 删除表
    3. 同步数据库
    4. 将用户数据重新导入新表;注意恢复原始 id 值

    或者,使用你的 mad python-django Skillz,将用户模型实例从旧复制到新并替换​​:

    1. 创建您的自定义模型并将其临时放置在默认模型旁边
    2. 编写将实例从默认模型复制到新模型的脚本
    3. 用您的自定义模型替换默认模型

    后者并不像听起来那么难,但显然需要更多的工作。

    【讨论】:

      【解决方案8】:

      从根本上说,问题在于有些人希望使用电子邮件地址作为唯一标识符,而 Django 中的用户身份验证系统需要最多 30 个字符的唯一用户名。也许这会在未来发生变化,但正如我所写的那样,Django 1.3 就是这种情况。

      我们知道 30 个字符对于许多电子邮件地址来说太短了;如What is the optimal length for an email address in a database? 中所述,即使是 75 个字符也不足以表示某些电子邮件地址。

      我喜欢简单的解决方案,因此我建议将电子邮件地址散列为符合 Django 中用户名限制的用户名。根据User authentication in Django,用户名最多30个字符,由字母数字字符和_、@、+、.组成。和 -。因此,如果我们使用 base-64 编码并仔细替换特殊字符,我们就有多达 180 位。所以我们可以使用 SHA-1 之类的 160 位哈希函数,如下所示:

      import hashlib
      import base64
      
      def hash_user(email_address):
          """Create a username from an email address"""
          hash = hashlib.sha1(email_address).digest()
          return base64.b64encode(hash, '_.').replace('=', '')
      

      简而言之,此函数将用户名关联到任何电子邮件地址。我知道哈希函数中发生冲突的可能性很小,但这在大多数应用程序中应该不是问题。

      【讨论】:

      • 为什么要浪费循环计算哈希和存储它的磁盘空间?另请注意,您需要编写自定义授权后端或自定义 login_form 才能使其工作。没什么大不了的,但这对我来说似乎是一种奇怪的扭曲。
      • 就我而言,哈希周期和磁盘空间比开发时间便宜得多。您的里程可能会有所不同。
      【解决方案9】:

      只需在 settings.py 底部添加以下代码

      from django.contrib.auth.models import User
      User._meta.get_field("username").max_length = 75
      

      【讨论】:

      • 在 Django 1.7 及更高版本中无法使用,因为尚未导入模型
      【解决方案10】:

      C:...\venv\Lib\site-packages\django\contrib\auth\models.py

      first_name = models.CharField(_('first name'), max_length=30, blank=True)

      改成

      first_name = models.CharField(_('first name'), max_length=75, blank=True)

      保存

      数据库中的变化

      【讨论】:

        【解决方案11】:

        我使用的是 django 1.4.3,这让它变得非常简单,并且在意识到我想使用长电子邮件地址作为用户名之后,我不必更改代码中的任何其他内容。

        如果您可以直接访问数据库,请将其更改为您想要的字符数,在我的情况下为 100 个字符。

        在您的应用模型 (myapp/models.py) 中添加以下内容

        from django.contrib.auth.models import User
        
        class UserProfile(models.Model):
        
            # This field is required.
            User._meta.get_field("username").max_length = 100
            user = models.OneToOneField(User)
        

        然后在你的 settings.py 中指定模型:

        AUTH_USER_MODEL = 'myapp.UserProfile'
        

        【讨论】:

          【解决方案12】:

          最好的解决方案是使用电子邮件字段作为电子邮件,使用用户名作为用户名。

          在输入登录表单验证中,查找数据是用户名还是电子邮件,如果是电子邮件,则查询电子邮件字段。

          这只需要猴子修补contrib.auth.forms.login_form,这是相应视图中的几行。

          而且这比试图修改模型和数据库表要好得多。

          【讨论】:

          • 我想你可能误解了这个问题,它是关于当你不使用用户名时,你使用的是电子邮件。为了在不接触 django 的情况下解决它,普遍接受的解决方案比您在此处描述的要多。它实际上是用独特的东西填充用户名(浪费字段)并创建一个 AUTHORIZATION_BACKENDS 类来处理电子邮件的身份验证。问题在于创建用户。有一个电子邮件字段,但还有一个不适合电子邮件的必填用户名字段。每次创建用户时,您仍然需要创建唯一的用户名。
          • perrierism:单独使用电子邮件而不接触 django 的解决方案显然是使用随机自动生成的用户名并使用电子邮件进行查询(使用电子邮件身份验证后端)。
          • 以前对您来说并不“明显”,因为这实际上是与您发布的解决方案不同的解决方案。您现在提到的解决方案不需要更改 contrib.auth.forms.login_form。如果您想要一个健壮的实现,自动生成的用户名问题也不是那么简单(参见与 bartek 的讨论)。最后,请注意问题不是“你如何做到这一点没有修改django?”。普遍接受的解决方案(您还没有真正说明)是已知的,但它有局限性。也就是说,您在创建用户时仍然需要唯一的用户名。
          【解决方案13】:

          如果您使用的是venv(虚拟环境),最简单的解决方案可能是直接更新核心代码,即打开以下两个文件: - - venv/lib/python2.7/sites-packages/django/contrib/auth/model.py -venv/lib/python2.7/sites-packages/django/contrib/auth/forms.py 搜索所有用户名字段并将 max_length 从 30 更改为 100。这是安全的,因为您已经在使用 venv 所以它不会影响任何其他 Django 项目。

          【讨论】:

            猜你喜欢
            • 2013-07-14
            • 1970-01-01
            • 2020-05-11
            • 2015-01-03
            • 2021-08-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-03
            相关资源
            最近更新 更多