Skip to content

Commit

Permalink
Merge pull request #47 from Yupeek/develop
Browse files Browse the repository at this point in the history
release 1.8.1
  • Loading branch information
darius BERNARD authored Jan 10, 2019
2 parents 7778404 + bc632de commit 7a76adf
Show file tree
Hide file tree
Showing 16 changed files with 147 additions and 84 deletions.
7 changes: 4 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ jobs:
- stage: deploy
script: skip # to not run Travis' default script
install: skip
on:
repo: Yupeek/django-rest-models
tags: true
deploy:
provider: pypi
user: yupeek
on:
repo: Yupeek/django-rest-models
tags: true

password:
secure: e24svC/SHS9PcbgTqv40QXsiziygb9KgYqz6pjwI5eIM05s5a9kuF7OadV+Kd2Gq/XlxpPn9wKomYUoBLwg1eZs4kpAo8qDMZp51xi9hR8gYHmmOzvmV2NTfSdeEKEGHNbSNid3GUOZiKm3IDw3ym7983kkLLVsdRlty8b/X8kqPH+m+M+1lXl7d5l4rH3+rov8udasBGX4h3x8qdI1YKZfiL33gW632Fr5rrkC3ISTX5ClI8SgDZHdc3uG8C6u9TLTwjcxrAtTvIBHIg8BX4i8HR0o8LdyfHARqZbk6g2N6sK5B1kIgXumiY2gA9g5cnz/TEC+ZJkn2AueEHJkpY5gyJnmiC67ro3v0h3Gkl3v4H7k3GhbmZiMjA3Vzt6Ea/hBCNk7YfBo22j8h65F8KuS24OiQjxPO+UFI6bzYnAvP6HQdiM9M3Wai7Szv+GYRLRqZt5psxEtg9pvqZLLmr6LW+kvFjYG1bYVGNZG2Lr0VLQ99PVfV+LwV74XpWAUBLFsGuTAEdIuWUln0zeXwLRh/FQXhthVc1/A9uEviBVT9yaiYG8KvRd43b5NGlaXsPjH4bkj3xVpEBE5OhO64C33sNBABI0xbaFzLQgbrH4FP1MPhzxxK4p5GiXa1w+WfPtF/gFYfBGjP7NtDkKhFAS/UZ/WoPgRWQXkBtqWBnw8=

Expand Down
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ dynamic-rest filtering system too.
Pizza.objects.exclude(..., ...).exclude(...)
Pizza.objects.filter(Q(..) & Q(..))
Pizza.objects.none()
pizza.toppings.add(...)
pizza.toppings.remove(...)
pizza.toppings.set(...)
pizza.toppings.clear(...)
.. note::

Expand Down
Binary file added assets/okami.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion rest_models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__VERSION__ = '1.8.0'
__VERSION__ = '1.8.1'

