我没有使用django的auth模块,而是使用了我自己的模块而且已经后悔了很多。
为了纠正这种情况,我正在尝试将数据从我的用户模型迁移到django.auth.models.User。
我创建了一个数据迁移,如下所示:
def forwards(self, orm):
"""Migrate user information from mooi User model to auth User model."""
OldUser = orm['mooi.User']
User = orm['auth.User']
Profile = orm['mooi.Profile']
oldUsers = OldUser.objects.all()
for oldUser in oldUsers:
newUser = User.objects.create_user(username=oldUser.id, email=oldUser.email, password=oldUser.password)
# ...more irrelevant code follows...
当我运行迁移时,我收到此错误(回溯):
#...irrelevant traceback precedes...
File "[projdir]/mooi/migrations/0005_from_mooi_users_create_auth_users_with_profiles.py", line 18, in forwards
newUser = User.objects.create_user(username=oldUser.id, email=oldUser.email, password=oldUser.password)
File "[virtual_env_dir]lib/python2.6/site-packages/south/orm.py", line 397, in __getattr__
return getattr(self.real, name)
AttributeError: 'Manager' object has no attribute 'create_user'
经过进一步调查,我发现被引用的Manager
是时间south.orm.NoDryRunManager
,这解释了错误。
现在,我甚至需要create_user
的原因是创建django.contrib.auth
将理解的密码哈希。
说了这么多,我该怎么做呢?考虑到我所在的洞,最优雅的解决方案是什么?!
提前致谢。
更新1
根据stevejalim的建议,我尝试使用User
的{{1}},如下所示:
set_password(...)
然而,这个错误失败了:
newUser.set_password(raw_password=oldUser.password)
newUser.save()
我确实在south documentation中找到了一条暗示:
的提示南方不冻结a的每个方面 模型;例如,它没有 保留新经理或自定义模型 方法,因为这些都需要 序列化运行的python代码 那些方法(和代码 取决于,等等。如果您想要自己的自定义方法 迁移,你必须复制 代码,包括任何导入它 依靠工作。但请记住, 对于您添加的每个导入,您都是 承诺保持导入有效 为了移民的生活。
我想问题仍然存在,这是最好/最安全的方法吗?复制File "[projdir]/mooi/migrations/0005_from_mooi_users_create_auth_users_with_profiles.py", line 21, in forwards
newUser.set_password(raw_password=oldUser.password)
AttributeError: 'User' object has no attribute 'set_password'
方法?创建一个为我隐藏密码的函数?还有其他想法吗?
答案 0 :(得分:5)
为什么你不能只输入你需要的东西? 我有同样的问题,我做的是:
from django.contrib.auth.hashers import make_password
class Migration(DataMigration):
...
def forwards(self, orm):
user = orm['auth.User'].objects....
user.password = make_password('123')
...
答案 1 :(得分:4)
好的,事实证明南方根本没有冻结方法,因此调用任何模型方法是没有用的。
我解决这个问题的方法是通过复制和修改contrib.auth中生成密码的代码。
以下是最终迁移的结果:
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Promise(object):
"""
This is just a base class for the proxy class created in
the closure of the lazy function. It can be used to recognize
promises in code.
"""
pass
def lazy(func, *resultclasses):
"""
Turns any callable into a lazy evaluated callable. You need to give result
classes or types -- at least one is needed so that the automatic forcing of
the lazy evaluation code is triggered. Results are not memoized; the
function is evaluated on every access.
"""
class __proxy__(Promise):
"""
Encapsulate a function call and act as a proxy for methods that are
called on the result of that function. The function is not evaluated
until one of the methods on the result is called.
"""
__dispatch = None
def __init__(self, args, kw):
self.__func = func
self.__args = args
self.__kw = kw
if self.__dispatch is None:
self.__prepare_class__()
def __reduce__(self):
return (
_lazy_proxy_unpickle,
(self.__func, self.__args, self.__kw) + resultclasses
)
def __prepare_class__(cls):
cls.__dispatch = {}
for resultclass in resultclasses:
cls.__dispatch[resultclass] = {}
for (k, v) in resultclass.__dict__.items():
# All __promise__ return the same wrapper method, but they
# also do setup, inserting the method into the dispatch
# dict.
meth = cls.__promise__(resultclass, k, v)
if hasattr(cls, k):
continue
setattr(cls, k, meth)
cls._delegate_str = str in resultclasses
cls._delegate_unicode = unicode in resultclasses
assert not (cls._delegate_str and cls._delegate_unicode), "Cannot call lazy() with both str and unicode return types."
if cls._delegate_unicode:
cls.__unicode__ = cls.__unicode_cast
elif cls._delegate_str:
cls.__str__ = cls.__str_cast
__prepare_class__ = classmethod(__prepare_class__)
def __promise__(cls, klass, funcname, func):
# Builds a wrapper around some magic method and registers that magic
# method for the given type and method name.
def __wrapper__(self, *args, **kw):
# Automatically triggers the evaluation of a lazy value and
# applies the given magic method of the result type.
res = self.__func(*self.__args, **self.__kw)
for t in type(res).mro():
if t in self.__dispatch:
return self.__dispatch[t][funcname](res, *args, **kw)
raise TypeError("Lazy object returned unexpected type.")
if klass not in cls.__dispatch:
cls.__dispatch[klass] = {}
cls.__dispatch[klass][funcname] = func
return __wrapper__
__promise__ = classmethod(__promise__)
def __unicode_cast(self):
return self.__func(*self.__args, **self.__kw)
def __str_cast(self):
return str(self.__func(*self.__args, **self.__kw))
def __cmp__(self, rhs):
if self._delegate_str:
s = str(self.__func(*self.__args, **self.__kw))
elif self._delegate_unicode:
s = unicode(self.__func(*self.__args, **self.__kw))
else:
s = self.__func(*self.__args, **self.__kw)
if isinstance(rhs, Promise):
return -cmp(rhs, s)
else:
return cmp(s, rhs)
def __mod__(self, rhs):
if self._delegate_str:
return str(self) % rhs
elif self._delegate_unicode:
return unicode(self) % rhs
else:
raise AssertionError('__mod__ not supported for non-string types')
def __deepcopy__(self, memo):
# Instances of this class are effectively immutable. It's just a
# collection of functions. So we don't need to do anything
# complicated for copying.
memo[id(self)] = self
return self
def __wrapper__(*args, **kw):
# Creates the proxy object, instead of the actual value.
return __proxy__(args, kw)
return wraps(func)(__wrapper__)
# code to encrypt passwords borrowed from django 1.2.1:
def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
"""
Returns a bytestring version of 's', encoded as specified in 'encoding'.
If strings_only is True, don't convert (some) non-string-like objects.
"""
if strings_only and isinstance(s, (types.NoneType, int)):
return s
if isinstance(s, Promise):
return unicode(s).encode(encoding, errors)
elif not isinstance(s, basestring):
try:
return str(s)
except UnicodeEncodeError:
if isinstance(s, Exception):
# An Exception subclass containing non-ASCII data that doesn't
# know how to print itself properly. We shouldn't raise a
# further exception.
return ' '.join([smart_str(arg, encoding, strings_only,
errors) for arg in s])
return unicode(s).encode(encoding, errors)
elif isinstance(s, unicode):
return s.encode(encoding, errors)
elif s and encoding != 'utf-8':
return s.decode('utf-8', errors).encode(encoding, errors)
else:
return s
def get_hexdigest(algorithm, salt, raw_password):
"""
Returns a string of the hexdigest of the given plaintext password and salt
using the given algorithm ('md5', 'sha1' or 'crypt').
"""
raw_password, salt = smart_str(raw_password), smart_str(salt)
if algorithm == 'crypt':
try:
import crypt
except ImportError:
raise ValueError('"crypt" password algorithm not supported in this environment')
return crypt.crypt(raw_password, salt)
# The rest of the supported algorithms are supported by hashlib, but
# hashlib is only available in Python 2.5.
try:
import hashlib
except ImportError:
if algorithm == 'md5':
import md5
return md5.new(salt + raw_password).hexdigest()
elif algorithm == 'sha1':
import sha
return sha.new(salt + raw_password).hexdigest()
else:
if algorithm == 'md5':
return hashlib.md5(salt + raw_password).hexdigest()
elif algorithm == 'sha1':
return hashlib.sha1(salt + raw_password).hexdigest()
raise ValueError("Got unknown password algorithm type in password.")
def get_encrypted_password(raw_password):
import random
algo = 'sha1'
salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
hsh = get_hexdigest(algo, salt, raw_password)
return '%s$%s$%s' % (algo, salt, hsh)
class Migration(DataMigration):
def forwards(self, orm):
"""Migrate user information from mooi User model to auth User model."""
OldUser = orm['mooi.User']
User = orm['auth.User']
Profile = orm['mooi.Profile']
oldUsers = OldUser.objects.all()
for oldUser in oldUsers:
newUser = User(username=oldUser.id, email=oldUser.email)
newUser.first_name = oldUser.name
newUser.save()
newUser.password = get_encrypted_password(oldUser.password)
newUser.save()
newUserProfile = Profile(user=newUser)
newUserProfile.phone = oldUser.phone
newUserProfile.credits = oldUser.credits
newUserProfile.transaction_status = oldUser.transaction_status
newUserProfile.location = oldUser.location
newUserProfile.save()
assert oldUser.id == newUser.username, \
"Old user: %s, is not equal to: %s" % (oldUser.id, newUser.username)
assert oldUser.name == newUser.first_name, \
"Names don't match, old: %s, new: %s" % (oldUser.name, newUser.first_name)
assert oldUser.email == newUser.email, \
"Emails don't match, old: %s, new: %s" % (oldUser.email, newUser.email)
assert oldUser.phone == newUserProfile.phone, \
"Phones don't match, old: %s, new: %s" % (oldUser.phone, newUserProfile.phone)
assert oldUser.credits == newUserProfile.credits, \
"Credits don't match, old: %s, new: %s" % (oldUser.credits, newUserProfile.credits)
assert oldUser.transaction_status == newUserProfile.transaction_status, \
"Trans. status don't match, old: %s, new: %s" % (oldUser.transaction_status, newUserProfile.transaction_status)
assert oldUser.location == newUserProfile.location, \
"Locations don't match: old: %s, new: %s" % (oldUser.location == newUserProfile.location)
答案 2 :(得分:3)
在迁移中使用冻结的ORM背后的重点是确保新的更改不会干扰旧的实现。 auth应用程序是django.contrib
的一部分,我怀疑你在寻找的功能在过去的几个版本中发生了很大的变化,或者计划很快就会改变。除此之外,您不会修改(auth)应用程序或其模型(对吗?对吗?)。所以说你不需要使用South的冻结版auth.User
是非常安全的。只需正常导入并以这种方式使用它。
答案 3 :(得分:1)
为什么不手动设置User
,然后在save()
d newUser.set_password()
之后设置密码?是的,你需要两次击中数据库,但这并不是很好的震撼。