Django Oscar API

This package provides a RESTful API for django-oscar, it’s based on django-rest-framework and it exposes most of Oscar’s functionality. You can find the source code on GitHub. If you have any questions or problems using Oscar API, please use the Github issuetracker.

Requirements:

Currently Oscar API is compatbile with python 2.7 / 3.5 / 3.6 and the following django versions:

  • Django 1.8: Oscar 1.1,1 / 1.2.2 / 1.3 and 1.4
  • Django 1.9: Oscar 1.3 and 1.4
  • Django 1.10: Osccar 1.4 (requires djangorestframework>=3.4)

See Travis for the current tested platforms.

Installation

Please see the installation instructions of Oscar to install Oscar and how to create your own project. Then you can install Oscar API by simply typing:

$ pip install django-oscar-api

Or you could add django-oscar-api to your project dependencies.

Note

If you would like to install the current development version, use this:

$ pip install git+https://github.com/django-oscar/django-oscar-api.git

Use out-of-the-box

You can use the oscarapi application in an oscar ecommerce site without any customization. See for more information: Use Oscar API out-of-the-box

Play around with the sandbox

You can also install Oscar API from source and run the sandbox site to play around a bit. Make sure to create a virtualenv first.

$ mkvirtualenv oscarapi
$ git clone https://github.com/django-oscar/django-oscar-api.git
$ cd django-oscar-api
$ make sandbox

# run the server
$ python sandbox/manage.py runserver

Now you can browse the API at http://localhost:8000/api. Here you can actually use the API already (a cool feature of django-rest-framework) by using your browser and test which JSON formats you can send/receive.

But I want to customise the standard serializers / views!

Probably you want this, because you already extended or changed Oscar’s functionality by forking it’s apps right? See Customizing Oscar API for this.

Use Oscar API out-of-the-box

To use the oscarapi application in an oscar ecommerce site without overriding or customizing the default views and serializers just follow these steps:

  1. Install it (see Installation)
  2. Add rest_framework and oscarapi to your INSTALLED_APPS section in settings.py
  3. Add the application’s urls to your own app’s url.py:
from oscarapi.app import application as api
urlpatterns = [
    # ... all the things you allready got
    url(r'^api/', include(api.urls)),
]

Middleware and mixed usage

There are some middleware classes shipped with Oscar API which can be useful for your project. For example, we have seen in practice that it can be useful to mix plain Oscar and the API (fill your basket with the API and use plain Oscar views for checkout).

See the Oscar API Middleware section for more information.

Example Usage

When you browse through the API (see also the Play around with the sandbox section), most of the things are already pretty clear in terms how you can communicate with the API. In the following examples we use the python requests package to demonstate how the API works.

Get the basket

First get our basket and see the response:

import requests

# this will persist the session cookies automatically for us
session = requests.Session()

response = session.get('http://localhost:8000/api/basket/')
print(response.content)
{
    "currency": null,
    "id": 1,
    "is_tax_known": true,
    "lines": "http://localhost:8000/api/baskets/1/lines/",
    "offer_discounts": [],
    "owner": null,
    "status": "Open",
    "total_excl_tax": "0.00",
    "total_excl_tax_excl_discounts": "0.00",
    "total_incl_tax": "0.00",
    "total_incl_tax_excl_discounts": "0.00",
    "total_tax": "0.00",
    "url": "http://localhost:8000/api/baskets/1/",
    "voucher_discounts": []
}

Note

We need to re-send the cookies (wich include the django session id) to make sure we get the same basket again, this is the reason we use requests.Session(). Otherwise we would get a new basket each request. So it’s important to keep a track of sessions in your application. You can also use an alternative session middleware like the Header Session Middleware.

response = session.get('http://localhost:8000/api/basket/')
# we get the same basket when we send the session cookie
print(response.json()['id'])
1

To see what’s inside the basket we need to get the basket lines:

lines_url = response.json()['lines']
response = session.get(lines_url)
# we don't have any lines yet because we did not put anything in the basket
print(response.content)
[]

Add products to the basket

To add a product to the basket we first need to get the available products:

response = session.get('http://localhost:8000/api/products/')
print(response.content)
[
    {
        "id": 2,
        "title": "",
        "url": "http://localhost:8000/api/products/2/"
    },
    {
        "id": 1,
        "title": "Oscar T-shirt",
        "url": "http://localhost:8000/api/products/1/"
    }
]

