Skip to content

Commit

Permalink
Merge pull request #124 from django-oscar/feature/django-20
Browse files Browse the repository at this point in the history
Django 2.0 / Oscar 1.6 support
  • Loading branch information
specialunderwear committed May 29, 2018
2 parents 1e12f8e + 3862c19 commit 0993ad7
Show file tree
Hide file tree
Showing 30 changed files with 125 additions and 104 deletions.
23 changes: 11 additions & 12 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,28 @@
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
sudo: false

# Cache pip downloads
cache:
directories:
- $HOME/.pip-cache/

language: python

python:
- '2.7.14'
- '3.5'
- '3.6'
- 2.7
- 3.5
- 3.6

env:
- DJANGO=Django==1.8.19 OSCAR=django-oscar==1.5.3
- DJANGO=Django==1.11.12 OSCAR=django-oscar==1.5.3
- DJANGO=Django==1.11.13 OSCAR=django-oscar==1.5.4
- DJANGO=Django==2.0.5 OSCAR=django-oscar==1.6

matrix:
exclude:
- python: 2.7
env: DJANGO=Django==2.0.5 OSCAR=django-oscar==1.6

before_install:
- pip install codecov

install:
- pip install $OSCAR
# django rest framework 3.7+ and django-tables2 1.17+ are not compatible with django 1.8
- if [ ${DJANGO} == "Django==1.8.18" ]; then pip install "djangorestframework<3.7"; pip install "django-tables2<1.17.0"; fi
- if [ ${DJANGO} == "Django==2.0.5" ]; then pip install "djangorestframework>=3.7"; fi
- make install

script:
Expand Down
7 changes: 3 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@ clean:
find . -name '*.egg-info' -delete

install:
pip install -e .[dev]
pip install django-oscar-api[docs]
pip install -e .[dev,docs]

sandbox: install
python sandbox/manage.py migrate
python sandbox/manage.py loaddata product productcategory productattribute productclass productattributevalue category attributeoptiongroup attributeoption stockrecord partner voucher country

test:
python sandbox/manage.py test oscarapi
python sandbox/manage.py test oscarapi --settings=sandbox.settings.nomigrations

coverage:
coverage run sandbox/manage.py test oscarapi
coverage run sandbox/manage.py test oscarapi --settings=sandbox.settings.nomigrations
coverage report -m
coverage xml -i

Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ steps:

urlpatterns = (
# all the things you already have
url(r'^api/', include(api.urls)),
url(r'^api/', api.urls),
)

See the Documentation_ for more information and the Changelog_ for release notes.
Expand Down
5 changes: 3 additions & 2 deletions demosite/mycustomapi/app.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from django.conf.urls import url

from mycustomapi import views

from oscarapi.app import RESTApiApplication

from . import views


class MyRESTApiApplication(RESTApiApplication):

Expand All @@ -14,4 +14,5 @@ def get_urls(self):

return urls + super(MyRESTApiApplication, self).get_urls()


application = MyRESTApiApplication()
19 changes: 9 additions & 10 deletions demosite/mycustomapi/serializers.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
from rest_framework import serializers

from oscar.core.loading import get_class

from oscarapi.serializers.checkout import PriceSerializer
from oscarapi.serializers.product import (
ProductImageSerializer, ProductLinkSerializer
)
from rest_framework import serializers

from oscarapi.serializers import checkout, product


Selector = get_class('partner.strategy', 'Selector')


class MyProductLinkSerializer(ProductLinkSerializer):
images = ProductImageSerializer(many=True, required=False)
class MyProductLinkSerializer(product.ProductLinkSerializer):
images = product.ProductImageSerializer(many=True, required=False)
price = serializers.SerializerMethodField()

class Meta(ProductLinkSerializer.Meta):
class Meta(product.ProductLinkSerializer.Meta):
fields = ('url', 'id', 'title', 'images', 'price')

