问题
I am using the most basic flask app practically copied from documentations, and am receiving an extremely annoying error. I've been able to trace it down but don't know how to solve it. I know the problem arises when implementing flask-security, but the error comes from inside sqlalchemy. Any advice?
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
import logging
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' #
db = SQLAlchemy(app)
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
class Role(db.Model):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
def __init__(self, name, desc):
self.name = name
self.description = desc
def __repr__(self):
return '<Role: {}>'.format(str(self.name))
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
def __init__(self, username, password):
self.username = username
self.password = password
def __repr__(self):
return '<User: {}>'.format(str(self.username))
def main():
db.create_all()
adminRole = Role('Admin', 'Unrestricted')
adminUser = User('admin', 'adminpassword')
adminUser.roles = [adminRole]
db.session.add(adminUser)
db.session.commit()
app.run(debug=True)
if __name__ == '__main__':
LOG_FILENAME = 'test.log'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,)
logging.debug('This message should go to the log file')
try:
main()
except:
logging.exception('Got exception on main handler')
raise
The above code works completely fine. The problem comes when using Flask-Security and subclassing RoleMixin (which adds the eq and ne functions to the model. Once the class becomes this:
class Role(db.Model):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
def __init__(self, name, desc):
self.name = name
self.description = desc
def __repr__(self):
return '<Role: {}>'.format(str(self.name))
def __eq__(self, other):
return (self.name == other or
self.name == getattr(other, 'name', None))
def __ne__(self, other):
return not self.__eq__(other)
I get the following error:
ERROR:root:Got exception on main handler
Traceback (most recent call last):
File "test.py", line 66, in <module>
main()
File "test.py", line 53, in main
adminUser.roles = [adminRole]
File "C:\Users\ramabodhi\Envs\test\lib\site-packages\sqlalchemy\orm\attributes.py", line 220, in __set__
instance_dict(instance), value, None)
File "C:\Users\ramabodhi\Envs\test\lib\site-packages\sqlalchemy\orm\attributes.py", line 975, in set
lambda adapter, i: adapter.adapt_like_to_iterable(i))
File "C:\Users\ramabodhi\Envs\test\lib\site-packages\sqlalchemy\orm\attributes.py", line 1010, in _set_iterable
collections.bulk_replace(new_values, old_collection, new_collection)
File "C:\Users\ramabodhi\Envs\test\lib\site-packages\sqlalchemy\orm\collections.py", line 782, in bulk_replace
constants = existing_idset.intersection(values or ())
File "C:\Users\ramabodhi\Envs\test\lib\site-packages\sqlalchemy\util\_collections.py", line 592, in intersection
result._members.update(self._working_set(members).intersection(other))
TypeError: unhashable type: 'Role'
DEBUG:root:This message should go to the log file
ERROR:root:Got exception on main handler
Traceback (most recent call last):
File "test.py", line 66, in <module>
main()
File "test.py", line 53, in main
adminUser.roles = [adminRole]
File "C:\Users\ramabodhi\Envs\test\lib\site-packages\sqlalchemy\orm\attributes.py", line 220, in __set__
instance_dict(instance), value, None)
File "C:\Users\ramabodhi\Envs\test\lib\site-packages\sqlalchemy\orm\attributes.py", line 975, in set
lambda adapter, i: adapter.adapt_like_to_iterable(i))
File "C:\Users\ramabodhi\Envs\test\lib\site-packages\sqlalchemy\orm\attributes.py", line 1010, in _set_iterable
collections.bulk_replace(new_values, old_collection, new_collection)
File "C:\Users\ramabodhi\Envs\test\lib\site-packages\sqlalchemy\orm\collections.py", line 782, in bulk_replace
constants = existing_idset.intersection(values or ())
File "C:\Users\ramabodhi\Envs\test\lib\site-packages\sqlalchemy\util\_collections.py", line 592, in intersection
result._members.update(self._working_set(members).intersection(other))
TypeError: unhashable type: 'Role'
I am on python 3.3 windows 7 and all my packages are up to date.
回答1:
I have a "solution" for this, but I want to make sure its a viable solution and not an unstable hack.
I realize that adding the ne and eq functions to the class requires the hash function to be added in order for the model to be hashable. So this is now working:
class Role(db.Model):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
def __init__(self, name, desc):
self.name = name
self.description = desc
def __repr__(self):
return '<Role: {}>'.format(str(self.name))
def __eq__(self, other):
return (self.name == other or
self.name == getattr(other, 'name', None))
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash(self.name)
Let me know if I've done this properly, thanks!
来源:https://stackoverflow.com/questions/21315633/unhashable-type-error-when-modifying-sqlalchemy-models