You can fetch the detail of each product by following it’s url:

products = response.json()
# get the details of the second product
response = session.get(products[1]['url'])
print(response.content)
{
    "attributes": [
        {
            "name": "Size",
            "value": "Small"
        }
    ],
    "availability": "http://localhost:8000/api/products/1/availability/",
    "categories": [
        "Clothing"
    ],
    "date_created": "2013-12-12T16:33:57.426000Z",
    "date_updated": "2013-12-12T16:33:57.426000Z",
    "description": "",
    "id": 1,
    "images": [],
    "options": [],
    "price": "http://localhost:8000/api/products/1/price/",
    "product_class": "T-shirt",
    "recommended_products": [],
    "stockrecords": "http://localhost:8000/api/products/1/stockrecords/",
    "title": "Oscar T-shirt",
    "url": "http://localhost:8000/api/products/1/"
}

Ok, now we want to add this to our basket:

data = {
    "url": products[1]['url'],
    "quantity": 1
}

response = session.post('http://localhost:8000/api/basket/add-product/', json=data)

And we can see that it has been added:

response = session.get('http://localhost:8000/api/basket/')
lines_url = response.json()['lines']
response = session.get(lines_url)
print(response.content)
[
    {
        "attributes": [],
        "basket": "http://localhost:8000/api/baskets/1/",
        "date_created": "2015-12-30T17:05:05.041698Z",
        "is_tax_known": true,
        "price_currency": "EUR",
        "price_excl_tax": "10.00",
        "price_excl_tax_excl_discounts": "10.00",
        "price_incl_tax": "10.00",
        "price_incl_tax_excl_discounts": "10.00",
        "product": "http://localhost:8000/api/products/1/",
        "quantity": 1,
        "stockrecord": "http://localhost:8000/api/stockrecords/1/",
        "url": "http://localhost:8000/api/lines/1/",
        "warning": null
    }
]

Update or delete basket lines

You can use a REST PUT and DELETE to update/delete the basket lines. So let’s update the quantity for example:

# first get our line
response = session.get('http://localhost:8000/api/basket/')
response = session.get(response.json()['lines'])
line_url = response.json()[0]['url']

# now update the quantity
data = {
    "quantity": 3
}
response = session.put(line_url, data)

# and we can see it's been updated
print(response.content)
{
    "attributes": [],
    "basket": "http://localhost:8000/api/baskets/1/",
    "date_created": "2016-03-05T21:09:52.664388Z",
    "line_reference": "1_1",
    "price_currency": "EUR",
    "price_excl_tax": "10.00",
    "price_incl_tax": "10.00",
    "product": "http://localhost:8000/api/products/1/",
    "quantity": 3,
    "stockrecord": "http://localhost:8000/api/stockrecords/1/",
    "url": "http://localhost:8000/api/lines/1/"
}

# and our basket recalculated the total as well:
 response = session.get('http://localhost:8000/api/basket/')
 print(response.content.json()["total_incl_tax"])
 30.00

Now we will delete this line, it will return a 204 when it’s successful:

response = session.delete(line_url)
print(response.status_code)
204

# we can verify that the basket is empty now
response = session.get('http://localhost:8000/api/basket/')
lines_url = response.json()['lines']
response = session.get(lines_url)
print(response.content)
[]

Place an order (checkout)

When your basket is filled an you want to proceed to checkout you can do a single call with all information needed. Note that we are doing an anonymous checkout here, so we need to set the guest_email field. (Make sure that OSCAR_ALLOW_ANON_CHECKOUT is set to True in your settings.py). If you don’t support anonymous checkouts you will have to login the user first (see login example below).

guest_email = "foo@example.com"

# get our basket information
response = session.get('http://localhost:8000/api/basket/')
basket_data = response.json()

# oscar needs a country for the shipping address. You can get a list of
# the available countries with the api
response = session.get('http://localhost:8000/api/countries/')
countries = response.json()
print(countries)
[
    {
        "display_order": 0,
        "is_shipping_country": true,
        "iso_3166_1_a3": "NLD",
        "iso_3166_1_numeric": "528",
        "name": "Kingdom of the Netherlands",
        "printable_name": "Netherlands",
        "url": "http://127.0.0.1:8000/api/countries/NL/"
    }
]

