Skip to content
This repository was archived by the owner on Jan 23, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions example/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class UserResource(Resource):
@api.register()
class UserView(ResourceView):
resource = UserResource
methods = [Create, Update, Fetch, List, Delete]
methods = [Create, Update, Fetch, BulkFetch, Delete]

class ContentResource(Resource):
document = documents.Content
Expand Down Expand Up @@ -87,7 +87,7 @@ def update_object(self, obj, data=None, save=True, parent_resources=None):
@api.register(name='posts', url='/posts/')
class PostView(ResourceView):
resource = PostResource
methods = [Create, Update, BulkUpdate, Fetch, List, Delete]
methods = [Create, Update, BulkUpdate, Fetch, BulkFetch, Delete]

class LimitedPostResource(Resource):
document = documents.Post
Expand All @@ -98,7 +98,7 @@ class LimitedPostResource(Resource):
@api.register(name='limited_posts', url='/limited_posts/')
class LimitedPostView(ResourceView):
resource = LimitedPostResource
methods = [Create, Update, Fetch, List]
methods = [Create, Update, Fetch, BulkFetch]

class DummyAuthenication(AuthenticationBase):
def authorized(self):
Expand All @@ -107,15 +107,15 @@ def authorized(self):
@api.register(name='auth', url='/auth/')
class DummyAuthView(ResourceView):
resource = PostResource
methods = [Create, Update, Fetch, List, Delete]
methods = [Create, Update, Fetch, BulkFetch, Delete]
authentication_methods = [DummyAuthenication]

@api.register(name='restricted', url='/restricted/')
class RestrictedPostView(ResourceView):
"""This class allows us to put restrictions in place regarding
who/what can be read, changed, added or deleted"""
resource = PostResource
methods = [Create, Update, Fetch, List, Delete]
methods = [Create, Update, Fetch, BulkFetch, Delete]

# Can't read a post if it isn't published
def has_read_permission(self, request, qs):
Expand Down Expand Up @@ -153,13 +153,13 @@ def upper_name(self, obj):
@api.register(name='test', url='/test/')
class TestView(ResourceView):
resource = TestResource
methods = [Create, Update, Fetch, List]
methods = [Create, Update, Fetch, BulkFetch]


@api.register(name='testfields', url='/testfields/')
class TestFieldsResource(ResourceView):
resource = TestFieldsResource
methods = [Create, Update, Fetch, List]
methods = [Create, Update, Fetch, BulkFetch]

class LanguageResource(Resource):
document = documents.Language
Expand All @@ -175,7 +175,7 @@ class PersonResource(Resource):
@api.register(name='person', url='/person/')
class PersonView(ResourceView):
resource = PersonResource
methods = [Create, Update, Fetch, List]
methods = [Create, Update, Fetch, BulkFetch]

# extra resources for testing max_limit
class Post10Resource(PostResource):
Expand All @@ -187,12 +187,12 @@ class Post250Resource(PostResource):
@api.register(name='posts10', url='/posts10/')
class Post10View(ResourceView):
resource = Post10Resource
methods = [Create, Update, BulkUpdate, Fetch, List, Delete]
methods = [Create, Update, BulkUpdate, Fetch, BulkFetch, Delete]

@api.register(name='posts250', url='/posts250/')
class Post250View(ResourceView):
resource = Post250Resource
methods = [Create, Update, BulkUpdate, Fetch, List, Delete]
methods = [Create, Update, BulkUpdate, Fetch, BulkFetch, Delete]

# Documents, resources, and views for testing differences between db refs and object ids
class A(db.Document):
Expand All @@ -218,17 +218,17 @@ class CResource(Resource):
@api.register(url='/a/')
class AView(ResourceView):
resource = AResource
methods = [Create, Update, BulkUpdate, Fetch, List, Delete]
methods = [Create, Update, BulkUpdate, Fetch, BulkFetch, Delete]

@api.register(url='/b/')
class BView(ResourceView):
resource = BResource
methods = [Create, Update, BulkUpdate, Fetch, List, Delete]
methods = [Create, Update, BulkUpdate, Fetch, BulkFetch, Delete]

@api.register(url='/c/')
class CView(ResourceView):
resource = CResource
methods = [Create, Update, BulkUpdate, Fetch, List, Delete]
methods = [Create, Update, BulkUpdate, Fetch, BulkFetch, Delete]


# Documents, resources, and views for testing method permissions
Expand Down Expand Up @@ -261,7 +261,7 @@ class FetchOnlyView(ResourceView):
@api.register(url='/list_only/')
class ListOnlyView(ResourceView):
resource = MethodTestResource
methods = [List]
methods = [BulkFetch]

@api.register(url='/delete_only/')
class DeleteOnlyView(ResourceView):
Expand All @@ -277,7 +277,7 @@ class ViewMethodTestResource(Resource):
@api.register(url='/test_view_method/')
class TestViewMethodView(ResourceView):
resource = ViewMethodTestResource
methods = [Create, Update, BulkUpdate, Fetch, List, Delete]
methods = [Create, Update, BulkUpdate, Fetch, BulkFetch, Delete]

def _dispatch_request(self, *args, **kwargs):
super(TestViewMethodView, self)._dispatch_request(*args, **kwargs)
Expand All @@ -290,7 +290,7 @@ class DateTimeResource(Resource):
@api.register(name='datetime', url='/datetime/')
class DateTimeView(ResourceView):
resource = DateTimeResource
methods = [Create, Update, Fetch, List]
methods = [Create, Update, Fetch, BulkFetch]


