Skip to content

Commit

Permalink
Merge 6fdf633 into b9fedef
Browse files Browse the repository at this point in the history
  • Loading branch information
jfinkels committed Mar 17, 2016
2 parents b9fedef + 6fdf633 commit 693c891
Show file tree
Hide file tree
Showing 14 changed files with 600 additions and 326 deletions.
15 changes: 14 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,22 @@ python:
- "pypy"
- "pypy3"

addons:
# HACK Need to update the installed version of PostgreSQL, because it doesn't
# implement all of the network operators (specifically the &&
# operator). Travis claims that version 9.4 is installed by default, but it
# claims that && is unknown unless this addon is here.
postgresql: "9.4"


before_install:
# Determine whether we're using PyPy, as it determines which requirements
# file we will use.
- if (python --version 2>&1 | grep PyPy > /dev/null); then export REQUIREMENTS=requirements-test-pypy.txt; else export REQUIREMENTS=requirements-test-cpython.txt; fi

install:
- pip install --upgrade pip
- pip install -r requirements.txt
- pip install -r $REQUIREMENTS
- pip install coveralls

script:
Expand Down
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Version 1.0.0-dev

Not yet released.

- #255: adds support for filtering by PostgreSQL network operators.
- #257: ensures additional attributes specified by the user actually exist on
the model.
- #363 (partial solution): don't use ``COUNT`` on requests that don't require
Expand Down
9 changes: 6 additions & 3 deletions docs/fetching.rst
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,8 @@ returned, clients can make requests like this:
Operators
.........

The operators recognized by the API incude:
Flask-Restless understands the following operators, which correspond to the
appropriate `SQLAlchemy column operators`_.

* ``==``, ``eq``, ``equals``, ``equals_to``
* ``!=``, ``neq``, ``does_not_equal``, ``not_equal_to``
Expand All @@ -949,7 +950,8 @@ The operators recognized by the API incude:
* ``has``
* ``any``

These correspond to the appropriate `SQLAlchemy column operators`_.
Flask-Restless also understands the `PostgreSQL network address operators`_
``<<``, ``<<=``, ``>>``, ``>>=``, ``<>``, and ``&&``.

.. warning::

Expand All @@ -960,7 +962,8 @@ These correspond to the appropriate `SQLAlchemy column operators`_.

.. _percent-encoded: https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_the_percent_character

.. _SQLAlchemy column operators: http://docs.sqlalchemy.org/en/latest/core/expression_api.html#sqlalchemy.sql.operators.ColumnOperators
.. _SQLAlchemy column operators: https://docs.sqlalchemy.org/en/latest/core/expression_api.html#sqlalchemy.sql.operators.ColumnOperators
.. _PostgreSQL network address operators: https://www.postgresql.org/docs/current/static/functions-net.html

.. _single:

Expand Down
6 changes: 6 additions & 0 deletions flask_restless/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ def _sub_operator(model, argument, fieldname):
'le': lambda f, a: f <= a,
'lte': lambda f, a: f <= a,
'leq': lambda f, a: f <= a,
'<<': lambda f, a: f.op('<<')(a),
'<<=': lambda f, a: f.op('<<=')(a),
'>>': lambda f, a: f.op('>>')(a),
'>>=': lambda f, a: f.op('>>=')(a),
'<>': lambda f, a: f.op('<>')(a),
'&&': lambda f, a: f.op('&&')(a),
'ilike': lambda f, a: f.ilike(a),
'like': lambda f, a: f.like(a),
'not_like': lambda f, a: ~f.like(a),
Expand Down
2 changes: 2 additions & 0 deletions requirements-test-cpython.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-r requirements-test.txt
psycopg2
2 changes: 2 additions & 0 deletions requirements-test-pypy.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-r requirements-test.txt
psycopg2cffi
3 changes: 3 additions & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
-r requirements.txt
nose
savalidation