# we need the country url in the shipping address
country_url = countries[0]['url']

# let's fill out the request data
data = {
    "basket": basket_data['url'],
    "guest_email": guest_email,
    "total": basket_data['total_incl_tax'],
    "shipping_method_code": "no-shipping-required",
    # the shipping charge is optional, but we leave it here for example purposes
    "shipping_charge": {
        "currency": basket_data['currency'],
        "excl_tax": "0.0",
        "tax": "0.0"
    },
    "shipping_address": {
        "country": country_url,
        "first_name": "Henk",
        "last_name": "Van den Heuvel",
        "line1": "Roemerlaan 44",
        "line2": "",
        "line3": "",
        "line4": "Kroekingen",
        "notes": "",
        "phone_number": "+31 26 370 4887",
        "postcode": "7777KK",
        "state": "Gerendrecht",
        "title": "Mr"
    }
}

# now we can place the order
response = session.post('http://localhost:8000/api/checkout/', json=data)

# and the api should give us a response with all info needed
print (response.content)
{
    "basket": "http://localhost:8000/api/baskets/1/",
    "billing_address": null,
    "currency": "EUR",
    "date_placed": "2016-01-02T23:18:01.089796Z",
    "guest_email": "foo@example.com",
    "lines": "http://localhost:8000/api/orders/1/lines/",
    # this is the order number generated in oscar
    "number": "10001",
    "offer_discounts": [],
    "owner": null,
    # the payment view is something you will have to implement yourself,
    # see the note below
    "payment_url": "You need to implement a view named 'api-payment' which redirects to the payment provider and sets up the callbacks.",
    "shipping_address": {
        "country": "http://localhost:8000/api/countries/NL/",
        "first_name": "Henk",
        "id": 3,
        "last_name": "Van den Heuvel",
        "line1": "Roemerlaan 44",
        "line2": "",
        "line3": "",
        "line4": "Kroekingen",
        "notes": "",
        "phone_number": "+31 26 370 4887",
        "postcode": "7777KK",
        "search_text": "Henk Van den Heuvel Roemerlaan 44 Kroekingen Gerendrecht 7777KK Kingdom of the Netherlands",
        "state": "Gerendrecht",
        "title": "Mr"
    },
    "shipping_code": "free-shipping",
    "shipping_excl_tax": "0.00",
    "shipping_incl_tax": "0.00",
    "shipping_method": "Free shipping",
    "status": "new",
    "total_excl_tax": "10.00",
    "total_incl_tax": "10.00",
    # you can fetch the order details by getting this url
    "url": "http://localhost:8000/api/orders/1/",
    "voucher_discounts": []
}

Note

After you placed an order with the api, the basket is frozen. Oscar API has checks for this in the checkout view and won’t let you checkout the same (or any frozen) basket again. At this stage an order is submitted in Oscar and you will have to implement the following steps regarding payment yourself. See the payment_url field above in the response. You can also use the regular Oscar checkout views if you like, take a look at the Middleware and mixed usage section.

Note

In the checkout view of Oscar, the function handle_successful_order is called after placing an order. This sends the order confirmation message, flushes your session and sends the post_checkout signal. The Oscar API checkout view is not calling this method by design. If you would like to send a confirmation message (or other stuff you need to do) after placing an order you can subscribe to the oscarapi_post_checkout signal, see Oscar API Signals.

Note

An extension on top of django-oscar-api providing a more flexible checkout API with a pluggable payment methods is written by Craig Weber, see django oscar api checkout

Login the user

When you don’t support anonymous checkouts you will need to login first. Oscar API comes with a simple login view for this:

data = {
    "username": "test",
    "password": "test"
}
response = session.post('http://localhost:8000/api/login/', json=data)

Note

Custom User models with a different username field are supported. In Oscar API this field will be mapped to the corresponding username field.

When the authentication was succesful, your will receive a new (authenticated) sessionid, and the anonymous basket has been automatically merged with a (previous stored) basket of this specific user. You can see now that the owner is set in the basket:

