Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions flask_potion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from flask import current_app, make_response, json, Response, request
from six import wraps
from werkzeug.exceptions import HTTPException
from werkzeug.wrappers import BaseResponse
from werkzeug.wrappers import Response
from .exceptions import PotionException
from .routes import RouteSet, to_camel_case
from .utils import unpack
Expand Down Expand Up @@ -156,7 +156,7 @@ def output(self, view):
def wrapper(*args, **kwargs):
resp = view(*args, **kwargs)

if isinstance(resp, BaseResponse):
if isinstance(resp, Response):
return resp

data, code, headers = unpack(resp)
Expand Down
6 changes: 3 additions & 3 deletions flask_potion/contrib/alchemy/manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from flask import current_app
from flask_sqlalchemy import Pagination as SAPagination, get_state
from flask_sqlalchemy.pagination import Pagination as SAPagination
from sqlalchemy import String, or_, and_
from sqlalchemy.dialects import postgresql
from sqlalchemy.exc import IntegrityError
Expand All @@ -9,7 +9,7 @@
from sqlalchemy.orm.exc import NoResultFound

from flask_potion import fields
from flask_potion.contrib.alchemy.filters import FILTER_NAMES, FILTERS_BY_TYPE, SQLAlchemyBaseFilter
from flask_potion.contrib.alchemy.filters import FILTER_NAMES, FILTERS_BY_TYPE
from flask_potion.exceptions import ItemNotFound, DuplicateKey, BackendConflict
from flask_potion.instances import Pagination
from flask_potion.manager import RelationalManager
Expand Down Expand Up @@ -144,7 +144,7 @@ def _is_sortable_field(self, field):

@staticmethod
def _get_session():
return get_state(current_app).db.session
return current_app.extensions['sqlalchemy'].session

@staticmethod
def _is_change(a, b):
Expand Down
1 change: 0 additions & 1 deletion flask_potion/contrib/principals/permission.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from flask import g
from flask_principal import Permission
from sqlalchemy import or_

from .needs import HybridNeed

Expand Down
1 change: 0 additions & 1 deletion flask_potion/reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import inspect

import six
from flask import current_app, _app_ctx_stack


class ResourceReference(object):
Expand Down
6 changes: 5 additions & 1 deletion flask_potion/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from types import MethodType

from flask import request
from sqlalchemy.orm.exc import StaleDataError
from werkzeug.utils import cached_property

from flask_potion.reference import _bind_schema
Expand Down Expand Up @@ -444,7 +445,10 @@ def relation_add(resource, item, target_item):
def relation_remove(resource, item, target_id):
target_item = self.target.manager.read(target_id)
resource.manager.relation_remove(item, self.attribute, self.target, target_item)
resource.manager.commit()
try:
resource.manager.commit()
except StaleDataError:
pass
return None, 204