# For testing PostgreSQL specific operations...
testing.postgresql
93 changes: 77 additions & 16 deletions tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from flask import json
try:
from flask.ext import sqlalchemy as flask_sqlalchemy
from flask.ext.sqlalchemy import SQLAlchemy
except ImportError:
has_flask_sqlalchemy = False
else:
Expand Down Expand Up @@ -283,9 +284,6 @@ def setup(self):
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['TESTING'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
# This is to avoid a warning in earlier versions of Flask-SQLAlchemy.
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# This is required by `manager.url_for()` in order to construct
# absolute URLs.
app.config['SERVER_NAME'] = 'localhost'
Expand All @@ -298,12 +296,69 @@ def setup(self):
force_content_type_jsonapi(self.app)


class DatabaseTestBase(FlaskTestBase):
class DatabaseMixin(object):
"""A class that accesses a database via a connection URI.
Subclasses can override the :meth:`database_uri` method to return a
connection URI for the desired database backend.
"""

def database_uri(self):
"""The database connection URI to use for the SQLAlchemy engine.
By default, this returns the URI for the SQLite in-memory
database. Subclasses that wish to use a different SQL backend
should override this method so that it returns the desired URI
string.
"""
return 'sqlite://'


class FlaskSQLAlchemyTestBase(FlaskTestBase, DatabaseMixin):
"""Base class for tests that use Flask-SQLAlchemy (instead of plain
old SQLAlchemy).
If Flask-SQLAlchemy is not installed, the :meth:`.setup` method will
raise :exc:`nose.SkipTest`, so that each test method will be
skipped individually.
"""

def setup(self):
super(FlaskSQLAlchemyTestBase, self).setup()
if not has_flask_sqlalchemy:
raise SkipTest('Flask-SQLAlchemy not found.')
self.flaskapp.config['SQLALCHEMY_DATABASE_URI'] = self.database_uri()
# This is to avoid a warning in earlier versions of
# Flask-SQLAlchemy.
self.flaskapp.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Store some attributes for convenience and so the test methods
# read more like the tests for plain old SQLAlchemy.
self.db = SQLAlchemy(self.flaskapp)
self.session = self.db.session

def teardown(self):
"""Drops all tables and unregisters Flask-SQLAlchemy session
signals.
"""
self.db.drop_all()
unregister_fsa_session_signals()


class SQLAlchemyTestBase(FlaskTestBase, DatabaseMixin):
"""Base class for tests that use a SQLAlchemy database.
The :meth:`setup` method does the necessary SQLAlchemy initialization, and
the subclasses should populate the database with models and then create the
database (by calling ``self.Base.metadata.create_all()``).
The :meth:`setup` method does the necessary SQLAlchemy
initialization, and the subclasses should populate the database with
models and then create the database (by calling
``self.Base.metadata.create_all()``).
By default, this class creates a SQLite database; subclasses can
override the :meth:`.database_uri` method to enable configuration of
an alternate database backend.
"""

Expand All @@ -312,11 +367,8 @@ def setup(self):
database.
"""
super(DatabaseTestBase, self).setup()
# initialize SQLAlchemy
app = self.flaskapp
engine = create_engine(app.config['SQLALCHEMY_DATABASE_URI'],
convert_unicode=True)
super(SQLAlchemyTestBase, self).setup()
engine = create_engine(self.database_uri(), convert_unicode=True)
self.Session = sessionmaker(autocommit=False, autoflush=False,
bind=engine)
self.session = scoped_session(self.Session)
Expand All @@ -329,16 +381,25 @@ def teardown(self):
self.Base.metadata.drop_all()


class ManagerTestBase(DatabaseTestBase):
class ManagerTestBase(SQLAlchemyTestBase):
"""Base class for tests that use a SQLAlchemy database and an
:class:`flask_restless.APIManager`.
:class:`~flask.ext.restless.APIManager`.
Nearly all test classes should subclass this class. Since we strive
to make Flask-Restless compliant with plain old SQLAlchemy first,
the default database abstraction layer used by tests in this class
will be SQLAlchemy. Test classes requiring Flask-SQLAlchemy must
instantiate their own :class:`~flask.ext.restless.APIManager`.
The :class:`flask_restless.APIManager` is accessible at ``self.manager``.
The :class:`~flask.ext.restless.APIManager` instance for use in
tests is accessible at ``self.manager``.
"""

def setup(self):
"""Initializes an instance of :class:`flask.ext.restless.APIManager`.
"""Initializes an instance of
:class:`~flask.ext.restless.APIManager` with a SQLAlchemy
session.
"""
super(ManagerTestBase, self).setup()
Expand Down
Loading

0 comments on commit 693c891

Please sign in to comment.