response = session.get('http://localhost:8000/api/basket/')
print(response.content)
{
    "currency": "EUR",
    "id": 2,
    "is_tax_known": true,
    "lines": "http://localhost:8000/api/baskets/2/lines/",
    "offer_discounts": [],
    # now, this basket has an owner
    "owner": "http://localhost:8000/api/users/2/",
    "status": "Open",
    "total_excl_tax": "10.00",
    "total_excl_tax_excl_discounts": "10.00",
    "total_incl_tax": "10.00",
    "total_incl_tax_excl_discounts": "10.00",
    "total_tax": "0.00",
    "url": "http://localhost:8000/api/baskets/2/",
    "voucher_discounts": []
}

Oscar API Middleware

Oscar API ships with some useful middelware classes for session management, to restrict access to your API and make sure that you can mix an Oscar stand-alone website and the API.

Basket Middleware

This is a replacement middleware for oscar.apps.basket.middleware.BasketMiddleware With this middleware, you can mix the API and regular oscar views (also for anonymous users).

class oscarapi.middleware.ApiBasketMiddleWare(get_response=None)[source]

Use this middleware instead of Oscar’s basket middleware if you want to mix the api with and regular oscar views.

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.

Header Session Middleware

This middleware is very useful when sessions are managed by an external website which commuicates with Oscar API.

class oscarapi.middleware.HeaderSessionMiddleware

The Header Session Middleware replaces the cookie based session middleware of Django when sessions are maintained by an external application / website based on the http://www.w3.org/TR/WD-session-id standard.

Caution

The session protocol should only be used when a TRUSTED application needs to perform operations on behalf of a user. Never use the session protocol in any application where the requests can be inspected by an attacker, such as a mobile application. CSRF protection is NOT applied so requests CAN be forged when using the session protocol. Regular cookie based sessions are still fully functional and the entire REST API should work with this method of authentication as well. When using cookie based session, csrf restrictions are enforced as usual, so this is the preferred method of authentication for any untrusted applications.

Important

When using the session protocol, the client application is 100% responsible for making sure session id’s uniquely identify users. A session CAN and WILL be attached to another user if that user logs in with a session id allready in use by someone else.

When using the session protocol for authentication, the REST API will not make use of cookies, which are usually used for transferring the session id between the client and the backend. Instead we will use the Session-Id header as specified in http://www.w3.org/TR/WD-session-id

The w3c Session Identification URI specification proposes a format for a session identifier as follows:

SID:type:realm:identifier[-thread][:count]
Where the fields type, realm, identifier. thread and count are defined as follows:

type
    Type of session identifier. This field allows other session identifier types to be defined. This draft specifies the identifier type "ANON".
realm
    Specifies the realm within which linkage of the identifier is possible. Realms have the same format as DNS names.
identifier
    Unstructured random integer specific to realm generated using a procedure with a negligible probability of collision. The identifier is encoded using base 64.
thread
    Optional extension of identifier field used to differentiate concurrent uses of the same session identifier. The thread field is an integer encoded in hexadecimal.
count
    Optional Hexadecimal encoded Integer containing a monotonically increasing counter value. A client should increment the count field after each operation.

An example of a session identifier would be: SID:ANON:www.example.com:82d7ac3f-135c-4b12-a296-ff3c4701307d. This identifier will be hashed to fit in 40 bytes to yield the final session key.

The thread and count values, while allowed will be ignored and not included when hashing. When upgrading a user from anonymous to authenticated, a new session id will be generated, by replacing ANON in the original session id with AUTH and performing the hashing again, example:

SID:AUTH:www.example.com:82d7ac3f-135c-4b12-a296-ff3c4701307d.

Every response of the REST API will also contain the Session-Id header. When a user is logged in, The response will contain a DIFFERENT Session-Id as the request, because ANON will be replaced with AUTH.

Note that the generation of the identifier is a responsibility of the client application, as specified in the w3c specification. This means that it remains possible for a client application to resume sessions, as long as the identifier is kept.

Client applications MUST ensure that session id’s are unique, since it must uniquely identify a user, in the same way as a user name.

Important

The above measures ensure the following behaviour:

  1. Anonymous sessions can be resumed indefinately, preventing loss of shopping basket content.
  2. Differentiating Session Identification URI’s between anonymous users and authenticated users prevents accidental retrieval of a private shopping basket for a user that has logged out of the client application.
  3. Keeping the session identifier part of the Session Identification URI the same for both anonymous and authenticated users, simplifies tracking and associating REST API resources with client application resources.