# Document, resource, and view for testing invalid JSON
Expand All @@ -303,7 +303,7 @@ class DictDocResource(Resource):
@api.register(url='/dict_doc/')
class DictDocView(ResourceView):
resource = DictDocResource
methods = [Fetch, List, Create, Update]
methods = [Fetch, BulkFetch, Create, Update]


if __name__ == "__main__":
Expand Down
56 changes: 35 additions & 21 deletions flask_mongorest/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
from flask import Blueprint
from flask_mongorest.methods import Create, BulkUpdate, List
from flask_mongorest.methods import *

def register_class(app, klass, **kwargs):
# Construct a url based on a 'name' kwarg with a fallback to the
# view's class name. Note that the name must be unique.
name = kwargs.pop('name', klass.__name__)
view_func = klass.as_view(name)
url = kwargs.pop('url', None)
if not url:
document_name = klass.resource.document.__name__.lower()
url = f'/{document_name}/'

# Insert the url prefix, if it exists
url_prefix = kwargs.pop('url_prefix', '')
if url_prefix:
url = f'{url_prefix}{url}'

# Add url rules
klass_methods = set(klass.methods)
if Create in klass_methods and BulkCreate in klass_methods:
raise ValueError('Use either Create or BulkCreate!')

for x in klass_methods & {Fetch, Update, Delete}:
endpoint = view_func.__name__ + x.__name__
app.add_url_rule(
f'{url}<string:pk>/', defaults={'short_mime': None},
view_func=view_func, methods=[x.method], endpoint=endpoint, **kwargs
)

for x in klass_methods & {Create, BulkFetch, BulkCreate, BulkUpdate, BulkDelete}:
endpoint = view_func.__name__ + x.__name__
app.add_url_rule(
url, defaults={'pk': None, 'short_mime': None},
view_func=view_func, methods=[x.method], endpoint=endpoint, **kwargs
)

class MongoRest(object):
def __init__(self, app, **kwargs):
Expand All @@ -10,26 +43,7 @@ def __init__(self, app, **kwargs):

def register(self, **kwargs):
def decorator(klass):
# Construct a url based on a 'name' kwarg with a fallback to the
# view's class name. Note that the name must be unique.
name = kwargs.pop('name', klass.__name__)
url = kwargs.pop('url', None)
if not url:
document_name = klass.resource.document.__name__.lower()
url = '/%s/' % document_name

# Insert the url prefix, if it exists
if self.url_prefix:
url = '%s%s' % (self.url_prefix, url)

# Add url rules
pk_type = kwargs.pop('pk_type', 'string')
view_func = klass.as_view(name)
if List in klass.methods:
self.app.add_url_rule(url, defaults={'pk': None}, view_func=view_func, methods=[List.method], **kwargs)
if Create in klass.methods or BulkUpdate in klass.methods:
self.app.add_url_rule(url, view_func=view_func, methods=[x.method for x in klass.methods if x in (Create, BulkUpdate)], **kwargs)
self.app.add_url_rule('%s<%s:%s>/' % (url, pk_type, 'pk'), view_func=view_func, methods=[x.method for x in klass.methods if x not in (List, BulkUpdate)], **kwargs)
register_class(self.app, klass, **kwargs)
return klass

return decorator
Expand Down
26 changes: 20 additions & 6 deletions flask_mongorest/methods.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
import sys
import inspect

class Fetch:
method = 'GET'

class Create:
method = 'POST'

class Update:
method = 'PUT'

class BulkUpdate:
method = 'PUT'
class Delete:
method = 'DELETE'

class Fetch:
method = 'GET'

class List:
class BulkFetch:
method = 'GET'

class Delete:
class BulkCreate:
method = 'POST'

class BulkUpdate:
method = 'PUT'

class BulkDelete:
method = 'DELETE'


members = inspect.getmembers(sys.modules[__name__], inspect.isclass)
__all__ = [m[0] for m in members]
25 changes: 25 additions & 0 deletions flask_mongorest/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class Operator(object):
"""Base class that all the other operators should inherit from."""

op = 'exact'
typ = 'string'

# Can be overridden via constructor.
allow_negation = False
Expand All @@ -75,20 +76,42 @@ def apply(self, queryset, field, value, negate=False):
kwargs = self.prepare_queryset_kwargs(field, value, negate)
return queryset.filter(**kwargs)

def try_float(value):
try:
return float(value)
except ValueError:
return value

class Ne(Operator):
op = 'ne'

class Lt(Operator):
op = 'lt'
typ = 'number'

def prepare_queryset_kwargs(self, field, value, negate):
return {'__'.join(filter(None, [field, self.op])): try_float(value)}

class Lte(Operator):
op = 'lte'
typ = 'number'

def prepare_queryset_kwargs(self, field, value, negate):
return {'__'.join(filter(None, [field, self.op])): try_float(value)}

class Gt(Operator):
op = 'gt'
typ = 'number'

def prepare_queryset_kwargs(self, field, value, negate):
return {'__'.join(filter(None, [field, self.op])): try_float(value)}

class Gte(Operator):
op = 'gte'
typ = 'number'

def prepare_queryset_kwargs(self, field, value, negate):
return {'__'.join(filter(None, [field, self.op])): try_float(value)}

class Exact(Operator):
op = 'exact'
Expand All @@ -106,6 +129,7 @@ class IExact(Operator):

class In(Operator):
op = 'in'
typ = 'array'

def prepare_queryset_kwargs(self, field, value, negate):
# this is null if the user submits an empty in expression (like
Expand Down Expand Up @@ -140,6 +164,7 @@ class IEndswith(Operator):

class Boolean(Operator):
op = 'exact'
typ = 'boolean'

def prepare_queryset_kwargs(self, field, value, negate):
if value == 'false':
Expand Down
Loading