4👍
Ok, it turns out that South doesn’t freeze methods at all, so calling any model methods is of no use.
The way I solved this was by coping and modifying the code in contrib.auth that generates passwords.
Here’s how the final migration looks like:
# 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)
5👍
Why you don’t just import what you need?.
I had the same problem and what I did was:
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')
...
- Django – Extending another apps ModelAdmin?
- TemplateSyntaxError: 'with' expected with atleast one variable assignment
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.
1👍
Why not make the User
manually, then set the password after it has been save()
d with newUser.set_password()
? Yes, you’ll need to hit the DB twice, but that’s no great shakes.
- Dyld: Library not loaded: @executable_path/../.Python
- DRF create method in viewset or in serializer
- For the django admin, how do I add a field to the User model and have it editable in the admin?