Note

Note that guessing the identifier of an authenticated or anonymous user and therefor hijacking the session, is nomore difficult then guessing the session id stored in a cookie for a web application.

Also note that the identifier, which is in the Session Identification URI, not used as the session id directly, which means session id’s gathered from cookies can not be used to authenticate with the header Session-Id

Gateway MiddleWare

Protects the usage of your API with an authentication token (we call it an ApiKey). To use this, add this as the first middleware in MIDDLEWARE_CLASSES in your settings.py.

class oscarapi.middleware.ApiGatewayMiddleWare[source]

Protect the api gateway with a token.

To use this, you need to add an ApiKey object in the Django admin called MyKey. From now on you need to send a HTTP Authorization: MyKey header when communicating with the API.

See also:
class oscarapi.models.ApiKey(id, key)[source]

Oscar API Settings

Main settings

OSCARAPI_BLOCK_ADMIN_API_ACCESS

Default: True

Useful in production websites wehere you want to make sure that admin users can’t access the API (they can read/write anything which is exposed by the API).

Serializer settings

Most of the model serializers in Oscar API have a default set of fields to use in the REST API. If you customized the Oscar models you can reflect this customization by adding settings for this serializer.

For example, the RecommendedProduct serializer is defined as following:

class RecommmendedProductSerializer(OscarModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='product-detail')

    class Meta:
        model = Product
        fields = overridable(
            'OSCARAPI_RECOMMENDED_PRODUCT_FIELDS',
            default=('url',)
        )

When you add the following section to your settings.py you will add the ‘title’ field as well:

OSCARAPI_RECOMMENDED_PRODUCT_FIELDS = ('url', 'title')

The following serializers have customizable field settings:

Basket serializers
class oscarapi.serializers.basket.BasketSerializer(instance=None, data=<class rest_framework.fields.empty>, **kwargs)[source]
class oscarapi.serializers.basket.BasketLineSerializer(*args, **kwargs)[source]

This serializer computes the prices of this line by using the basket strategy.

class oscarapi.serializers.basket.VoucherSerializer(*args, **kwargs)[source]
Checkout serializers
class oscarapi.serializers.checkout.OrderSerializer(*args, **kwargs)[source]

The order serializer tries to have the same kind of structure as the basket. That way the same kind of logic can be used to display the order as the basket in the checkout process.

class oscarapi.serializers.checkout.OrderLineSerializer(*args, **kwargs)[source]

This serializer renames some fields so they match up with the basket

class oscarapi.serializers.checkout.UserAddressSerializer(*args, **kwargs)[source]
Login serializers
class oscarapi.serializers.login.UserSerializer(instance=None, data=<class rest_framework.fields.empty>, **kwargs)[source]
Product serializers
class oscarapi.serializers.product.OptionSerializer(*args, **kwargs)[source]
class oscarapi.serializers.product.ProductLinkSerializer(*args, **kwargs)[source]
class oscarapi.serializers.product.RecommmendedProductSerializer(*args, **kwargs)[source]
class oscarapi.serializers.product.ProductSerializer(*args, **kwargs)[source]
class oscarapi.serializers.product.ProductAttributeSerializer(*args, **kwargs)[source]
class oscarapi.serializers.product.ProductAttributeValueSerializer(*args, **kwargs)[source]

Permissions and security

Some notes on gateway & resource protection.

Because we need the REST API to fascilitate the same usage patterns as a regular oscar HTML frontend, we need protection of resources on several levels.

  1. Gateway level. An API token can be used to communicate with the REST API. That means we can authorize client applications to make use of the REST API. Examples of client applications are a website or a mobile application. See also the Gateway MiddleWare.
  2. User level. Because we don’t want resource protection to be the responsibility of the client application, we need restrictions of resource access on a user level. The user will use the REST API through an authorized client application. A user must only be able to access his own resources and it must be strictly enforced inside the REST API, that there is no way an API client application can access resources of a different user than the one identified to the REST API. Effectively an authorized client application can only manipulate resources on a user level and not on an administrator level. See aso the Main settings.

Anonymous users

