Skip to content

Commit

Permalink
Merge pull request #134 from django-oscar/feature/update-line-attribu…
Browse files Browse the repository at this point in the history
…te-options

Functionality to update line attribute options
  • Loading branch information
specialunderwear committed Oct 1, 2018
2 parents 67970e2 + 8a27fdb commit 763adbd
Show file tree
Hide file tree
Showing 13 changed files with 192 additions and 88 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ install:

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

test:
python sandbox/manage.py test oscarapi --settings=sandbox.settings.nomigrations
Expand Down
63 changes: 55 additions & 8 deletions docs/source/usage/communicate_with_the_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,35 @@ You can fetch the detail of each product by following it's url:
"url": "http://localhost:8000/api/products/1/"
}
We want the blue version, so let's check the product options:

.. code-block:: python
response = session.get('http://localhost:8000/api/options/')
print(response.content)
[
{
"url": "http://localhost:8000/api/options/1/",
"id": 1,
"name": "Color",
"code": "color",
"type": "Required"
}
]
option_url = self.response.json()[0]['url']
Ok, now we want to add this to our basket:

.. code-block:: python
data = {
"url": products[1]['url'],
"quantity": 1
"quantity": 1,
options = [{
"option": option_url, "value": "blue"
}]
}
response = session.post('http://localhost:8000/api/basket/add-product/', json=data)
Expand All @@ -132,7 +154,13 @@ And we can see that it has been added:
print(response.content)
[
{
"attributes": [],
"attributes": [
{
'option': 'http://localhost:8000/api/options/1/',
'url': 'http://localhost:8000/api/lineattributes/1/',
'value': 'blue'
}
],
"basket": "http://localhost:8000/api/baskets/1/",
"date_created": "2015-12-30T17:05:05.041698Z",
"is_tax_known": true,
Expand Down Expand Up @@ -170,7 +198,13 @@ You can use a REST PUT and DELETE to update/delete the basket lines. So let's up
# and we can see it's been updated
print(response.content)
{
"attributes": [],
"attributes": [
{
'option': 'http://localhost:8000/api/options/1/',
'url': 'http://localhost:8000/api/lineattributes/1/',
'value': 'blue'
}
],
"basket": "http://localhost:8000/api/baskets/1/",
"date_created": "2016-03-05T21:09:52.664388Z",
"line_reference": "1_1",
Expand All @@ -188,6 +222,19 @@ You can use a REST PUT and DELETE to update/delete the basket lines. So let's up
print(response.content.json()["total_incl_tax"])
30.00
You can also update the color to red if you like:

.. code-block:: python
line_attribute_url = response.content.json()['attributes'][0]['url']
data = {
"value": "red"
}
session.put(line_attribute_url, data)
Now we will delete this line, it will return a 204 when it's successful:

.. code-block:: python
Expand Down Expand Up @@ -236,7 +283,7 @@ If you don't support anonymous checkouts you will have to login the user first
"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']
Expand Down Expand Up @@ -321,7 +368,7 @@ If you don't support anonymous checkouts you will have to login the user first
"number": "10001",
"offer_discounts": [],
"owner": null,
# the payment view is something you will have to implement yourself,
# 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": {
Expand Down Expand Up @@ -350,7 +397,7 @@ If you don't support anonymous checkouts you will have to login the user first
# 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 :ref:`mixed-usage-label` section.
Expand All @@ -359,7 +406,7 @@ If you don't support anonymous checkouts you will have to login the user first
If your shipping methods depend in any way on the shipping address, you can
also POST to the shipping_method api. Just post the shipping details in
the same format as accepted by the checkout api::

{
"country": "http://localhost:8000/api/countries/NL/",
"first_name": "Henk",
Expand Down Expand Up @@ -402,7 +449,7 @@ When you don't support anonymous checkouts you will need to login first. Oscar A
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
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:
Expand Down
24 changes: 18 additions & 6 deletions oscarapi/basket/operations.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"This module contains operation on baskets and lines"
from django.conf import settings

from oscar.core.loading import get_class, get_model
from oscar.core.utils import get_default_currency

Expand All @@ -12,13 +13,14 @@
'get_anonymous_basket',
'get_user_basket',
'store_basket_in_session',
'request_contains_basket',
'flush_and_delete_basket',
'request_contains_line',
'request_allows_access_to',
'save_line_with_default_currency',
)

Basket = get_model('basket', 'Basket')
Line = get_model('basket', 'Line')
LineAttribute = get_model('basket', 'LineAttribute')
Applicator = get_class('offer.applicator', 'Applicator')
Selector = None

Expand Down Expand Up @@ -108,7 +110,7 @@ def store_basket_in_session(basket, session):
session.save()


def request_contains_basket(request, basket):
def request_allows_access_to_basket(request, basket):
if basket.can_be_edited:
if request.user.is_authenticated:
return request.user == basket.owner
Expand All @@ -124,10 +126,20 @@ def flush_and_delete_basket(basket, using=None):
basket.delete(using)


def request_contains_line(request, line):
def request_allows_access_to(request, obj):
if isinstance(obj, Basket):
return request_allows_access_to_basket(request, obj)

basket = get_basket(request, prepare=False)
if basket and basket.pk == line.basket.pk:
return request_contains_basket(request, basket)

if isinstance(obj, Line):
if basket and basket.pk == obj.basket.pk:
return request_allows_access_to_basket(request, basket)

elif isinstance(obj, LineAttribute):
if basket and basket.pk == obj.line.basket.pk:
return request_allows_access_to_basket(request, basket)

return False


Expand Down
8 changes: 8 additions & 0 deletions oscarapi/fixtures/option.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<django-objects version="1.0">
<object model="catalogue.option" pk="1">
<field name="name" type="CharField">Color</field>
<field name="code" type="SlugField">color</field>
<field name="type" type="CharField">Required</field>
</object>
</django-objects>
2 changes: 1 addition & 1 deletion oscarapi/fixtures/voucher.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
<field type="PositiveIntegerField" name="num_basket_additions">0</field>
<field type="PositiveIntegerField" name="num_orders">0</field>
<field type="DecimalField" name="total_discount">0.00</field>
<field type="DateField" name="date_created">2015-11-28</field>
<field type="DateTimeField" name="date_created">2015-11-28T23:00:00+00:00</field>
<field to="offer.conditionaloffer" name="offers" rel="ManyToManyRel"><object pk="2"></object></field>
</object>
</django-objects>
7 changes: 5 additions & 2 deletions oscarapi/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from rest_framework import authentication

from oscarapi.basket.operations import (
request_contains_basket,
request_allows_access_to_basket,
store_basket_in_session,
get_basket
)
Expand Down Expand Up @@ -177,8 +177,11 @@ def __call__(self, request):
request,
Exception("get_cookie_basket doesn't use the manager argument")
)

if basket is not None:
if request_contains_basket(request, basket):
# when a basket exists and we are already allowed to access
# this basket
if request_allows_access_to_basket(request, basket):
pass
else:
store_basket_in_session(basket, request.session)
Expand Down
21 changes: 6 additions & 15 deletions oscarapi/permissions.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,27 @@
from oscarapi.basket.operations import request_contains_basket, request_contains_line
from rest_framework.permissions import BasePermission, IsAuthenticated

from oscarapi.basket.operations import request_allows_access_to


class HasUser(BasePermission):
"Only anonymous and authenticated users can access this resource."
def has_permission(self, request, view):
return request.user


class IsAdminUserOrRequestContainsBasket(HasUser):
"""
Permission class that checks if a request contains a basket.
"""

def has_object_permission(self, request, view, obj):
return request_contains_basket(request, obj) or request.user.is_staff


class IsAdminUserOrRequestContainsLine(BasePermission):
class IsAdminUserOrRequestAllowsAccessTo(BasePermission):
"""
Permission class that checks if a request contains the basket this line
belongs to.
Permission class that checks if a request allows access to a basket.
"""
def has_object_permission(self, request, view, obj):
return request_contains_line(request, obj) or request.user.is_staff
return request_allows_access_to(request, obj) or request.user.is_staff


class IsOwner(IsAuthenticated):
"""
Permission that checks if this object has a foreign key pointing to the
authenticated user of this request
"""

def has_object_permission(self, request, view, obj):
return obj.user == request.user

0 comments on commit 763adbd

Please sign in to comment.