Skip to content

Commit e6466b3

Browse files
authored
be able to select all related columns (piccolo-orm#161)
1 parent d3159b8 commit e6466b3

File tree

2 files changed

+87
-31
lines changed

2 files changed

+87
-31
lines changed

piccolo/columns/column_types.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,13 +1006,20 @@ class Band(Table):
10061006
10071007
**Joins**
10081008
1009-
Can also use it to perform joins:
1009+
You also use it to perform joins:
10101010
10111011
.. code-block:: python
10121012
10131013
>>> await Band.select(Band.name, Band.manager.name).first().run()
10141014
{'name': 'Pythonistas', 'manager.name': 'Guido'}
10151015
1016+
To retrieve all of the columns in the related table:
1017+
1018+
.. code-block:: python
1019+
1020+
>>> await Band.select(Band.name, *Band.manager.all_columns()).first().run()
1021+
{'name': 'Pythonistas', 'manager.id': 1, 'manager.name': 'Guido'}
1022+
10161023
To get a referenced row as an object:
10171024
10181025
.. code-block:: python
@@ -1217,11 +1224,27 @@ def set_proxy_columns(self):
12171224
the ``ForeignKey`` column for each column in the table being pointed
12181225
to.
12191226
"""
1220-
_foreign_key_meta = object.__getattribute__(self, "_foreign_key_meta")
1221-
for column in _foreign_key_meta.resolved_references._meta.columns:
1227+
_fk_meta = object.__getattribute__(self, "_foreign_key_meta")
1228+
for column in _fk_meta.resolved_references._meta.columns:
12221229
_column: Column = column.copy()
12231230
setattr(self, _column._meta.name, _column)
1224-
_foreign_key_meta.proxy_columns.append(_column)
1231+
_fk_meta.proxy_columns.append(_column)
1232+
1233+
def all_columns():
1234+
"""
1235+
Allow a user to access all of the columns on the related table.
1236+
1237+
For example:
1238+
1239+
Band.select(Band.name, *Band.manager.all_columns()).run_sync()
1240+
1241+
"""
1242+
return [
1243+
getattr(self, column._meta.name)
1244+
for column in _fk_meta.resolved_references._meta.columns
1245+
]
1246+
1247+
setattr(self, "all_columns", all_columns)
12251248

12261249
def __getattribute__(self, name: str):
12271250
"""

tests/columns/test_foreignkey.py

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,34 @@
33

44
from piccolo.columns import Column, ForeignKey, LazyTableReference, Varchar
55
from piccolo.table import Table
6+
from tests.base import DBTestCase
7+
from tests.example_app.tables import Band, Manager
68

79

8-
class Manager(Table):
10+
class Manager1(Table, tablename="manager"):
911
name = Varchar()
1012
manager = ForeignKey("self", null=True)
1113

1214

13-
class Band1(Table):
14-
manager = ForeignKey(references=Manager)
15+
class Band1(Table, tablename="band"):
16+
manager = ForeignKey(references=Manager1)
1517

1618

17-
class Band2(Table):
18-
manager = ForeignKey(references="Manager")
19+
class Band2(Table, tablename="band"):
20+
manager = ForeignKey(references="Manager1")
1921

2022

21-
class Band3(Table):
23+
class Band3(Table, tablename="band"):
2224
manager = ForeignKey(
2325
references=LazyTableReference(
24-
table_class_name="Manager",
26+
table_class_name="Manager1",
2527
module_path="tests.columns.test_foreignkey",
2628
)
2729
)
2830

2931

30-
class Band4(Table):
31-
manager = ForeignKey(references="tests.columns.test_foreignkey.Manager")
32+
class Band4(Table, tablename="band"):
33+
manager = ForeignKey(references="tests.columns.test_foreignkey.Manager1")
3234

3335

3436
class TestForeignKeySelf(TestCase):
@@ -38,18 +40,18 @@ class TestForeignKeySelf(TestCase):
3840
"""
3941

4042
def setUp(self):
41-
Manager.create_table().run_sync()
43+
Manager1.create_table().run_sync()
4244

4345
def test_foreign_key_self(self):
44-
manager = Manager(name="Mr Manager")
46+
manager = Manager1(name="Mr Manager")
4547
manager.save().run_sync()
4648

47-
worker = Manager(name="Mr Worker", manager=manager.id)
49+
worker = Manager1(name="Mr Worker", manager=manager.id)
4850
worker.save().run_sync()
4951

5052
response = (
51-
Manager.select(Manager.name, Manager.manager.name)
52-
.order_by(Manager.name)
53+
Manager1.select(Manager1.name, Manager1.manager.name)
54+
.order_by(Manager1.name)
5355
.run_sync()
5456
)
5557
self.assertEqual(
@@ -61,7 +63,7 @@ def test_foreign_key_self(self):
6163
)
6264

6365
def tearDown(self):
64-
Manager.alter().drop_table().run_sync()
66+
Manager1.alter().drop_table().run_sync()
6567

6668

6769
class TestForeignKeyString(TestCase):
@@ -71,23 +73,25 @@ class TestForeignKeyString(TestCase):
7173
"""
7274

7375
def setUp(self):
74-
Manager.create_table().run_sync()
76+
Manager1.create_table().run_sync()
7577

7678
def test_foreign_key_string(self):
7779
Band2.create_table().run_sync()
7880
self.assertEqual(
79-
Band2.manager._foreign_key_meta.resolved_references, Manager
81+
Band2.manager._foreign_key_meta.resolved_references,
82+
Manager1,
8083
)
8184
Band2.alter().drop_table().run_sync()
8285

8386
Band4.create_table().run_sync()
8487
self.assertEqual(
85-
Band4.manager._foreign_key_meta.resolved_references, Manager
88+
Band4.manager._foreign_key_meta.resolved_references,
89+
Manager1,
8690
)
8791
Band4.alter().drop_table().run_sync()
8892

8993
def tearDown(self):
90-
Manager.alter().drop_table().run_sync()
94+
Manager1.alter().drop_table().run_sync()
9195

9296

9397
class TestForeignKeyRelativeError(TestCase):
@@ -98,7 +102,7 @@ def test_foreign_key_relative_error(self):
98102
"""
99103
with self.assertRaises(ValueError) as manager:
100104

101-
class Band(Table):
105+
class BandRelative(Table, tablename="band"):
102106
manager = ForeignKey("..example_app.tables.Manager", null=True)
103107

104108
self.assertEqual(
@@ -112,14 +116,14 @@ def test_foreign_key_references(self):
112116
Make sure foreign key references are stored correctly on the table
113117
which is the target of the ForeignKey.
114118
"""
115-
self.assertEqual(len(Manager._meta.foreign_key_references), 5)
119+
self.assertEqual(len(Manager1._meta.foreign_key_references), 5)
116120

117121
self.assertTrue(Band1.manager in Manager._meta.foreign_key_references)
118122
self.assertTrue(Band2.manager in Manager._meta.foreign_key_references)
119123
self.assertTrue(Band3.manager in Manager._meta.foreign_key_references)
120124
self.assertTrue(Band4.manager in Manager._meta.foreign_key_references)
121125
self.assertTrue(
122-
Manager.manager in Manager._meta.foreign_key_references
126+
Manager1.manager in Manager1._meta.foreign_key_references
123127
)
124128

125129

@@ -128,8 +132,6 @@ def test_lazy_reference_to_app(self):
128132
"""
129133
Make sure a LazyTableReference to a Table within a Piccolo app works.
130134
"""
131-
from tests.example_app.tables import Manager
132-
133135
reference = LazyTableReference(
134136
table_class_name="Manager", app_name="example_app"
135137
)
@@ -153,18 +155,49 @@ def test_recursion_limit(self):
153155
if the call chain is too large.
154156
"""
155157
# Should be fine:
156-
column: Column = Manager.manager.name
158+
column: Column = Manager1.manager.name
157159
self.assertTrue(len(column._meta.call_chain), 1)
158160
self.assertTrue(isinstance(column, Varchar))
159161

160162
with self.assertRaises(Exception):
161-
Manager.manager.manager.manager.manager.manager.manager.manager.manager.manager.manager.manager.name # noqa
163+
Manager1.manager.manager.manager.manager.manager.manager.manager.manager.manager.manager.manager.name # noqa
162164

163165
def test_recursion_time(self):
164166
"""
165167
Make sure that a really large call chain doesn't take too long.
166168
"""
167169
start = time.time()
168-
Manager.manager.manager.manager.manager.manager.manager.name
170+
Manager1.manager.manager.manager.manager.manager.manager.name
169171
end = time.time()
170172
self.assertTrue(end - start < 1.0)
173+
174+
175+
class TestAllColumns(DBTestCase):
176+
def setUp(self):
177+
Manager.create_table().run_sync()
178+
manager = Manager(name="Guido")
179+
manager.save().run_sync()
180+
181+
Band.create_table().run_sync()
182+
Band(manager=manager, name="Pythonistas").save().run_sync()
183+
184+
def tearDown(self):
185+
Band.alter().drop_table().run_sync()
186+
Manager.alter().drop_table().run_sync()
187+
188+
def test_all_columns(self):
189+
"""
190+
Make sure you can retrieve all columns from a related table, without
191+
explicitly specifying them.
192+
"""
193+
result = Band.select(Band.name, *Band.manager.all_columns()).run_sync()
194+
self.assertEqual(
195+
result,
196+
[
197+
{
198+
"name": "Pythonistas",
199+
"manager.id": 1,
200+
"manager.name": "Guido",
201+
}
202+
],
203+
)

0 commit comments

Comments
 (0)