Just like the oscar HTML frontend, some part of the functionality of oscar is available to anonymous users. That doesn’t mean an anonymous user can not be identified. They are identified by their session, the same way as in a regular HTML frontend.

There is an upgrade path for an anonymous user, to become an authenticated user, which opens up the functionality restricted to authenticated users.

A client application can upgrade a user by using the login API, See also the Login the user example.

The following actions will be performed when a user logs in:

  1. The user will be authenticated with the REST API. The next steps will only be performed is login is succesful.
  2. The anonymous cart will be merged with the private cart associated with that authenticated user.
  3. A new session will be started, this session identifies the authenticated user for the duration of the session.
  4. The new, merged cart will be associated with this session.
  5. The anonymous session will be terminated.
  6. A response will be issued containing the new session id.

Permissions

The Django REST Framework already supplies it’s own defined permissions to make sure that you can define which user can do what (PUT or just GET etc.). Oscar API is using the following permissions, which you can use in your own views:

class oscarapi.permissions.HasUser[source]

Only anonymous and authenticated users can access this resource.

class oscarapi.permissions.IsAdminUserOrRequestContainsBasket[source]

Permission class that checks if a request contains a basket.

class oscarapi.permissions.IsAdminUserOrRequestContainsLine[source]

Permission class that checks if a request contains the basket this line belongs to.

class oscarapi.permissions.IsOwner[source]

Permission that checks if this object has a foreign key pointing to the authenticated user of this request

Oscar API Signals

An overview of the signals defined by Oscar API.

oscarapi_post_checkout

class oscarapi.signals.oscarapi_post_checkout

Raised when an order is successfully placed with the checkout API call

Arguments sent with this signal:

order

The currently placed order

user

The user in question

request

The request instance

response

The response instance

Customizing Oscar API

By using the django-rest-framework life has become easy, at least for customizing the Oscar API. Oscar API exists of a collection of views and serializers which can be overriden by following the steps below.

Note

In oscar you can fork an app to easily customize only the things you want to change.

Oscar API is using the basics of this so you can see Oscar API as one of the apps you customized just like in Oscar. Each Oscar app (or forked app) has a app.py which manages the url’s to your custom views.

In Oscar API the entry point of this is oscarapi.app:RESTApiApplication.

In your own app, you can extend this class, and override some of the urls to direct them to your own views. You can subclass any of the views in oscarapi, or just write your own from scratch.

So, to modify some of the functionality in oscarapi, do the following:

  1. In your project, create a new django app with manage.py startapp mycustomapi.
  2. In your app, create a file named app.py and in there extend oscarapi.app:RESTApiApplication, like the following example:
from oscarapi.app import RESTApiApplication

    class MyRESTApiApplication(RESTApiApplication):

        def get_urls(self):
            urls = super(MyRESTApiApplication, self).get_urls()
            return urls

application = MyRESTApiApplication()
  1. Make sure you use this application instead of the app shipped with oscarapi in your urls.py:
from django.conf.urls import include, url
from django.contrib import admin

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

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

]

Note

If you think that this is not changing anything (yet) then this is correct, see below.

  1. Include your own app in INSTALLED_APPS instead of django-oscar-api (and add django-oscar-api to your app’s dependencies) and see if this works.
  2. Add a serializer and a view for the parts you want to change. In this example, we will override the ProductList view so we can specify a different ProductLinkSerializer which includes images as well:

serializers.py

from oscarapi.serializers.product import (
    ProductImageSerializer, ProductLinkSerializer)


class MyProductLinkSerializer(ProductLinkSerializer):
    images = ProductImageSerializer(many=True, required=False)

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

views.py

from oscarapi.views import basic

from .serializers import MyProductLinkSerializer


class ProductList(basic.ProductList):
    serializer_class = MyProductLinkSerializer
  1. Adjust your app.py with your custom urls:
from django.conf.urls import url

from mycustomapi import views

from oscarapi.app import RESTApiApplication


class MyRESTApiApplication(RESTApiApplication):

    def get_urls(self):
        urls = [url(
            r'^products/$',
            views.ProductList.as_view(), name='product-list')]

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

application = MyRESTApiApplication()
  1. Your urls.py still looks the same, as we have configured the override in app.py:
from django.conf.urls import include, url
from django.contrib import admin

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

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

]

The complete example above is available in the Github repository of Oscar API if you want to try it out.

