Commit 818220f6 by Nicolas Joyard

Add API pagination and list URLs

parent a9b5786f
Showing with 149 additions and 73 deletions
# -*- coding: utf-8 -*-
from flask import abort, jsonify
from flask import abort, jsonify, request, url_for
from flask_marshmallow import Marshmallow
from .models import Regime, Legislature, Organe, Acteur, Mandat
class PaginationResult(object):
def __init__(self, items):
self._items = items
self._count = len(items)
class API(object):
def __init__(self, schemas):
self.schemas = schemas
self.tables = {i['model'].__tablename__: i for i in schemas}
self.descriptions = {i['model'].__tablename__: i['description']
for i in schemas}
def __init__(self, ma, app):
self.endpoints = {}
self.ma = ma
self.page_size = app.config['API_PAGE_SIZE'] or 10
def listURL(self, table):
return self.ma.AbsoluteURLFor('api_list', table=table)
def detailURL(self, table):
return self.ma.AbsoluteURLFor('api_detail', table=table, id='<id>')
def nested(self, schema):
return self.ma.Nested(schema)
def nestedList(self, schema):
return self.ma.List(self.ma.Nested(schema))
@property
def descriptions(self):
return {k: v['description'] for k, v in self.endpoints.items()}
def endpoint(self, model, detail_schema, **kwargs):
table = model.__tablename__
self.endpoints[table] = {
'model': model,
'detail_schema': self.add_list_url(detail_schema),
'list_schema': self.add_pagination(kwargs.get('list_schema',
detail_schema)),
'description': kwargs.get('description', table)
}
def add_list_url(self, schema):
table = schema.Meta.model.__tablename__
_fields = ()
if len(schema.Meta.fields):
_fields = schema.Meta.fields + ('_list',)
class ListSchema(schema):
class Meta(schema.Meta):
fields = _fields
_list = self.listURL(table)
return ListSchema
def add_pagination(self, schema):
class PaginatedSchema(self.ma.Schema):
_count = self.ma.Int()
_items = self.ma.List(self.ma.Nested(schema))
_prev = self.ma.Str()
_next = self.ma.Str()
return PaginatedSchema
def get_schema_or_404(self, table):
if table not in self.tables:
if table not in self.endpoints:
abort(404)
item = self.tables[table]
item = self.endpoints[table]
model = item['model']
schema = item['schema']
list_schema = item.get('list_schema', schema)
detail_schema = item['detail_schema']
list_schema = item['list_schema']
return list_schema, schema, model
return list_schema, detail_schema, model
def get_list_or_404(self, table):
list_schema, detail_schema, model = self.get_schema_or_404(table)
items = model.query.all()
result = list_schema(many=True).dump(items)
page = int(request.args.get('page') or 1)
size = int(request.args.get('page_size') or self.page_size)
data = model.query.paginate(page, size)
obj = {
'_count': data.total,
'_items': data.items,
}
if data.has_prev:
obj['_prev'] = '%s?page=%d%s' % (
url_for('api_list', table=table, _external=True),
data.prev_num,
'&page_size=%d' % size if size != self.page_size else ''
)
if data.has_next:
obj['_next'] = '%s?page=%d%s' % (
url_for('api_list', table=table, _external=True),
data.next_num,
'&page_size=%d' % size if size != self.page_size else ''
)
result = list_schema().dump(obj)
return jsonify(result.data)
def get_detail_or_404(self, table, id):
list_schema, detail_schema, model = self.get_schema_or_404(table)
item = model.query.get_or_404(id)
result = detail_schema().dump(item)
return jsonify(result.data)
......@@ -54,17 +135,7 @@ class API(object):
def setup_api(app):
ma = Marshmallow(app)
# API helpers
def detailURL(table):
return ma.AbsoluteURLFor('api_detail', table=table, id='<id>')
def nested(schema):
return ma.Nested(schema)
def nestedList(schema):
return ma.List(ma.Nested(schema))
api = API(ma, app)
# Base schemas (for use in lists & some relations)
......@@ -73,35 +144,35 @@ def setup_api(app):
model = Regime
fields = ('nom', '_url')
_url = detailURL('regimes')
_url = api.detailURL('regimes')
class LegislatureBaseSchema(ma.ModelSchema):
class Meta:
model = Legislature
fields = ('numero', '_url')
_url = detailURL('legislatures')
_url = api.detailURL('legislatures')
class OrganeBaseSchema(ma.ModelSchema):
class Meta:
model = Organe
fields = ('libelle', 'type', '_url')
_url = detailURL('organes')
_url = api.detailURL('organes')
class ActeurBaseSchema(ma.ModelSchema):
class Meta:
model = Acteur
fields = ('nom', 'prenom', '_url')
_url = detailURL('acteurs')
_url = api.detailURL('acteurs')
class MandatBaseSchema(ma.ModelSchema):
class Meta:
model = Mandat
fields = ('qualite', '_url')
_url = detailURL('mandats')
_url = api.detailURL('mandats')
# Semi-detailed schemas (for use in some relations)
......@@ -109,13 +180,13 @@ def setup_api(app):
class Meta(MandatBaseSchema.Meta):
fields = MandatBaseSchema.Meta.fields + ('organe',)
organe = nested(OrganeBaseSchema)
organe = api.nested(OrganeBaseSchema)
class MandatOrganeSchema(MandatBaseSchema):
class Meta(MandatBaseSchema.Meta):
fields = MandatBaseSchema.Meta.fields + ('acteur',)
acteur = nested(ActeurBaseSchema)
acteur = api.nested(ActeurBaseSchema)
# Detailed schemas
......@@ -123,68 +194,72 @@ def setup_api(app):
class Meta(RegimeBaseSchema.Meta):
fields = ()
legislatures = nestedList(LegislatureBaseSchema)
organes = nestedList(OrganeBaseSchema)
legislatures = api.nestedList(LegislatureBaseSchema)
organes = api.nestedList(OrganeBaseSchema)
class LegislatureDetailSchema(LegislatureBaseSchema):
class Meta(LegislatureBaseSchema.Meta):
fields = ()
regime = nested(RegimeBaseSchema)
organes = nestedList(OrganeBaseSchema)
regime = api.nested(RegimeBaseSchema)
organes = api.nestedList(OrganeBaseSchema)
class OrganeDetailSchema(OrganeBaseSchema):
class Meta(OrganeBaseSchema.Meta):
fields = ()
legislature = nested(LegislatureBaseSchema)
mandats = nestedList(MandatOrganeSchema)
regime = nested(RegimeBaseSchema)
legislature = api.nested(LegislatureBaseSchema)
mandats = api.nestedList(MandatOrganeSchema)
regime = api.nested(RegimeBaseSchema)
class ActeurDetailSchema(ActeurBaseSchema):
class Meta(ActeurBaseSchema.Meta):
fields = ()
mandats = nestedList(MandatActeurSchema)
mandats = api.nestedList(MandatActeurSchema)
class MandatDetailSchema(MandatBaseSchema):
class Meta(MandatBaseSchema.Meta):
fields = ()
acteur = nested(ActeurBaseSchema)
organe = nested(OrganeBaseSchema)
acteur = api.nested(ActeurBaseSchema)
organe = api.nested(OrganeBaseSchema)
# API creation
return API([
{
'model': Regime,
'description': u'Régimes politiques',
'schema': RegimeDetailSchema,
'list_schema': RegimeBaseSchema
},
{
'model': Legislature,
'description': u'Législatures',
'schema': LegislatureDetailSchema,
'list_schema': LegislatureBaseSchema
},
{
'model': Organe,
'description': u'Organes (ministères, commissions, organismes...)',
'schema': OrganeDetailSchema,
'list_schema': OrganeBaseSchema
},
{
'model': Acteur,
'description': u'Acteurs (ministres, parlementaires...)',
'schema': ActeurDetailSchema,
'list_schema': ActeurBaseSchema
},
{
'model': Mandat,
'description': u'Mandats',
'schema': MandatDetailSchema,
'list_schema': MandatBaseSchema
},
])
api.endpoint(
Regime,
RegimeDetailSchema,
list_schema=RegimeBaseSchema,
description=u'Régimes politiques'
)
api.endpoint(
Legislature,
LegislatureDetailSchema,
list_schema=LegislatureBaseSchema,
description=u'Législatures'
)
api.endpoint(
Organe,
OrganeDetailSchema,
list_schema=OrganeBaseSchema,
description=u'Organes (ministères, commissions, organismes...)'
)
api.endpoint(
Acteur,
ActeurDetailSchema,
list_schema=ActeurBaseSchema,
description=u'Acteurs (ministres, parlementaires...)'
)
api.endpoint(
Mandat,
MandatDetailSchema,
list_schema=MandatBaseSchema,
description=u'Mandats'
)
return api
......@@ -13,6 +13,7 @@ class Config(object):
'postgresql://parlapi:parlapi@localhost:5432/parlapi'
SQLALCHEMY_TRACK_MODIFICATIONS = False
DATA_DIR = os.path.join(BASE_DIR, 'data')
API_PAGE_SIZE = 10
class DebugConfig(Config):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment