Restful API framework wrapped around MongoEngine

Overview

Flask-MongoRest Build Status

A Restful API framework wrapped around MongoEngine.

Setup

from flask import Flask
from flask_mongoengine import MongoEngine
from flask_mongorest import MongoRest
from flask_mongorest.views import ResourceView
from flask_mongorest.resources import Resource
from flask_mongorest import operators as ops
from flask_mongorest import methods


app = Flask(__name__)

app.config.update(
    MONGODB_HOST = 'localhost',
    MONGODB_PORT = '27017',
    MONGODB_DB = 'mongorest_example_app',
)

db = MongoEngine(app)
api = MongoRest(app)

class User(db.Document):
    email = db.EmailField(unique=True, required=True)

class Content(db.EmbeddedDocument):
    text = db.StringField()

class ContentResource(Resource):
    document = Content

class Post(db.Document):
    title = db.StringField(max_length=120, required=True)
    author = db.ReferenceField(User)
    content = db.EmbeddedDocumentField(Content)

class PostResource(Resource):
    document = Post
    related_resources = {
        'content': ContentResource,
    }
    filters = {
        'title': [ops.Exact, ops.Startswith],
        'author_id': [ops.Exact],
    }
    rename_fields = {
        'author': 'author_id',
    }

@api.register(name='posts', url='/posts/')
class PostView(ResourceView):
    resource = PostResource
    methods = [methods.Create, methods.Update, methods.Fetch, methods.List]

With this app, following cURL commands could be used:

Create a Post:
curl -H "Content-Type: application/json" -X POST -d \
'{"title": "First post!", "author_id": "author_id_from_a_previous_api_call", "content": {"text": "this is our test post content"}}' http://0.0.0.0:5000/posts/
{
  "id": "1",
  "title": "First post!",
  "author_id": "author_id_from_a_previous_api_call",
  "content": {
    "text": "this is our test post content"
  }
} 

Get a Post:

curl http://0.0.0.0:5000/posts/1/
{
  "id": "1",
  "title": "First post!",
  "author_id": "author_id_from_a_previous_api_call",
  "content": {
    "text": "this is our test post content"
  }
} 

List all Posts or filter by the title:

curl http://0.0.0.0:5000/posts/ or curl http://0.0.0.0:5000/posts/?title__startswith=First%20post
{
  "data": [
    {
      "id": "1",
      "title": "First post!",
      "author_id": "author_id_from_a_previous_api_call",
      "content": {
        "text": "this is our test post content"
      }
    },
    ... other posts
  ]
}

Delete a Post:

curl -X DELETE http://0.0.0.0:5000/posts/1/
# Fails since PostView.methods does not allow Delete

Request Params

_skip and _limit => utilize the built-in functions of mongodb.

_fields => limit the response's fields to those named here (comma separated).

_order_by => order results if this string is present in the Resource.allowed_ordering list.

Resource Configuration

rename_fields => dict of renaming rules. Useful for mapping _id fields such as "organization": "organization_id"

filters => filter results of a List request using the allowed filters which are used like /user/?id__gt=2 or /user/[email protected]

related_resources => nested resource serialization for reference/embedded fields of a document

child_document_resources => Suppose you have a Person base class which has Male and Female subclasses. These subclasses and their respective resources share the same MongoDB collection, but have different fields and serialization characteristics. This dictionary allows you to map class instances to their respective resources to be used during serialization.

Authentication

The AuthenticationBase class provides the ability for application's to implement their own API auth. Two common patterns are shown below along with a BaseResourceView which can be used as the parent View of all of your app's resources.

class SessionAuthentication(AuthenticationBase):
    def authorized(self):
        return current_user.is_authenticated()

class ApiKeyAuthentication(AuthenticationBase):
    """
    @TODO ApiKey document and key generation left to the specific implementation
    """
    def authorized(self):
        if 'AUTHORIZATION' in request.headers:
            authorization = request.headers['AUTHORIZATION'].split()
            if len(authorization) == 2 and authorization[0].lower() == 'basic':
                try:
                    authorization_parts = base64.b64decode(authorization[1]).partition(':')
                    key = smart_unicode(authorization_parts[0])
                    api_key = ApiKey.objects.get(key__exact=key)
                    if api_key.user:
                        login_user(api_key.user)
                        setattr(current_user, 'api_key', api_key)
                    return True
                except (TypeError, UnicodeDecodeError, ApiKey.DoesNotExist):
                    pass
        return False

class BaseResourceView(ResourceView):
    authentication_methods = [SessionAuthentication, ApiKeyAuthentication]

Running the test suite

This package uses nosetests for automated testing. Just run python setup.py nosetests to run the tests. No setup or any other prep needed.

Contributing

Pull requests are greatly appreciated!

Comments
  • Dealing with embedded documents.

    Dealing with embedded documents.

    Hey there,

    I'm new to MongoDB and I would like to know how I can access an embedded document using flask-mongorest. I know how to do it in the cli, but I can't seem to find documentation here.

    Example

    Given an output of...

    
        "data": [
            {
                "adducts": {
                    "Anion": {
                        "[M-H]1-": [
                            [
                                349.2093240735, 
                                100.0
                            ], 
                            [
                                350.2126789113, 
                                21.631456585464488
                            ]
                        ]
                    }, 
                    "Canion": {}, 
                    "Nominal": [
                        [
                            350.2093240735, 
                            100.0
                        ], 
                        [
                            351.2126789113, 
                            21.631456585464488
                        ]
                    ]
                }, 
                "id": "586bf20b9f0029837dfc9d39", 
                "molecular_formula": "C20H30O5", 
                "name": "Oryzalic acid B", 
                "origins": [
                    "Endogenous", 
                    "Food"
                ]
            }...
    

    I'd like to filter out anything that has an "anion" from "adducts" from a given value compared to the first element the first list in that given key.

    Is this possible in flask-mongorest?

    Thanks,

    Keiron.

    opened by KeironO 24
  • Doesn't this violate REST principles?

    Doesn't this violate REST principles?

    Maybe I'm missing something, but I think there is a fundamental issue in MongoRest.

    I created a super simple example to demonstrate the issue here: https://gist.github.com/mtiller/4961630

    If I run this application and then create an author as follows:

    % curl -H "Content-Type: application/json" -X POST -d '{"name": "Douglas Adams"}' http://localhost:5000/authors/

    It creates an author, but the response looks like this:

    {"name": "Douglas Adams", "id": "511e66731d41c8718c196708"}

    The problem I see here is that this is not returning a URI. The "Uniform Interface" constraint for REST says that resources should be named. In this case, the resources name is, in fact, /authors/511e66731d41c8718c196708/ (which I figured out by trial and error). But a POST should return to the URI (resource), not the representation. If I had done this:

    % curl http://localhost:5000/authors/511e66731d41c8718c196708/

    THEN, I get:

    {"name": "Douglas Adams", "id": "511e66731d41c8718c196708"}

    ...which is correct since this is a JSON representation.

    But the problem goes deeper than just POST responses. If I then want to create a Book object I should be using the RESOURCE not the representation, e.g.

    curl -H "Content-Type: application/json" -X POST -d '{"title": "Hitchhikers Guide to the Galaxy", "author": "/authors/511e66731d41c8718c196708/"}' http://localhost:5000/books/

    However, this fails with:

    {"field-errors": {"name": "Field is required"}}

    It turns out what is required is this:

    % curl -H "Content-Type: application/json" -X POST -d '{"title": "Hitchhikers Guide to the Galaxy", "author": {"name": "Douglas Adams", "id": "511e66731d41c8718c196708"}}' http://localhost:5000/books/

    Note that I had to put the REPRESENTATION in for the author, not the resource.

    Am I missing something here? This seems like a significantly violation of REST principles. If I remove the 'related_resources' setting, it gets slightly better because then it requires this:

    % curl -H "Content-Type: application/json" -X POST -d '{"title": "Hitchhikers Guide to the Galaxy", "author": "511e66731d41c8718c196708"}' http://localhost:5000/books/

    ...and you get back...

    {"title": "Hitchhikers Guide to the Galaxy", "id": "511e6bb61d41c8723fba687c", "author": "511e66731d41c8718c196708"}

    So at least now we are giving and getting a resource identifier (although it isn't technically a URI). But it is still inconsistent with what is returned by the POST method used to create the author. In other words, as a developer using such an API I have to understand how to turn the representation (from POST) into a resource identifier which I should not have to do.

    Or am I missing something?

    opened by xogeny 24
  • Bulk update limit

    Bulk update limit

    Primarily to minimize the effects of a poorly constructed request.

    After this change, flask-mongorest will by default limit bulk updates to 1k documents. If more than that would be affected, a 400 response is returned.

    This PR also introduces a method which you can use to validate the request before processing of a bulk update starts.

    opened by wojcikstefan 11
  • How to use Flask-MongoRest resource filters?

    How to use Flask-MongoRest resource filters?

    Following up from https://github.com/closeio/flask-mongorest/issues/103, I am experiencing an issue involving the use of an EmbeddedDocument.

    My MongoDB collection currently resembles the following:

    [
        {
            "accurate_mass": 350.45000749099137, 
            "smiles": "CC(C)(C1CCC23CC(CCC2C1(C)CC(O)=O)C(=C)C3O)C(O)=O", 
            "isotopic_distributions": [
                [
                    0.0, 
                    100.0
                ], 
                [
                    1.003354837799975, 
                    21.631456585464488
                ]
            ], 
            "name": "Oryzalic acid B", 
            "origins": [
                "Endogenous", 
                "Food"
            ], 
            "molecular_formula": "C20H30O5", 
            "adduct_weights": {
                "positive": {
                    "count": 0, 
                    "peaks": []
                }, 
                "neutral": 350.2093240735, 
                "negative": {
                    "count": 1, 
                    "peaks": [
                        [
                            "[M-H]1-", 
                            349.2093240735
                        ]
                    ]
                }
            }
        },...
    ]
    

    If you look at the adduct_weights key, it holds a collection resembling:

    "adduct_weights": {
                "positive": {
                    "count": 0, 
                    "peaks": []
                }, 
                "neutral": 350.2093240735, 
                "negative": {
                    "count": 1, 
                    "peaks": [
                        [
                            "[M-H]1-", 
                            349.2093240735
                        ]
                    ]
                }
    

    Following the example provided within this repository, I have written the following Documents and Resources.

    class NegativeAdduct(db.EmbeddedDocument):
        count = db.IntField()
        peaks = db.ListField(db.ListField(db.DynamicField()))
    
    class PositiveAdduct(db.EmbeddedDocument):
        count = db.IntField()
        peaks = db.ListField(db.ListField(db.DynamicField()))
    
    class AdductWeights(db.EmbeddedDocument):
        neutral = db.FloatField()
        negative = db.EmbeddedDocumentField(NegativeAdduct)
        positive = db.EmbeddedDocumentField(PositiveAdduct)
    
    class AdductWeightsResource(Resource):
        document = AdductWeights
    
    class MetaboliteAdduct(db.DynamicDocument):
        meta = {"collection": "metabolites"}
        name = db.StringField()
        accurate_mass = db.FloatField()
        smiles = db.StringField()
        isotopic_distributions = db.StringField()
        molecular_formula = db.StringField()
        origins = db.ListField(db.StringField())
        adduct_weights = db.EmbeddedDocumentField(AdductWeights)
    
    class MetaboliteAdductResource(Resource):
        document = MetaboliteAdduct
        filters = {
            "name" : [ops.Contains, ops.Startswith, ops.Exact],
        }
    
        related_resources = {
            "adduct_weights" : AdductWeightsResource
        }
    
    @api.register(name="adductsapi", url="/api/adducts/")
    class MetaboliteAdductView(ResourceView):
        resource =  MetaboliteAdductResource
        methods = [methods.List, methods.Fetch]
    

    No error is being thrown when I query MetaboliteAdductView's url, however no data is being returned either.

    {
        "data": [], 
        "has_more": true
    }
    

    Where have I gone wrong here?

    opened by KeironO 9
  • Bulk update improvements:

    Bulk update improvements:

    • Introduces two helper methods (update_objects and update_object) that can be overridden in subclasses
    • Makes sure view_method is propagated to subresources
    • Fixes an issue where has_change_permission is called after the object is updated in bulk updates
    • Fixes an issue where obj.save() is unnecessarily called after update_object() (which already saves) in bulk updates
    opened by thomasst 7
  • Added supplemental validation capability

    Added supplemental validation capability

    These changes allow a developer to define additional custom validation criteria for a Resource by overriding the default custom_validation method on Resource and throwing a ValidationError.

    In addition to adding the feature, I also added test cases to exercise this validation in the context of either a PUT or POST request.

    opened by xogeny 7
  • `params` doesn't make sense if we don't have a request

    `params` doesn't make sense if we don't have a request

    If we don't have an active request context, evaluating params doesn't make sense and is analogous to the attribute not existing at all, so we raise an AttributeError to make hasattr return False.

    opened by jpmelos 6
  • Python 3 incompatible syntax on views.py

    Python 3 incompatible syntax on views.py

    There are a couple of except Exception, e: in views.py and that syntax is incompatible with python 3. A simple change to except Exception as e: should solve the issue.

    opened by marco-lavagnino 4
  • Form Validation of None Values

    Form Validation of None Values

    It's not the prettiest solution but without fixing mongoengine's email validation, this will have to do

    If you setup the following resource form:

    class SomeDocument(Document):
         email = EmailField(required=False)
         name = StringField()
    
    ResourceFormBase = model_form(SomeDocument, exclude=['email'])
    class ResourceForm(ResourceFormBase):
        email = fields.TextField(validators=[validators.optional(), validators.email()])
    

    A POST with the following json: {'name':'John Doe'} fails without these changes.

    Because form.data returns: {'name':'John Doe', 'email':''} instead of {'name':'John Doe'} which causes a validation error. There might be other consequences of this behavior.

    opened by lucasvo 4
  • Support for Python 3 / latest MongoEngine

    Support for Python 3 / latest MongoEngine

    This is tested on:

    • Python 2.7 and our own MongoEngine fork
    • Python 3.5 and upstream MongoEngine

    Note that I skipped two tests since upstream MongoEngine doesn't have certain features like SafeReferenceField.

    opened by thomasst 3
  • Don't return parent's child_document_resources in subclasses.

    Don't return parent's child_document_resources in subclasses.

    By default, don't inherit child_document_resources. This lets us have multiple resources for a child document without having to reset the child_document_resources property in the subclass.

    Consider the following example:

    class ParentResource(Resource):
        child_document_resources = { Child: 'ChildResource' }
    
    class ChildResource(ParentResource):
        document = Child
        fields = ['id']
    
    class VerboseChildResource(ChildResource):
        fields = ['id', 'foo', 'bar']
    

    If we call VerboseChildResource().serialize(obj), before this PR it would delegate the call to ChildResource since VerboseChildResource would inherit child_document_resources from ParentResource. This is usually not expected behavior, so I'm changing get_child_document_resources to only return the current class' child_document_resources. In the rare case where this isn't desirable, a parent can always explicitly change the behavior by overriding get_child_document_resources.

    opened by thomasst 3
  • Register class, BulkCreate/Delete, Flasgger, and other improvements

    Register class, BulkCreate/Delete, Flasgger, and other improvements

    This PR is a fresh start of #124. It includes:

    • A separate register_class function to use with Flask Blueprints (see e.g. here). This should supersede #85 and #115.
    • The register_class function also defines endpoints for each URL to enable integration with Flasgger (see this commit).
    • Implementation of BulkCreate and BulkDelete methods.
    • A typ property for operators to enable easier integration with Swagger definitions (see here).
    • Automatically trying to convert values to floats for numeric operators.
    • Static get_optional_fields method to allow retrieving optional fields from the class (see here)
    • Ensuring that order is maintained in get_requested_fields().
    • Forwarding the view_method when serializing.
    • Support for Decimal128 in MongoEncoder.
    • Improved error handling and kwargs forwarding.
    • Bugfix for #129.
    • mimerender dependency updated to include martinblech/mimerender#36

    @wojcikstefan @thomasst I'm working on getting the tests to pass and implement new ones for BulkCreate/Delete. Would you mind starting the review of this PR in the meantime? Thanks! I will base new PRs for additional functionality currently included in #124 off this PR.

    opened by tschaume 1
  • has_add_permission() cannot prevent object creation

    has_add_permission() cannot prevent object creation

    On views.py:162, object is created without save=True, so it's saved in database before has_add_permission is called, 5 lines below.

    I tried to create a PR with a fix, by first calling create_object(save=False), then self._resource.save_object(obj). But on tests/init.py:304 there's an explicit expectation that the unauthorized object have been saved.

    Is this really the expected behavior?

    opened by lfagundes 1
  • Saving Reference field along with object in POST request

    Saving Reference field along with object in POST request

    I would like to save the reference field along with object in post request. Is there any way to do this?

    For eg.

    class User(db.Document):
        name = db.StringField(required=True, unique=True)
    
    class Post(db.Document):
        # Some fields
        # ...
       author = db.ReferenceField(User)
    

    Now, I want to create author while making a POST request: /post/ => { "author": { "name": "Test" }, ..other fields }

    opened by anujism 0
  • Package requirements are broken in PyPI

    Package requirements are broken in PyPI

    Package requirements aren't recognized when installing from PyPI. Also, it could be helpful to move nose to something like

        extras_require={
            'test': ['nose'],
        },
    

    After that, it will be possible to setup test environment like that

    python setup.py develop
    pip install Flask-MongoRest[test]
    
    opened by lig 0
Releases(v0.3.0)
  • v0.3.0(Aug 26, 2019)

    The release:

    • Improves support for Python 3.
    • Bumps the dependency on pymongo.
    • Drops the dependency on Flask-Views. As a result, self.args and self.kwargs are no longer available in views.
    Source code(tar.gz)
    Source code(zip)
  • v0.2.3(Sep 27, 2017)

    This release splits Resource.serialize into separate field-type serialization methods, making it easier to add custom serialization logic for new field types.

    Source code(tar.gz)
    Source code(zip)
Owner
Close
The inside sales CRM of choice for SMBs. Join our eng team: http://jobs.close.com/
Close
Flask RestAPI Project - Transimage Rest API For Python

[ 이미지 변환 플라스크 Rest API ver01 ] 0. Flask Rest API - in SunnyWeb : 이미지 변환 웹의 Flask

OliverKim 1 Jan 12, 2022
Eazytraining - Simple application to show how to query API from webapp

student-list Eazytraining - Simple application to show how to query API from webapp This repo is a simple application to list student with a webserver

⚡Christophe FREIJANES 2 Nov 15, 2021
A light REST library for Django.

django-nap Read The Docs: https://django-nap.readthedocs.io/en/latest/ Change log: https://django-nap.readthedocs.io/en/latest/changelog.html An API l

Curtis Maloney 223 Dec 07, 2022
A JSON Web Token authentication plugin for the Django REST Framework.

Simple JWT Abstract Simple JWT is a JSON Web Token authentication plugin for the Django REST Framework. For full documentation, visit django-rest-fram

Jazzband 3.3k Jan 04, 2023
A RESTful whois

whois-rest A RESTful whois. Installation $ pip install poetry $ poetry install $ uvicorn app:app INFO: Started server process [64616] INFO: W

Manabu Niseki 4 Feb 19, 2022
Django-rest-auth provides a set of REST API endpoints for Authentication and Registration

This app makes it extremely easy to build Django powered SPA's (Single Page App) or Mobile apps exposing all registration and authentication related functionality as CBV's (Class Base View) and REST

Tivix 2.4k Dec 29, 2022
Build a Backend REST API with Python & Django

Build a Backend REST API with Python & Django Skills Python Django djangorestframework Aws Git Use the below Git commands in the Windows Command Promp

JeonSoohyun a.k.a Edoc.. 1 Jan 25, 2022
Extensions for Django REST Framework

Extensions for Django REST Framework

aiden 6 Dec 27, 2022
Document Web APIs made with Django Rest Framework

DRF Docs Document Web APIs made with Django Rest Framework. View Demo Contributors Wanted: Do you like this project? Using it? Let's make it better! S

Manos Konstantinidis 626 Nov 20, 2022
A Django-powered API with various utility apps / endpoints.

A Django-powered API Includes various utility apps / endpoints. Demos These web apps provide a frontend to the APIs in this project. Issue API Explore

Shemar Lindie 0 Sep 13, 2021
A Django api to display items and their current up-to-date prices from different online retailers in one platform.

A Django api to display items and their current up-to-date prices from different online retailers in one platform. Utilizing scrapy to periodically scrape the latest prices from different online reta

Kennedy Ngugi Mwaura 1 Nov 05, 2021
Authentication Module for django rest auth

django-rest-knox Authentication Module for django rest auth Knox provides easy to use authentication for Django REST Framework The aim is to allow for

James McMahon 873 Dec 30, 2022
Example Starlette REST API application

The idea of this project is to show how Starlette, Marshmallow, and SQLAlchemy can be combined to create a RESTful HTTP API application that is modular, lightweight, and capable of dealing with many

Robert Wikman 0 Jan 07, 2022
Key-Value база данных на Tarantool и REST API к ней.

KVmail Key-Value база данных на Tarantool и REST API к ней. Документация к API доступна здесь. Requiremrnts ubuntu 16.04+ python3.6+ supervisord nginx

1 Jun 16, 2021
Python bindings for Podman's RESTful API

podman-py This python package is a library of bindings to use the RESTful API of Podman. It is currently under development and contributors are welcom

Containers 142 Jan 06, 2023
REST API framework designed for human beings

Eve Eve is an open source Python REST API framework designed for human beings. It allows to effortlessly build and deploy highly customizable, fully f

eve 6.6k Jan 04, 2023
Simplified REST API to get stickers from Snap

Snap Sticker kit REST API Simplified REST API to get stickers from Snap 💻 Instructions Search stickers Request: url = "https://sticker-kit-horizon733

Dishant Gandhi 1 Jan 05, 2022
DSpace REST API Client Library

DSpace Python REST Client Library This client library allows Python 3 scripts (Python 2 probably compatible but not officially supported) to interact

The Library Code GmbH 10 Nov 21, 2022
Django queries

Djaq Djaq - pronounced “Jack” - provides an instant remote API to your Django models data with a powerful query language. No server-side code beyond t

Paul Wolf 53 Dec 12, 2022