def get_price(self, obj):
request = self.context.get("request")
strategy = Selector().strategy(
request=request, user=request.user)
ser = PriceSerializer(

ser = checkout.PriceSerializer(
strategy.fetch_for_product(obj).price,
context={'request': request})

return ser.data
9 changes: 3 additions & 6 deletions demosite/mycustomapi/settings.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import os

from oscar import get_core_apps

from oscar import OSCAR_MAIN_TEMPLATE_DIR
from oscar import get_core_apps

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Expand Down Expand Up @@ -37,13 +36,12 @@
'widget_tweaks',
] + get_core_apps()

MIDDLEWARE_CLASSES = [
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'oscar.apps.basket.middleware.BasketMiddleware',
Expand All @@ -65,13 +63,12 @@
'oscar.apps.search.context_processors.search_form',
'oscar.apps.promotions.context_processors.promotions',
'oscar.apps.checkout.context_processors.checkout',
'oscar.apps.customer.notifications.context_processors.notifications', # noqa
'oscar.apps.customer.notifications.context_processors.notifications',
'oscar.core.context_processors.metadata',
],
'loaders': [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
'django.template.loaders.eggs.Loader',
],
},
},
Expand Down
10 changes: 5 additions & 5 deletions demosite/mycustomapi/urls.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from django.conf.urls import include, url
from django.conf.urls import url
from django.contrib import admin

from mycustomapi.app import application as api
from oscar.app import application as oscar

from .app import application as api

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include(api.urls)),
url(r'', include(oscar.urls)),

url(r'^api/', api.urls),
url(r'', oscar.urls),
]
1 change: 1 addition & 0 deletions demosite/mycustomapi/wsgi.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mycustomapi.settings")
Expand Down
2 changes: 1 addition & 1 deletion demosite/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
zip_safe=False,
install_requires=[
'django-oscar-api',
'django>=1.8, <2.0',
'django>=1.11, <2.1',
'django-oscar>=1.5.1'
],
)
12 changes: 6 additions & 6 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ Requirements:
-------------
This version of Oscar API is compatbile with python 2.7 / 3.5 / 3.6 and the following django versions:

Django 1.8:
~~~~~~~~~~~
- Oscar 1.5.3
- requires `djangorestframework<3.7`, and `django-tables2<1.17.0`

Django 1.11:
~~~~~~~~~~~~
- Osccar 1.5.3
- Osccar 1.5.3 / 1.6
- requires `djangorestframework>=3.4`

Django 2.0:
~~~~~~~~~~~
- Oscar 1.6
- requires `djangorestframework>=3.7`
- requires python 3.x

See `Travis`_ for the current tested platforms.

Expand Down
2 changes: 1 addition & 1 deletion docs/source/usage/outofthebox.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ To use the oscarapi application in an oscar ecommerce site without overriding or
from oscarapi.app import application as api
urlpatterns = [
# ... all the things you allready got
url(r'^api/', include(api.urls)),
url(r'^api/', api.urls),
]
.. _mixed-usage-label:
Expand Down
1 change: 1 addition & 0 deletions docs/source/usage/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Product serializers
.. autoclass:: oscarapi.serializers.product.OptionSerializer
.. autoclass:: oscarapi.serializers.product.ProductLinkSerializer
.. autoclass:: oscarapi.serializers.product.RecommmendedProductSerializer
.. autoclass:: oscarapi.serializers.product.ChildProductSerializer
.. autoclass:: oscarapi.serializers.product.ProductSerializer
.. autoclass:: oscarapi.serializers.product.ProductAttributeSerializer
.. autoclass:: oscarapi.serializers.product.ProductAttributeValueSerializer
Expand Down
4 changes: 2 additions & 2 deletions oscarapi/basket/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def prepare_basket(basket, request):

def get_basket(request, prepare=True):
"Get basket from the request."
if request.user.is_authenticated():
if request.user.is_authenticated:
basket = get_user_basket(request.user)
else:
basket = get_anonymous_basket(request)
Expand Down Expand Up @@ -110,7 +110,7 @@ def store_basket_in_session(basket, session):

def request_contains_basket(request, basket):
if basket.can_be_edited:
if request.user.is_authenticated():
if request.user.is_authenticated:
return request.user == basket.owner

