Django在旧版数据库上运行迁移 [英] Django running migrations on legacy database

查看:78
本文介绍了Django在旧版数据库上运行迁移的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用旧版数据库,并且已经创建了一个自定义用户模型.我正在努力设置注册和身份验证功能.我已经创建了用户管理器,并且在用户模型中为django添加了一些字段,例如is_staff,is_active,date_joined.当我运行迁移时,旧表仍然没有我在模型中添加的列.它真的应该改变旧数据库吗?

I am working with a legacy database and I have created a custom user model. I am working to set up the register and authentication funcs. I have created the user manager and in user model there are some fields that I have added for django like is_staff, is_active, date_joined. When I run migrations, the legacy table still does not have the columns I have added in the model. Should It actually alter the legacy database?

class TbUser(AbstractBaseUser, PermissionsMixin):
    id = models.CharField(primary_key=True, max_length=40)
    usname = models.CharField(max_length=40, blank=True, null=True, unique=True)
    psword = models.CharField(max_length=255, blank=True, null=True)
  
    # added columns
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    date_joined = models.DateTimeField(default=timezone.now)

    objects = TbUserManager()

    USERNAME_FIELD = 'usname'
    REQUIRED_FIELDS = []

    class Meta:
        managed = False
        db_table = 'tb_user'

另外,当我创建超级用户时,出现以下错误

Also, when I am creating the superuser, I get the following error

django.db.utils.OperationalError:(1054,字段列表中的未知列'tb_user.password"")

尽管用户管理器看起来像这样

Although The user manager looks like this

class TbUserManager(BaseUserManager):
    
    def create_user(self, email, psword=None, **kwargs):
        if not email:
            raise ValueError('Users must have a valid email address.')
        if not kwargs.get('usname'):
            raise ValueError('Users must have a valid username.')
        user = self.model(
            email=self.normalize_email(email), usname=kwargs.get('usname')
        )
        user.psword(psword)
        user.save()
        return user

    def create_superuser(self, email, psword, **kwargs):
        user = self.create_user(email, psword, **kwargs)

        user.is_superuser = True
        user.save()

        return user

我真的不知道在哪里找到了 tb_user.password 的错误,因为我已将其全部重命名为 psword

I really dont know where the error found the tb_user.password because I have renamed all to psword

如果您需要一些详细信息,请随时询问.

If you need some details feel free to ask.

我发现密码错误是由于模型命名psword引起的,有没有办法告诉django这是密码字段?例如:USER_PASSWORD =密码"

I found that password error is due to model naming psword, is there a way to tell django that this is the password field? ex: USER_PASSWORD='psword'

推荐答案

您得到的错误是由 password 字段(以及 last_login字段)已经在 AbstractBaseUser 中定义,并且如果您要使用Django验证,则必须使用该列才能使其正常工作.

The error you're getting is caused by the fact that password field (as well as last_login field) is already defined in the AbstractBaseUser and if you want to use Django validation, you have to use that column for it to work properly.

将旧数据库转换为与Django身份验证系统兼容的形式会更好.

You'll be better of converting that legacy database into the form compatible with Django auth system.

您可能会遇到的第二个问题是

The 2nd issue you'll probably face is how Django stores passwords. As you already have existing data, user passwords may be already stored in the database in some form. You can fix it in 3 different ways.

如果您的旧系统将所有密码存储为纯文本格式或使用任何方法 Django已支持,您只需将密码转换为其中之一即可.

If your legacy system was storing all the passwords in plain text or using any method already supported by Django, you can just convert your passwords into one of them.

我建议仅使用默认情况下启用的一个散列器,或者从以下所述的散列器中选择另一种解决方案.

I recommend using only one of the hashers enabled by default or choosing another solution from ones described below.

例如,如果您的旧方法使用md5对密码进行哈希处理,则可以编写自己的密码哈希器,该哈希器将采用现有的编码密码并将其放入现有的密码哈希器之一(例如 django.contrib).auth.hashers.PBKDF2PasswordHasher ).

For example, if your legacy method uses md5 to hash the passwords, you can write your own password hasher, that will take the existing encoded password and put it inside one of the existing password hashers (like django.contrib.auth.hashers.PBKDF2PasswordHasher).

然后,您的设置可能如下所示:

Then, your settings may look like:

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    'your_auth_app.hashers.PBKDF2LegacyPasswordHasher',
]

使用该配置,Django将知道如何验证用户提供的密码,但是当用户登录时,它将自动将其密码转换为列表中的第一个哈希(因为Django可以访问纯文本密码,因此可以自己编码)

With that configuration, Django will know how to validate the password provided by user, but it also will automatically convert his password into the first hasher from the list when user logs in (as Django will have access to the plaintext password, it can encode it on its own)

只需在旧密码的前面添加一个字符,因此Django知道它不能用于登录.用户仍然可以使用密码重置电子邮件功能重置其密码.

Simply add a ! character in the front of the old password, so Django knows it cannot be used to log in. Users will still be able to reset their passwords using the password reset email feature.