try:
from rest_models.checks import register_checks
Expand Down
16 changes: 11 additions & 5 deletions rest_models/backend/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,7 @@ def execute_sql(self, return_id=False, chunk_size=None):
if response_files.status_code != 201:
raise FakeDatabaseDbAPI2.ProgrammingError(
"error while creating (uploading files and data) %s with data=%s ; files=%s.\n%s" % (
obj, obj_data, files, message_from_response(response)))
obj, obj_data, files, message_from_response(response_files)))
# update with json formated
new_id = response_files.json()[get_resource_name(query.model, many=False)]['id']
response = self.connection.cursor().patch(
Expand All @@ -1184,9 +1184,12 @@ def execute_sql(self, return_id=False, chunk_size=None):
new = result_json[get_resource_name(query.model, many=False)]

for field in opts.concrete_fields:
raw_val = new[field.concrete and field.db_column or field.name]
try:
raw_val = new[field.concrete and field.db_column or field.name]
except KeyError:
continue
if isinstance(field, FileField) and hasattr(field.storage, 'prepare_result_from_api'):
python_val = field.storage.prepare_result_from_api(raw_val, self.connection)
python_val = field.storage.prepare_result_from_api(raw_val, self.connection.cursor())
elif hasattr(field, "to_python"):
python_val = field.to_python(raw_val)
else:
Expand Down Expand Up @@ -1328,9 +1331,12 @@ def execute_sql(self, result_type=MULTI, chunk_size=None):
instance_data = {}
obj = None
for field, _, val in self.query.values:
raw_val = result_json[field.concrete and field.db_column or field.name]
try:
raw_val = result_json[field.concrete and field.db_column or field.name]
except KeyError:
continue
if isinstance(field, FileField) and hasattr(field.storage, 'prepare_result_from_api'):
python_val = field.storage.prepare_result_from_api(raw_val, self.connection)
python_val = field.storage.prepare_result_from_api(raw_val, self.connection.cursor())
obj = val.instance
elif hasattr(field, "to_python"):
python_val = field.to_python(raw_val)
Expand Down
19 changes: 13 additions & 6 deletions rest_models/backend/connexion.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ def prepared_request_to_wsgi_request(self, prepared_request):
def http_response_to_response(self, http_response, prepared_request):
"""
transform a WSGIResponse into a requests's Response model
:param django.http.response.HttpResponse http_response: the http response send by django view
:param django.http.response.HttpResponse|django.http.response.StreamingHttpResponse http_response:
the http response send by django view
:return: the requests's Response model corresponding to the http_response
:rtype: Response
"""
Expand All @@ -89,7 +90,8 @@ def http_response_to_response(self, http_response, prepared_request):
try:
response._content = http_response.content
except AttributeError:
response._streaming_content = http_response.streaming_content
response._content = http_response.getvalue()
response.raw.read = lambda *args: http_response.getvalue()
req = prepared_request

if isinstance(req.url, bytes): # pragma: no cover
Expand Down Expand Up @@ -217,19 +219,24 @@ def __exit__(self, *args):
def request(self, method, url, **kwargs):

start = time.time()
response = None
try:
return self.connection.request(method, url, **kwargs)
response = self.connection.request(method, url, **kwargs)
finally:
stop = time.time()
duration = stop - start
sql = build_url(url, kwargs['params'])
elapsed_sec = response.elapsed.total_seconds() if response else 0.
self.db.queries_log.append({
'sql': "%s %s ||| %s" % (method, sql, kwargs),
'time': "%.3f" % duration,
'time': "%.3f " % elapsed_sec
})
logger.debug('(%.3f) %s %s; args=%s' % (duration, method, sql, kwargs),
extra={'duration': duration, 'sql': sql, 'params': kwargs, 'method': method}
logger.debug('(%.3f) (backend:%.3f) %s %s; args=%s' % (duration, elapsed_sec,
method, sql, kwargs),
extra={'duration': duration, 'backend': elapsed_sec,
'sql': sql, 'params': kwargs, 'method': method}
)
return response


class ApiConnexion(ApiVerbShortcutMixin):
Expand Down
2 changes: 2 additions & 0 deletions rest_models/backend/middlewares.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import json
import logging
import datetime

logger = logging.getLogger(__name__)

Expand All @@ -11,6 +12,7 @@ class FakeApiResponse(object):
def __init__(self, data, status_code):
self.data = data
self.status_code = status_code
self.elapsed = datetime.timedelta(seconds=1)

def json(self):
return self.data
Expand Down
5 changes: 4 additions & 1 deletion rest_models/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ def allow_migrate(self, db, app_label, model_name=None, **hints):
if model_name is None:
return None # we are model specific
# all our models must not being created in other databases
model = apps.get_model(app_label, model_name)
try:
model = apps.get_model(app_label, model_name)
except LookupError:
return None
if self.is_api_model(model):
return False
return None
7 changes: 4 additions & 3 deletions rest_models/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import os
import threading

from django.core.files import File
from django.core.files.base import ContentFile
from django.core.files.storage import Storage
from django.utils.deconstruct import deconstructible

Expand Down Expand Up @@ -73,8 +73,9 @@ def _open(self, name, mode='rb'):
if 'r' not in mode:
NotImplementedError("This backend doesn't support writing on file.")
cursor = self.get_cursor(name) # fetch a valid cursor which just got the name
response = cursor.session.get(self.url(name), stream=True)
return File(response.raw.file_to_stream, name)
response = cursor.session.get(self.url(name))
response.raise_for_status()
return ContentFile(response.content, name)

def _save(self, name, content):
# self.uploaded_file_pool[id] = content
Expand Down
5 changes: 4 additions & 1 deletion rest_models/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ def process_request(self, params, requestid, connection):
# no mocked data have matched
return self.not_found(url,
self,
extra='%s fixture for this url, but filter did not match' % len(results_for_url)
extra='%s fixture for this url, but filter did not match. got %s' % (
len(results_for_url),
params
)
)

data = result_found.get('data')
Expand Down
6 changes: 4 additions & 2 deletions rest_models/tests/test_middlewares.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,10 @@ def test_not_found_abs(self):
def test_found_bad_filters(self):
with self.assertRaises(Exception) as e:
self.cnx.get('c')
self.assertEqual(e.exception.args, ("the query 'c' was not provided as mocked data: "
"1 fixture for this url, but filter did not match",))
self.assertTrue(e.exception.args[0].startswith("the query 'c' was not provided as mocked data: "
"1 fixture for this url, but filter did not match. "
"got "
))

def test_ok(self):
self.cnx.post('c', data={})
Expand Down
11 changes: 7 additions & 4 deletions rest_models/tests/test_restmodeltestcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
from django.db import connections

from rest_models.test import RestModelTestCase
from rest_models.utils import Path
from testapp.models import Menu


class TestLoadFixtureTest(RestModelTestCase):
rest_fixtures = {
'c': str(os.path.join(os.path.dirname(__file__), 'rest_fixtures', 'data_test_fixtures.json')),
'c': Path(os.path.join(os.path.dirname(__file__), 'rest_fixtures', 'data_test_fixtures.json')),
"a": [],
"b": [{
"status_code": 503,
Expand All @@ -36,12 +37,14 @@ def setUp(self):
def test_fixtures_loaded(self):
self.assertEqual(self.client.get('c').json(), {'C': 'c'})
self.assertEqual(self.client.get('d').json(), {'A': 'a', 'B': 'b'})
r = self.client.get('b')
self.assertEqual(r.status_code, 503)

def test_fixtures_loaded_missing(self):
self.assertRaisesMessage(Exception,
"the query 'a' was not provided as mocked data: "
"0 fixture for this url, but filter did not match",
"urls was %r" % (['b', 'c', 'd'], ),
self.client.get, 'a')
r = self.client.get('b')
self.assertEqual(r.status_code, 503)

def test_variable_fixtures(self):
self.rest_fixtures_variables['user'] = '123'
Expand Down
44 changes: 22 additions & 22 deletions rest_models/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,45 @@
import rest_models.utils
from rest_models.backend.connexion import ApiConnexion
from rest_models.test import MockDataApiMiddleware, PrintQueryMiddleware
from rest_models.utils import JsonFixtures
from rest_models.utils import JsonFixtures, Path


def load_tests(loader, tests, ignore):
tests.addTests(doctest.DocTestSuite(rest_models.utils))
return tests


PARTIAL_DATA_FIXTURES = str(os.path.join(os.path.dirname(__file__), 'rest_fixtures', 'data_test_fixtures.json'))
FULL_TEST_FIXTURES = str(os.path.join(os.path.dirname(__file__), 'rest_fixtures', 'full_test_fixtures.json'))
PARTIAL_DATA_FIXTURES = Path(os.path.join(os.path.dirname(__file__), 'rest_fixtures', 'data_test_fixtures.json'))
FULL_TEST_FIXTURES = Path(os.path.join(os.path.dirname(__file__), 'rest_fixtures', 'full_test_fixtures.json'))


class TestLoadFixtures(TestCase):
def test_fixtures_file_full_path(self):
a = JsonFixtures(FULL_TEST_FIXTURES)
self.assertEqual(a._load(), {
'a': None,
'b': 503,
'd': {'A': 'a', 'B': 'b'}
'a': [None],
'b': [503],
'd': [{'A': 'a', 'B': 'b'}],
})

def test_fixtures_file_full_str(self):
a = JsonFixtures(str(FULL_TEST_FIXTURES))
a = JsonFixtures(Path(FULL_TEST_FIXTURES))
self.assertEqual(a._load(), {
'a': None,
'b': 503,
'd': {'A': 'a', 'B': 'b'}
'a': [None],
'b': [503],
'd': [{'A': 'a', 'B': 'b'}]
})

def test_raw_data(self):
a = JsonFixtures(**{
'a': None,
'b': 503,
'b': (503,),
'd': {'A': 'a', 'B': 'b'}
})
self.assertEqual(a._load(), {
'a': None,
'b': 503,
'd': {'A': 'a', 'B': 'b'}
'a': [None],
'b': [503],
'd': [{'A': 'a', 'B': 'b'}]
})

def test_fixtures_file_partial_path(self):
Expand All @@ -60,18 +60,18 @@ def test_fixtures_file_partial_path(self):

def test_fixtures_file_partial_str(self):
a = JsonFixtures(
c=str(PARTIAL_DATA_FIXTURES)
c=Path(PARTIAL_DATA_FIXTURES)
)
self.assertEqual(a._load(), {'c': [{'data': {'C': 'c'}}, {}, {}]})

def test_recursive(self):
b = JsonFixtures(FULL_TEST_FIXTURES)
a = JsonFixtures(b, c=[1, 2, 3])
self.assertEqual(a._load(), {
'a': None,
'b': 503,
'a': [None],
'b': [503],
'c': [1, 2, 3],
'd': {'A': 'a', 'B': 'b'}
'd': [{'A': 'a', 'B': 'b'}]
})

def test_args(self):
Expand All @@ -85,10 +85,10 @@ def test_args(self):
}
)
self.assertEqual(a._load(), {
'a': None,
'b': 502,
'a': [None],
'b': [503, 502],
'c': [{'data': {'C': 'c'}}, {}, {}],
'd': {'A': 'a', 'B': 'b'}
'd': [{'A': 'a', 'B': 'b'}]
})

def test_type_error(self):
Expand All @@ -104,7 +104,7 @@ def test_variables_fixtures(self):
with self.assertRaises(KeyError):
a["/me/1234/"]
v['userid'] = 1234
self.assertIs(a["/me/1234/"], o)
self.assertEqual(a["/me/1234/"], [o])

def test_load_bad_fixtures(self):
_, path = tempfile.mkstemp(".json", text=True)
Expand Down
Loading

0 comments on commit 7a76adf

Please sign in to comment.