Skip to content

Commit

Permalink
Merge pull request #5 from piccolo-orm/readable_get_single
Browse files Browse the repository at this point in the history
sinisaos suggestion - support __readable parameter in get detail
  • Loading branch information
dantownsend committed Dec 8, 2020
2 parents e90a4c5 + f71dcc1 commit b4fff73
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 19 deletions.
38 changes: 29 additions & 9 deletions piccolo_api/crud/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,23 @@ def pydantic_model(self) -> t.Type[pydantic.BaseModel]:
self.table, model_name=f"{self.table.__name__}In"
)

def _pydantic_model_output(
self, include_readable: bool = False
) -> t.Type[pydantic.BaseModel]:
return create_pydantic_model(
self.table,
include_default_columns=True,
include_readable=include_readable,
model_name=f"{self.table.__name__}Output",
)

@property
def pydantic_model_output(self) -> t.Type[pydantic.BaseModel]:
"""
Contains the default columns, which is required when exporting
data (for example, in a GET request).
"""
return create_pydantic_model(
self.table,
include_default_columns=True,
model_name=f"{self.table.__name__}Output",
)
return self._pydantic_model_output()

@property
def pydantic_model_optional(self) -> t.Type[pydantic.BaseModel]:
Expand Down Expand Up @@ -514,7 +520,7 @@ async def detail(self, request: Request) -> Response:
)

if request.method == "GET":
return await self._get_single(row_id)
return await self._get_single(request, row_id)
elif request.method == "PUT":
data = await request.json()
return await self._put_single(row_id, data)
Expand All @@ -526,13 +532,23 @@ async def detail(self, request: Request) -> Response:
else:
return Response(status_code=405)

async def _get_single(self, row_id: int) -> Response:
async def _get_single(self, request: Request, row_id: int) -> Response:
"""
Returns a single row.
"""
params = dict(request.query_params)
split_params: Params = self._split_params(params)
try:
columns = self.table._meta.columns
if split_params.include_readable:
readable_columns = [
self.table._get_related_readable(i)
for i in self.table._meta.foreign_key_columns
]
columns = columns + readable_columns

row = (
await self.table.select()
await self.table.select(*columns)
.where(self.table.id == row_id)
.first()
.run()
Expand All @@ -541,7 +557,11 @@ async def _get_single(self, row_id: int) -> Response:
return Response(
"Unable to find a resource with that ID.", status_code=404
)
return CustomJSONResponse(self.pydantic_model_output(**row).json())
return CustomJSONResponse(
self._pydantic_model_output(
include_readable=split_params.include_readable
)(**row).json()
)

async def _put_single(
self, row_id: int, data: t.Dict[str, t.Any]
Expand Down
70 changes: 60 additions & 10 deletions tests/crud/test_crud_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,18 +310,20 @@ def test_put_new(self):


class TestGetAll(TestCase):

movies = [
{"name": "Star Wars", "rating": 93},
{"name": "Lord of the Rings", "rating": 90},
]

def setUp(self):
Movie.create_table(if_not_exists=True).run_sync()
Movie.insert(*[Movie(**kwargs) for kwargs in self.movies]).run_sync()

movie = Movie(name="Star Wars", rating=93)
movie.save().run_sync()

Movie(name="Lord of the Rings", rating=90).save().run_sync()

Role.create_table(if_not_exists=True).run_sync()
Role(name="Luke Skywalker", movie=movie.id).save().run_sync()

def tearDown(self):
Movie.alter().drop_table().run_sync()
Role.alter().drop_table().run_sync()

def test_get_all(self):
"""
Expand All @@ -335,6 +337,36 @@ def test_get_all(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"rows": rows})

def test_get_all_readable(self):
"""
Make sure that bulk GETs with the ``__readable`` parameter return the
correct data.
"""
client = TestClient(PiccoloCRUD(table=Role, read_only=False))

response = client.get("/", params={"__readable": "true"})
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json(),
{
"rows": [
{
"id": 1,
"name": "Luke Skywalker",
"movie": 1,
"movie_readable": "Star Wars",
}
]
},
)

response = client.get("/", params={})
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json(),
{"rows": [{"id": 1, "name": "Luke Skywalker", "movie": 1}]},
)

def test_reverse_order(self):
"""
Make sure that descending ordering works, e.g. ``__order=-id``.
Expand Down Expand Up @@ -491,23 +523,41 @@ def test_post_error(self):
class TestGet(TestCase):
def setUp(self):
Movie.create_table(if_not_exists=True).run_sync()
Role.create_table(if_not_exists=True).run_sync()

def tearDown(self):
Movie.alter().drop_table().run_sync()
Role.alter().drop_table().run_sync()

def test_get(self):
"""
Make sure a get can return a row successfully.
"""
client = TestClient(PiccoloCRUD(table=Movie, read_only=False))
client = TestClient(PiccoloCRUD(table=Role, read_only=False))

movie = Movie(name="Star Wars", rating=93)
movie.save().run_sync()

response = client.get(f"/{movie.id}/")
role = Role(name="Luke Skywalker", movie=movie.id)
role.save().run_sync()

response = client.get(f"/{role.id}/")
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json(), {"id": 1, "name": "Star Wars", "rating": 93}
response.json(),
{"id": role.id, "name": "Luke Skywalker", "movie": movie.id},
)

response = client.get(f"/{role.id}/", params={"__readable": "true"})
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json(),
{
"id": role.id,
"name": "Luke Skywalker",
"movie": movie.id,
"movie_readable": "Star Wars",
},
)

response = client.get(f"/123/")
Expand Down

0 comments on commit b4fff73

Please sign in to comment.