yield relation_route.for_method('DELETE',
Expand Down
5 changes: 4 additions & 1 deletion flask_potion/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ def __init__(self, schema):
def schema(self):
return self._schema


class FieldSet(Schema, ResourceBound):
"""
A schema representation of a dictionary of :class:`fields.Raw` objects.
Expand Down Expand Up @@ -264,7 +265,9 @@ def parse_request(self, request):
if not self.all_fields_optional:
raise RequestMustBeJSON()

data = request.get_json(silent=False)
data = None
if request.method != "GET":
data = request.get_json(silent=False)

if data is None and self.all_fields_optional:
data = {}
Expand Down
27 changes: 8 additions & 19 deletions flask_potion/utils.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,18 @@
from flask import _app_ctx_stack, _request_ctx_stack
from urllib.parse import urlsplit

from flask import current_app, request
from werkzeug.exceptions import NotFound
from werkzeug.urls import url_parse


def route_from(url, method=None):
# Source: http://stackoverflow.com/questions/19129407/looking-for-inverse-of-url-for-in-flask
appctx = _app_ctx_stack.top
reqctx = _request_ctx_stack.top
if appctx is None:
raise RuntimeError('Attempted to match a URL without the '
'application context being pushed. This has to be '
'executed when application context is available.')
environ = dict(request.environ)
url_request = current_app.request_class(environ)
url_adapter = current_app.create_url_adapter(url_request)
parsed_url = urlsplit(url)

if reqctx is not None:
url_adapter = reqctx.url_adapter
else:
url_adapter = appctx.url_adapter
if url_adapter is None:
raise RuntimeError('Application was not able to create a URL '
'adapter for request independent URL matching. '
'You might be able to fix this by setting '
'the SERVER_NAME config variable.')
parsed_url = url_parse(url)
if parsed_url.netloc is not "" and parsed_url.netloc != url_adapter.server_name:
raise NotFound()

return url_adapter.match(parsed_url.path, method)


Expand Down
24 changes: 12 additions & 12 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
tests_require = [
'Flask-Testing>=0.4.1',
'Flask-Principal>=0.4.0',
'Flask-SQLAlchemy>=2.0',
'Flask-MongoEngine>=0.7.1',
'peewee==2.*',
'nose>=1.1.2',
'Flask-SQLAlchemy>=3.0',
# 'Flask-MongoEngine>=0.7.1',
# 'peewee==2.*',
# 'nose>=1.1.2',
]

setup(
Expand All @@ -26,7 +26,7 @@
test_suite='nose.collector',
tests_require=tests_require,
install_requires=[
'Flask>=0.10',
'Flask>=2.3.0',
'jsonschema>=2.4.0',
'aniso8601>=0.84',
'blinker>=1.3',
Expand All @@ -51,19 +51,19 @@
],
zip_safe=False,
extras_require={
'docs': ['sphinx', 'Flask-Principal', 'Flask-SQLAlchemy', 'peewee'],
'docs': ['sphinx', 'Flask-Principal', 'Flask-SQLAlchemy'],
'principal': [
'Flask-Principal',
],
'sqlalchemy': [
'Flask-SQLAlchemy>=2.0'
],
'peewee': [
'peewee==2.*'
],
'mongoengine': [
'Flask-MongoEngine>=0.7.0'
],
# 'peewee': [
# 'peewee==2.*'
# ],
# 'mongoengine': [
# 'Flask-MongoEngine>=0.7.0'
# ],
'tests': tests_require,
}
)
1 change: 0 additions & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ def open(self, *args, **kw):


class BaseTestCase(TestCase):

def assertJSONEqual(self, first, second, msg=None):
self.assertEqual(json.loads(json.dumps(first)), json.loads(json.dumps(second)), msg)

Expand Down
2 changes: 1 addition & 1 deletion tests/contrib/alchemy/test_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def setUp(self):
super(FilterTestCase, self).setUp()
app = self.app
app.config['SQLALCHEMY_ENGINE'] = 'sqlite://'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
app.config['TESTING'] = True

self.api = Api(self.app)
Expand Down Expand Up @@ -44,7 +45,6 @@ class Thing(sa.Model):

sa.create_all()


class UserResource(ModelResource):
class Schema:
gender = fields.String(enum=['f', 'm'], nullable=True)
Expand Down
12 changes: 7 additions & 5 deletions tests/contrib/alchemy/test_manager_sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class SQLAlchemyTestCase(BaseTestCase):
def setUp(self):
super(SQLAlchemyTestCase, self).setUp()
self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://'
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
self.api = Api(self.app, default_manager=SQLAlchemyManager)
self.sa = sa = SQLAlchemy(self.app, session_options={"autoflush": False})

Expand Down Expand Up @@ -64,10 +65,6 @@ def test_field_discovery(self):
self.assertEqual(self.MachineResource.meta.name, 'machine')
self.assertEqual(self.TypeResource.meta.name, 'type')

def test_create_no_json(self):
response = self.client.post('/machine', data='invalid')
self.assert400(response)

def test_create_json_string(self):
response = self.client.post('/machine', data='invalid', force_json=True)
self.assert400(response)
Expand Down Expand Up @@ -193,7 +190,7 @@ def test_update(self):
"additionalProperties": False,
"properties": {
"$ref": {
"pattern": "^\\/type\\/[^/]+$",
"pattern": "^/type\\/[^/]+$",
"type": "string"
}
},
Expand Down Expand Up @@ -233,6 +230,7 @@ class SQLAlchemyRelationTestCase(BaseTestCase):
def setUp(self):
super(SQLAlchemyRelationTestCase, self).setUp()
self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://'
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
self.api = Api(self.app)
self.sa = sa = SQLAlchemy(self.app, session_options={"autoflush": False})

Expand Down Expand Up @@ -375,6 +373,7 @@ class SQLAlchemyInspectionTestCase(BaseTestCase):
def setUp(self):
super(SQLAlchemyInspectionTestCase, self).setUp()
self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://'
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
self.api = Api(self.app)
self.sa = sa = SQLAlchemy(self.app, session_options={"autoflush": False})

Expand Down Expand Up @@ -417,6 +416,7 @@ class SQLAlchemySequenceTestCase(BaseTestCase):
def setUp(self):
super(SQLAlchemySequenceTestCase, self).setUp()
self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://'
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
self.api = Api(self.app)
self.sa = sa = SQLAlchemy(self.app, session_options={"autoflush": False})

Expand Down Expand Up @@ -455,6 +455,7 @@ class SQLAlchemySortTestCase(BaseTestCase):
def setUp(self):
super(SQLAlchemySortTestCase, self).setUp()
self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://'
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
self.api = Api(self.app)
self.sa = sa = SQLAlchemy(
self.app, session_options={"autoflush": False})
Expand Down Expand Up @@ -529,6 +530,7 @@ class QueryOptionsSQLAlchemyTestCase(BaseTestCase):
def setUp(self):
super(QueryOptionsSQLAlchemyTestCase, self).setUp()
self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://'
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
self.api = Api(self.app, default_manager=SQLAlchemyManager)
self.sa = sa = SQLAlchemy(self.app, session_options={"autoflush": False})

Expand Down
3 changes: 2 additions & 1 deletion tests/contrib/alchemy/test_signals.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from functools import partial
from contextlib import contextmanager
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import backref
from flask_potion import fields
Expand All @@ -7,14 +8,14 @@
from flask_potion.resource import ModelResource
from flask_potion import Api
from tests import BaseTestCase
from blinker._utilities import contextmanager
from blinker import ANY


class SQLAlchemySignalTestCase(BaseTestCase):
def setUp(self):
super(SQLAlchemySignalTestCase, self).setUp()
self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://'
self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
self.api = Api(self.app)
self.sa = sa = SQLAlchemy(self.app)

Expand Down
23 changes: 0 additions & 23 deletions tests/contrib/mongoengine/__init__.py

This file was deleted.

Loading