diff --git a/flask_sqlalchemy/__init__.py b/flask_sqlalchemy/__init__.py index 8b54365a..619c7196 100644 --- a/flask_sqlalchemy/__init__.py +++ b/flask_sqlalchemy/__init__.py @@ -30,7 +30,7 @@ from sqlalchemy.orm.exc import UnmappedClassError from sqlalchemy.orm.session import Session as SessionBase -from ._compat import itervalues, string_types, xrange +from ._compat import itervalues, string_types, xrange, to_str __version__ = '2.2.1' @@ -662,6 +662,10 @@ class Model(object): #: Equivalent to ``db.session.query(Model)`` unless :attr:`query_class` has been changed. query = None + def __repr__(self): + pk = ', '.join(to_str(value) for value in inspect(self).identity) + return '<{0} {1}>'.format(type(self).__name__, pk) + class SQLAlchemy(object): """This class is used to control the SQLAlchemy integration to one diff --git a/flask_sqlalchemy/_compat.py b/flask_sqlalchemy/_compat.py index cc0bd81a..f1786a1c 100644 --- a/flask_sqlalchemy/_compat.py +++ b/flask_sqlalchemy/_compat.py @@ -10,7 +10,6 @@ """ import sys - PY2 = sys.version_info[0] == 2 @@ -25,6 +24,15 @@ def itervalues(d): string_types = (unicode, bytes) + def to_str(x, charset='utf8', errors='strict'): + if x is None or isinstance(x, str): + return x + + if isinstance(x, unicode): + return x.encode(charset, errors) + + return str(x) + else: def iteritems(d): return iter(d.items()) @@ -34,4 +42,13 @@ def itervalues(d): xrange = range - string_types = (str, ) + string_types = (str,) + + def to_str(x, charset='utf8', errors='strict'): + if x is None or isinstance(x, str): + return x + + if isinstance(x, bytes): + return x.decode(charset, errors) + + return str(x) diff --git a/tests/test_model_class.py b/tests/test_model_class.py index 627543f7..e2e82ca8 100644 --- a/tests/test_model_class.py +++ b/tests/test_model_class.py @@ -1,4 +1,6 @@ +# coding=utf8 import flask_sqlalchemy as fsa +from flask_sqlalchemy._compat import to_str def test_custom_query_class(app): @@ -11,3 +13,32 @@ class SomeModel(db.Model): id = db.Column(db.Integer, primary_key=True) assert isinstance(SomeModel(), CustomModelClass) + + +def test_repr(db): + class User(db.Model): + name = db.Column(db.String, primary_key=True) + + class Report(db.Model): + id = db.Column(db.Integer, primary_key=True, autoincrement=False) + user_name = db.Column(db.ForeignKey(User.name), primary_key=True) + + db.create_all() + + u = User(name='test') + db.session.add(u) + db.session.flush() + assert repr(u) == '' + assert repr(u) == str(u) + + u2 = User(name=u'🐍') + db.session.add(u2) + db.session.flush() + assert repr(u2) == to_str(u'') + assert repr(u2) == str(u2) + + r = Report(id=2, user_name=u.name) + db.session.add(r) + db.session.flush() + assert repr(r) == '' + assert repr(u) == str(u)