Skip to content

Commit

Permalink
Merge pull request #9 from colibris-framework/pagination
Browse files Browse the repository at this point in the history
Pagination
  • Loading branch information
lucifurtun authored Jul 17, 2019
2 parents a605895 + 79c3a69 commit 50dc9d6
Show file tree
Hide file tree
Showing 9 changed files with 269 additions and 155 deletions.
47 changes: 47 additions & 0 deletions colibris/pagination.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import math

from colibris.api.envelope import wrap_many
from marshmallow.validate import Range

from colibris import api
from colibris.schemas import fields, Schema, ValidationError

page_validator = Range(min=1, error='Value must be greater than 0.')


class PageNumberPagination:
PAGE_SIZE = 25
PAGE_SIZE_QUERY_PARAM = 'page_size'
PAGE_QUERY_PARAM = 'page'

_query_schema = type('PaginationQuerySchema', (Schema,), {
PAGE_SIZE_QUERY_PARAM: fields.Integer(missing=PAGE_SIZE, validate=page_validator),
PAGE_QUERY_PARAM: fields.Integer(missing=1, validate=page_validator)
})()

def __init__(self, query, request):
self.query = query
self.request = request

try:
query_params = self._query_schema.load(self.request.query)
except ValidationError as err:
raise api.SchemaError(details=err.messages)

self.page = query_params[self.PAGE_QUERY_PARAM]
self.page_size = query_params[self.PAGE_SIZE_QUERY_PARAM]

def paginate_query(self):
return self.query.paginate(self.page, self.page_size)

def get_enveloped_data(self, data):
count = self.query.count()
pages = math.ceil(count / self.page_size)

return wrap_many(
objs=data,
count=count,
page=self.page,
pages=pages,
page_size=self.page_size
)
8 changes: 0 additions & 8 deletions colibris/schemas/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,6 @@ def __init__(self, meta, **kwargs):
class ModelSchema(marshmallow_peewee.ModelSchema):
OPTIONS_CLASS = ModelSchemaOpts

@post_dump(pass_many=True)
def wrap(self, data, many, **kwargs):
if many:
return envelope.wrap_many(data)

else:
return envelope.wrap_one(data)

@post_load
def make_instance(self, data, **kwargs):
if not self.opts.load_instance:
Expand Down
8 changes: 4 additions & 4 deletions colibris/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class APIView(View):
body_schema_class = None
query_schema_class = None

def get_body_schema_class(self, *args, **kwargs):
def get_body_schema(self, *args, **kwargs):
assert self.body_schema_class is not None, 'The attribute "body_schema_class" is required for {}'.format(self)

kwargs.update({
Expand All @@ -20,7 +20,7 @@ def get_body_schema_class(self, *args, **kwargs):

return schema

def get_query_schema_class(self, *args, **kwargs):
def get_query_schema(self, *args, **kwargs):
assert self.query_schema_class is not None, 'The attribute "query_schema_class" is required for {}'.format(self)

kwargs.update({
Expand All @@ -33,7 +33,7 @@ def get_query_schema_class(self, *args, **kwargs):

async def get_validated_body(self, schema=None):
if schema is None:
schema = self.get_body_schema_class()
schema = self.get_body_schema()

json_payload = await self.get_request_payload()

Expand All @@ -57,7 +57,7 @@ async def get_request_payload(self):

async def get_validated_query(self, schema=None):
if schema is None:
schema = self.get_query_schema_class()
schema = self.get_query_schema()

query = await self.get_request_query()

Expand Down
24 changes: 18 additions & 6 deletions colibris/views/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,29 @@ def __init__(cls, name, bases, attrs):

class ListMixin(metaclass=_GenericMixinMeta):
async def get(self):
items = self.get_query()
schema = self.get_body_schema_class(many=True)
result = schema.dump(list(items))
query = self.get_query()
schema = self.get_body_schema(many=True)

if self.pagination_class is not None:
return await self._get_paginated_response(query, schema)

result = schema.dump(list(query))

return web.json_response(result)

async def _get_paginated_response(self, query, schema):
paginator = self.pagination_class(query, self.request)
paginated_query = paginator.paginate_query()
result = schema.dump(list(paginated_query))
paginated_result = paginator.get_enveloped_data(result)

return web.json_response(paginated_result)


class CreateMixin(metaclass=_GenericMixinMeta):
async def post(self):
query = self.get_query()
schema = self.get_body_schema_class()
schema = self.get_body_schema()
data = await self.get_validated_body(schema)

instance = query.model.create(**data)
Expand All @@ -70,7 +82,7 @@ async def put(self):
async def _update(self, partial):
instance = self.get_object()

schema = self.get_body_schema_class(partial=partial, instance=instance)
schema = self.get_body_schema(partial=partial, instance=instance)
data = await self.get_validated_body(schema)

instance.update_fields(data)
Expand All @@ -91,7 +103,7 @@ async def delete(self):

class RetrieveMixin(metaclass=_GenericMixinMeta):
async def get(self):
schema = self.get_body_schema_class()
schema = self.get_body_schema()
instance = self.get_object()

result = schema.dump(instance)
Expand Down
2 changes: 1 addition & 1 deletion colibris/views/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class ModelView(APIView):
model = None
body_schema_class = None
pagination_class = None
url_identifier = 'id'
lookup_field = 'id'

Expand Down
136 changes: 0 additions & 136 deletions tests/test_views/test_model_view.py

This file was deleted.

File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 50dc9d6

Please sign in to comment.