14

django の認証モジュールを使用する代わりに、私は自分の認証モジュールを使用しましたが、すでに後悔しています。

状況を修正するために、ユーザー モデルから 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'sを使用しようとしました。set_password(...)

newUser.set_password(raw_password=oldUser.password)
newUser.save()

ただし、次のエラーで失敗しました:

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'

私は南部のドキュメントに次のようなヒントを見つけました:

南は、モデルのすべての側面を凍結するわけではありません。たとえば、新しいマネージャやカスタム モデル メソッドは保持されません。これは、これらのメソッドを実行する Python コード (およびそれに依存するコードなど) をシリアル化する必要があるためです。

移行でカスタム メソッドが必要な場合は、動作に依存するすべてのインポートを含め、コードをコピーする必要があります。ただし、追加するすべてのインポートについて、そのインポートを移行の期間中有効に保つことを約束していることを忘れないでください。

疑問が残ると思いますが、これを行うための最良/最も安全な方法は何ですか? メソッドをコピーset_password(...)しますか? パスワードをハッシュする関数を作成しますか? 他のアイデアはありますか?

4

4 に答える 4

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')
        ...
于 2014-04-24T15:24:13.187 に答える
4

OK、South はメソッドをまったくフリーズしないことがわかったので、モデル メソッドを呼び出しても意味がありません。

私がこれを解決した方法は、パスワードを生成する 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)
于 2010-11-16T16:40:02.703 に答える
3

The whole point behind using the frozen ORM in migrations is to ensure that new changes don't interfere with old implementations. The auth app is part of django.contrib, and I doubt the functionality you're looking for has changed much in the past few releases, or is planned to change anytime soon. Aside from that, you're not going to be modifying the (auth) app or its models (right? right??). So it's pretty safe to say you don't need to use South's frozen version of auth.User; just import it normally and use it that way.

于 2010-11-09T01:32:37.990 に答える
1

Userを手動で作成し、 でパスワードを設定した後にパスワードを設定してsave()みませんnewUser.set_password()か? はい、DB を 2 回ヒットする必要がありますが、それは大きな揺れではありません。

于 2010-07-20T15:36:31.650 に答える