return get_basket_id_from_session(request) == basket.pk
Expand Down
22 changes: 15 additions & 7 deletions oscarapi/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
from django.conf import settings
from django.contrib.sessions.middleware import SessionMiddleware
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.http.response import HttpResponse
from django.utils.deprecation import MiddlewareMixin
from django.utils.translation import ugettext as _

from oscar.core.loading import get_class
Expand Down Expand Up @@ -166,10 +167,11 @@ def process_response(self, request, response):
request, response)


class ApiGatewayMiddleWare(IsApiRequest):
class ApiGatewayMiddleWare(MiddlewareMixin, IsApiRequest):
"""
Protect the api gateway with a token.
"""

def process_request(self, request):
if self.is_api_request(request):
key = authentication.get_authorization_header(request)
Expand All @@ -189,20 +191,24 @@ def process_request(self, request):
class ApiBasketMiddleWare(BasketMiddleware, IsApiRequest):
"""
Use this middleware instead of Oscar's basket middleware if you
want to mix the api with and regular oscar views.
want to mix the api with regular oscar views.
Note that this middleware only works with MIDDLEWARE (Django > 1.10)
as MIDDLEWARE_CLASSES is deprecated and Oscar drops the MiddlewareMixin
compatibility in BasketMiddleware since version 1.6
Oscar uses a cookie based session to store baskets for anonymous users, but
oscarapi can not do that, because we don't want to put the burden
of managing a cookie jar on oscarapi clients that are not websites.
"""
def process_request(self, request):
super(ApiBasketMiddleWare, self).process_request(request)

def __call__(self, request):
if self.is_api_request(request):
request.cookies_to_delete = []
# we should make sure that any cookie baskets are turned into
# session baskets, since oscarapi uses only baskets from the
# session.
cookie_key = self.get_cookie_key(request)

basket = self.get_cookie_basket(
cookie_key,
request,
Expand All @@ -214,6 +220,8 @@ def process_request(self, request):
else:
store_basket_in_session(basket, request.session)

return super(ApiBasketMiddleWare, self).__call__(request)

def process_response(self, request, response):
if self.is_api_request(request) and hasattr(request, 'user') and request.session:
# at this point we are sure a basket can be found in the session
Expand All @@ -230,7 +238,7 @@ def process_response(self, request, response):
for cookie_key in cookies_to_delete:
response.delete_cookie(cookie_key)

if not request.user.is_authenticated():
if not request.user.is_authenticated:
response.set_cookie(
cookie_key, cookie,
max_age=settings.OSCAR_BASKET_COOKIE_LIFETIME,
Expand Down
4 changes: 2 additions & 2 deletions oscarapi/serializers/checkout.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from rest_framework.response import Response

from django.conf import settings
from django.core.urlresolvers import reverse, NoReverseMatch
from django.urls import reverse, NoReverseMatch
from django.utils.translation import gettext as _
from oscar.core import prices
from oscar.core.loading import get_class, get_model
Expand Down Expand Up @@ -213,7 +213,7 @@ def get_initial_order_status(self, basket):
def validate(self, attrs):
request = self.context['request']

if request.user.is_anonymous():
if request.user.is_anonymous:
if not settings.OSCAR_ALLOW_ANON_CHECKOUT:
message = _('Anonymous checkout forbidden')
raise serializers.ValidationError(message)
Expand Down
4 changes: 2 additions & 2 deletions oscarapi/serializers/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def get_value(self, obj):
return obj.value.json()
else:
return _(
"%(entity)s has no json method, can not convert to json" % {
"%(entity)s has no json method, can not convert to json" % {
'entity': repr(obj.value)
}
)
Expand Down Expand Up @@ -140,7 +140,7 @@ class Meta(BaseProductSerializer.Meta):
'url', 'upc', 'id', 'title', 'structure',
# 'parent', 'description', 'images', are not included by default, but
# easily enabled by overriding OSCARAPI_CHILDPRODUCTDETAIL_FIELDS
# in your settings file
# in your settings file
'date_created', 'date_updated', 'recommended_products',
'attributes', 'categories', 'product_class',
'stockrecords', 'price', 'availability', 'options'))
Expand Down

0 comments on commit 0993ad7

Please sign in to comment.