由于您提供了旧系统中现有密码哈希的示例,因此,我添加了一个与您的案例更相关的示例.

As you've provided an example of the existing password hash from your old system, I'm adding an example more relevant to your case.

$ 2a 表示 bcrypt .但是在旧系统中如何精确地计算它很难确定,而必须在旧系统的代码中进行查询,因为 bcrypt 可能会在其他算法或任何其他算法的基础上使用密码操作方案.

$2a at the beginning of the password indicates bcrypt. But how exactly it is computed in your old system can't be easily determined and has to be looked up in the code of your old system as the bcrypt may be used on top of another algorithm or any other password manipulation scheme.

但是,由于 bcrypt 本身足够安全,因此您无需在另一层哈希中包装这种类型的哈希.如果您以前的系统不能简单地通过bcrypt或先通过sha256,然后再通过bcrypt,则可能需要实施自己的密码哈希器.

But as the bcrypt itself is secure enough, you don't need to wrap this type of hashes in another layer of hashing. It may be required though to implement your own password hasher if your previous system doesn't simply pass it through bcrypt or first through the sha256 and then through the bcrypt.

如果您很难通过阅读旧系统的代码来确定它,或者根本无法这样做,则可以通过尝试检查错误来进行检查.要检查Django内置的2个变体,请先通过安装 django [bcrypt] 安装所需的库.

If it is hard for you to determine it by reading the code of the old system, or you can't do it at all, you can check it by trial an error. To check for the 2 variants that are built-into Django, first install the required libraries by installing django[bcrypt].

接下来,确保在Django设置中同时启用了要测试的两个哈希,最好将 PASSWORD_HASHERS 设置设置为:

Next, make sure both hashers you're about to test are enabled in your Django settings, preferably set your PASSWORD_HASHERS setting as:

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    'django.contrib.auth.hashers.BCryptPasswordHasher',
]

接下来,在旧系统中创建一个新的用户帐户(或获取您知道密码的任何现有帐户),从中复制密码哈希,并在该密码前面添加 bcrypt $ 前缀它(此哈希的最终形式应在此修改后以 bcrypt $$ 2a $ 开头.请注意两个 $ 符号).使用准备好的哈希值,使用 ./manage.py shell 打开管理控制台,然后执行:

Next, create a new user account in your old system (or get any existing one for which you know the password), copy the password hash from it and add a bcrypt$ prefix in front of it (the final form of this hash should start with bcrypt$$2a$ after this modification. Note the double $ symbol). With that prepared hash, open the management console using ./manage.py shell and execute:

from django.contrib.auth.hashers import check_password

check_password('your known password', 'your_modified_password_hash')

如果此函数返回True,则您的旧系统使用纯bcrypt.如果结果为False,则将 bcrypt 替换为 bcrypt_sha256 ,以检查第二个密码哈希,然后再次执行 check_password .如果成功,您的旧系统会在将密码传递给bcrypt之前用sha256封装密码.

If this function returns True, your old system used plain bcrypt. If it results in False, replace bcrypt with bcrypt_sha256 to check the 2nd password hash and execute the check_password again. If it succeeds, your old system wraps the password with sha256 before passing it to the bcrypt.

如果任何一项测试成功,则只需根据测试结果向所有的密码散列添加 bcrypt $ bcrypt_sha256 $ 前缀(取决于您的测试结果)旧系统.如果它们都不起作用,则需要查看旧系统代码以确定确切的哈希方法.

If either test succeeded, all you need to do is to add a bcrypt$ or bcrypt_sha256$ prefix respectively, depending on your test results, to all password hashes from the old system. If none of them worked, you need to look at your old system code to determine the exact hashing method.

如果您的旧系统正在将bcrypt与sha256配合使用(并且您需要对旧数据进行的操作就是添加 bcrypt_sha256 $ 前缀),那么我建议还原 PASSWORD_HASHERS 为默认值.如果您想继续使用旧的哈希方法,可以将方法移到 PASSWORD_HASHERS 列表的顶部,因此Django将使用此方法创建任何新帐户,并且不会更改旧密码.用户使用自己的密码登录时,使用其他方法哈希.

If your old system was using bcrypt with sha256 (and all you need to do with the old data is to add the bcrypt_sha256$ prefix), I recommend reverting the PASSWORD_HASHERS to the default value. If you want to keep using the old hashing method, you can move your method to the top of the PASSWORD_HASHERS list, so Django will create any new account using this method and it won't change the old password hashes to another method when user logs in using his password.

有关 BCryptSHA256PasswordHasher 的旁注.Django在默认的密码哈希器套件中使用它,因为纯bcrypt具有最大密码长度限制.首先将其传递给SHA256,以确保无论用户提供的实际密码长度如何,bcrypt算法的输入都不会超过该限制.

A side note about the BCryptSHA256PasswordHasher. Django uses it in the default password hashers suite because a plain bcrypt has a max password length limit. Passing it through the SHA256 first ensures that the input to the bcrypt algorithm never exceeds that limit regardless of the actual password length provided by user.

这篇关于Django在旧版数据库上运行迁移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