Changelog

1.1.4 (2017-07-04)

Features:
  • #102 Let the ProductAttribute and ProductAttributeValue serializer fields be overridable in the settings (yazanhorani)
  • #101 Don’t delete anonymous basket which are merged after login, leave them in the database with the status MERGED (aleksandrpanteleymonov)
Notes:
Before this release, anonymous baskets where merged in the LoginView and after being merged, deleted. This behaviour is now removed, so anonymous baskets remain in the database and have the status MERGED (This is more in the direction of how Oscar is working). You can change this behaviour by overriding the merge_baskets method / hook in the LoginView, or you should add a cron job to cleanup old baskets with the status MERGED from the database.

1.1.3 (2017-05-23)

Features:
  • Updated documentation to mention the django-oscar-api-checkout plugin
Fixes:
  • #100 The checkout view should not use the wrong mixin to check the basket ownership

1.1.2 (2017-05-10)

Fixes:
  • #98 Fix user address integrity error

1.1.1 (2017-05-09)

Features:
  • #97 Now it’s possible to manage your address book (user addresses)
Fixes:
  • #95 basket/shipping-methods could use another basket (aleksandrpanteleymonov)
  • Fixed sandbox settings to work with Django 1.10
  • Updated docs to use new urlpatterns format

1.1.0 (2017-03-13)

Features:
  • #88 Checkout with a billing address is now supported
  • Drops support for Django 1.7, tested with Oscar 1.4
Fixes:
  • Updated requirements: djangorestframework>=3.3

1.0.10 (2016-12-08)

Fixes:
  • #82 Recalculate offers when updating lines or receiving them individually
  • Make sure that the post and delete methods of the LoginView return valid (json) responses
  • #86 Add missing Meta.fields attribute to work the default first level of api endpoints. (jklapuch)
Features:
  • Updated the documentation and added a demosite to explain how to override a view/serializer

1.0.9 (2016-10-24)

Fixes:
  • RestFramework will nolonger complain about “Creating a ModelSerializer without either the ‘fields’ attribute or the ‘exclude’ attribute has been deprecated since 3.3.0, and is now disallowed. Add an explicit fields = ‘__all__’ to the LineAttributeSerializer serializer.”

1.0.8 (2016-10-04)

Fixes:
  • #78 PUT on BasketLineSerializer was raising a 500 error due to incorrect LineAttributeSerializer definition

1.0.7 (2016-08-26)

Fixes:
  • #77 Use configured LoginSerializer instead of the hardcoded one (whyscream)
  • Cleaned up urls.py to be compatible with django 1.10 (SalahAdDin)

1.0.6 (2016-07-27)

Features:
  • Make add_voucher a class based view so we can easily override the serializer
Fixes:
  • Oscar expects ‘vouchercode’ to be uppercase
  • #74 Python 3 does not have itertools.imap, we use six.moves.map now (crgwbr)

1.0.5 (2016-07-13)

Fixes:
  • #70 Change process_response to have acorrect API created basket cookie added to the response (albertojacini)

1.0.4 (2016-04-04)

Features:
  • #65 Add Docker configuration for testing convenience (crgwbr)
Fixes:
  • #66 Raise a ValidationError (instead of a 500 server error) when you try to checkout with an empty basket (crgwbr)
  • #67 Fixes an AssertionError in the LineList view (missing queryset attribute)

1.0.3 (2016-03-21)

Features:
  • #35 Changes format of urls of basket lines (lines/1 -> basket/1/lines/1)
  • #63 Make AddProductSerializer easily overridable
Fixes:
  • #63 You can now update basketlines more easily with a PUT, updated documentation for this

1.0.2 (2016-03-01)

Features:
  • #58 Send a signal after placing an order so you don’t need to customize the CheckoutView for custom post actions (bufke)
Fixes:
  • #60 is_quantity_allowed returned the quantity and not an error message (bootinge)
  • Updated the docs with forgotten application definition (SamuelSilveira)

1.0.1 (2016-01-29)

Fixes:
  • #57 Make sure that we are really compatible with Django 1.9 (against Oscar Dev)
  • Removed django-compressor<2.0 as a dependency
  • Fix for the LoginSerializer to make it work with custom username fields

1.0.0 (2016-01-14)

Initial release.