From 3e2c8998ac79bfb6b1b892e68517b601f4a8d30a Mon Sep 17 00:00:00 2001 From: Tymoteusz Jankowski Date: Sun, 20 Aug 2017 10:03:33 +0200 Subject: [PATCH 1/3] Add default __repr__ for Model class --- flask_sqlalchemy/__init__.py | 5 +++++ tests/test_model_class.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/flask_sqlalchemy/__init__.py b/flask_sqlalchemy/__init__.py index 8b54365a..6ce5f462 100644 --- a/flask_sqlalchemy/__init__.py +++ b/flask_sqlalchemy/__init__.py @@ -662,6 +662,11 @@ class Model(object): #: Equivalent to ``db.session.query(Model)`` unless :attr:`query_class` has been changed. query = None + def __repr__(self): + pks = inspect(self).identity_key[1] + pretty_pk = pks[0] if len(pks) == 1 else ', '.join(map(str, pks)) + return '<{0}({1})>'.format(type(self).__name__, pretty_pk) + class SQLAlchemy(object): """This class is used to control the SQLAlchemy integration to one diff --git a/tests/test_model_class.py b/tests/test_model_class.py index 627543f7..f65b7330 100644 --- a/tests/test_model_class.py +++ b/tests/test_model_class.py @@ -11,3 +11,17 @@ class SomeModel(db.Model): id = db.Column(db.Integer, primary_key=True) assert isinstance(SomeModel(), CustomModelClass) + + +def test_default_repr_works_when_no_override(app): + class CustomModelClass(fsa.Model): + pass + + db = fsa.SQLAlchemy(app, model_class=CustomModelClass) + + class SomeModel(db.Model): + id = db.Column(db.Integer, primary_key=True) + + instance = SomeModel() + + assert instance.__repr__, "".format(instance.pk) From 6a0fc24611f6c2fda97a5356ddc180b476fa393c Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 26 Sep 2017 10:59:30 -0700 Subject: [PATCH 2/3] adjust repr format, fix test --- flask_sqlalchemy/__init__.py | 5 ++--- tests/test_model_class.py | 33 +++++++++++++++++++++------------ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/flask_sqlalchemy/__init__.py b/flask_sqlalchemy/__init__.py index 6ce5f462..253cf070 100644 --- a/flask_sqlalchemy/__init__.py +++ b/flask_sqlalchemy/__init__.py @@ -663,9 +663,8 @@ class Model(object): query = None def __repr__(self): - pks = inspect(self).identity_key[1] - pretty_pk = pks[0] if len(pks) == 1 else ', '.join(map(str, pks)) - return '<{0}({1})>'.format(type(self).__name__, pretty_pk) + pk = ', '.join(str(value) for value in inspect(self).identity) + return '<{0} {1}>'.format(self.__class__.__name__, pk) class SQLAlchemy(object): diff --git a/tests/test_model_class.py b/tests/test_model_class.py index f65b7330..22679fae 100644 --- a/tests/test_model_class.py +++ b/tests/test_model_class.py @@ -13,15 +13,24 @@ class SomeModel(db.Model): assert isinstance(SomeModel(), CustomModelClass) -def test_default_repr_works_when_no_override(app): - class CustomModelClass(fsa.Model): - pass - - db = fsa.SQLAlchemy(app, model_class=CustomModelClass) - - class SomeModel(db.Model): - id = db.Column(db.Integer, primary_key=True) - - instance = SomeModel() - - assert instance.__repr__, "".format(instance.pk) +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) + + r = Report(id=2, user_name=u.name) + db.session.add(r) + db.session.flush() + assert repr(r) == '' + assert repr(u) == str(u) From bceb09b9284aac57f2628fc6272e0d02cc41e068 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 26 Sep 2017 13:18:49 -0700 Subject: [PATCH 3/3] unicode -> utf8 in repr for py2 --- flask_sqlalchemy/__init__.py | 6 +++--- flask_sqlalchemy/_compat.py | 21 +++++++++++++++++++-- tests/test_model_class.py | 8 ++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/flask_sqlalchemy/__init__.py b/flask_sqlalchemy/__init__.py index 253cf070..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' @@ -663,8 +663,8 @@ class Model(object): query = None def __repr__(self): - pk = ', '.join(str(value) for value in inspect(self).identity) - return '<{0} {1}>'.format(self.__class__.__name__, pk) + pk = ', '.join(to_str(value) for value in inspect(self).identity) + return '<{0} {1}>'.format(type(self).__name__, pk) class SQLAlchemy(object): 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 22679fae..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): @@ -29,6 +31,12 @@ class Report(db.Model): 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()