Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: django-oscar/django-oscar
...
head fork: django-oscar/django-oscar
  • 17 commits
  • 27 files changed
  • 2 commit comments
  • 2 contributors
Commits on Jul 19, 2012
@fjern fjern Use basic is_staff condition when checking permissions for generating…
… order reports
cdc3273
@codeinthehole codeinthehole Minor tweak to README 4d17c77
Commits on Jul 20, 2012
@fjern fjern Fix bug in translation tag a86877c
@codeinthehole codeinthehole Merge pull request #240 from tangentlabs/report-permissions
Permissions for generating reports in dashboard
a1b816e
Commits on Jul 23, 2012
@codeinthehole codeinthehole Merge branch 'master' of github.com:tangentlabs/django-oscar 6a49bb0
@codeinthehole codeinthehole Replaced favicon with new one 47d22ae
@fjern fjern Check if shipping method is set only if shipping is required f28c62e
@fjern fjern Enable creating zero-priced products in tests 2ac0314
@codeinthehole codeinthehole Merge pull request #244 from tangentlabs/test-tweaks
Enable creating zero-priced products in tests
17de306
@codeinthehole codeinthehole Merge pull request #245 from tangentlabs/no-shipping-checkout
Check if shipping method is set only if shipping is required
8a4bdf6
@codeinthehole codeinthehole Improved basket usage.
* Less DB queries
* Added basic tests
369fa8e
@codeinthehole codeinthehole Fixed issue with updating products and adding a stock record at the s…
…ame time
f20f308
@codeinthehole codeinthehole Fixed a few i18n issues in detail template bb7cc22
@codeinthehole codeinthehole Fixed #247 - Corrected basket flush implementation.
Also tidied up test case for basket model
82970f5
@codeinthehole codeinthehole Bumped haystack to 2.0.0-beta
This is needed for internal Tangent projects.  The search app in oscar
still needs some work but it works in a very basic fashion at the
moment.

The template for search results still needs attention.
0ec010a
Commits on Jul 24, 2012
@codeinthehole codeinthehole Merge branch 'releases/0.3' 01775f5
@codeinthehole codeinthehole Merge branch 'master' of github.com:tangentlabs/django-oscar f8133f7
Showing with 163 additions and 156 deletions.
  1. +1 −0  .gitignore
  2. +9 −6 README.rst
  3. +9 −5 oscar/apps/basket/abstract_models.py
  4. +8 −8 oscar/apps/basket/middleware.py
  5. +8 −4 oscar/apps/checkout/views.py
  6. +2 −1  oscar/apps/dashboard/catalogue/forms.py
  7. +6 −2 oscar/apps/dashboard/catalogue/views.py
  8. +1 −1  oscar/apps/order/reports.py
  9. +0 −1  oscar/apps/search/__init__.py
  10. +0 −35 oscar/apps/search/abstract_indexes.py
  11. +2 −15 oscar/apps/search/app.py
  12. +1 −1  oscar/apps/search/forms.py
  13. +31 −13 oscar/apps/search/search_indexes.py
  14. +3 −6 oscar/apps/search/views.py
  15. +0 −3  oscar/search_sites.py
  16. BIN  oscar/static/oscar/favicon.ico
  17. +3 −3 oscar/templates/oscar/catalogue/detail.html
  18. +1 −1  oscar/templates/oscar/customer/address-form.html
  19. +1 −1  oscar/templates/oscar/partials/nav_alternate.html
  20. +26 −29 oscar/templates/oscar/search/results.html
  21. +1 −1  oscar/test/helpers.py
  22. +1 −0  requirements.txt
  23. +7 −3 sandbox/settings.py
  24. +1 −0  sandbox/update_sandbox.sh
  25. +2 −1  setup.py
  26. +5 −2 tests/config.py
  27. +34 −14 tests/unit/basket_tests.py
View
1  .gitignore
@@ -34,3 +34,4 @@ sandbox/public/
logs/
build/
*.pdf
+sandbox/whoosh_index
View
15 README.rst
@@ -4,11 +4,11 @@
Domain-driven e-commerce for Django
===================================
-Oscar is an e-commerce framework for Django designed for building
-domain-driven e-commerce sites. It is structured such that any part of the
-core functionality can be customised to suit the needs of your project. This
-allows it to handle a wide range of e-commerce requirements, from large-scale B2C
-sites to complex B2B sites rich in domain-specific business logic.
+Oscar is an e-commerce framework for Django designed for building domain-driven
+e-commerce sites. It is structured such that any part of the core functionality
+can be customised to suit the needs of your project. This allows it to handle a
+wide range of e-commerce requirements, from large-scale B2C sites to complex B2B
+sites rich in domain-specific business logic.
More information:
@@ -38,6 +38,9 @@ Oscar was written by `David Winterbottom`_ (`@codeinthehole`_) and is developed
and maintained by `Tangent Labs`_, a London-based digital agency, with help from
`Mirumee`.
+License
+-------
+
Oscar is released under the permissive `New BSD license`_.
.. _`David Winterbottom`: http://codeinthehole.com
@@ -58,4 +61,4 @@ companies, from large multinationals to small, boutique stores:
* Dolbeau - http://www.dolbeau.ca/
* The UK Labour party - http://shop.labour.org.uk (will be live in early June)
-Many more on the way.
+Many more on the way. If you use Oscar in production, please let us know.
View
14 oscar/apps/basket/abstract_models.py
@@ -63,6 +63,8 @@ def all_lines(self):
This is important for offers as they alter the line models and you don't
want to reload them from the DB.
"""
+ if self.id is None:
+ return []
if self._lines is None:
self._lines = self.lines.all()
return self._lines
@@ -75,7 +77,7 @@ def flush(self):
"""Remove all lines from basket."""
if self.status == FROZEN:
raise PermissionDenied("A frozen basket cannot be flushed")
- self.lines_all().delete()
+ self.lines.all().delete()
self._lines = None
def add_product(self, product, quantity=1, options=None):
@@ -90,7 +92,7 @@ def add_product(self, product, quantity=1, options=None):
if not self.id:
self.save()
- # Line reference is used to distinguish between variations of the same
+ # Line reference is used to distinguish between variations of the same
# product (eg T-shirts with different personalisations)
line_ref = self._create_line_reference(product, options)
@@ -237,8 +239,10 @@ def _get_total(self, property):
@property
def is_empty(self):
- """Return bool based on basket having 0 lines"""
- return self.num_lines == 0
+ """
+ Test if this basket is empty
+ """
+ return self.id is None or self.num_lines == 0
@property
def total_excl_tax(self):
@@ -319,7 +323,7 @@ def total_excl_tax_excl_discounts(self):
@property
def num_lines(self):
"""Return number of lines"""
- return self.all_lines().count()
+ return len(self.all_lines())
@property
def num_items(self):
View
16 oscar/apps/basket/middleware.py
@@ -13,7 +13,7 @@
class BasketMiddleware(object):
def process_request(self, request):
- self.cookies_to_delete = []
+ request.cookies_to_delete = []
basket = self.get_basket(request)
self.apply_offers_to_basket(request, basket)
request.basket = basket
@@ -23,7 +23,7 @@ def get_basket(self, request):
cookie_basket = self.get_cookie_basket(settings.OSCAR_BASKET_COOKIE_OPEN,
request, manager)
- if request.user.is_authenticated():
+ if hasattr(request, 'user') and request.user.is_authenticated():
# Signed-in user: if they have a cookie basket too, it means
# that they have just signed in and we need to merge their cookie
# basket into their user basket, then delete the cookie
@@ -31,7 +31,7 @@ def get_basket(self, request):
basket, _ = manager.get_or_create(owner=request.user)
except Basket.MultipleObjectsReturned:
# Not sure quite how we end up here with multiple baskets
- # We merge any them and create a fresh one
+ # We merge them and create a fresh one
old_baskets = list(manager.filter(owner=request.user))
basket = old_baskets[0]
for other_basket in old_baskets[1:]:
@@ -39,7 +39,7 @@ def get_basket(self, request):
if cookie_basket:
self.merge_baskets(basket, cookie_basket)
- self.cookies_to_delete.append(settings.OSCAR_BASKET_COOKIE_OPEN)
+ request.cookies_to_delete.append(settings.OSCAR_BASKET_COOKIE_OPEN)
elif cookie_basket:
# Anonymous user with a basket tied to the cookie
basket = cookie_basket
@@ -59,8 +59,8 @@ def merge_baskets(self, master, slave):
def process_response(self, request, response):
# Delete any surplus cookies
- if hasattr(self, 'cookies_to_delete'):
- for cookie_key in self.cookies_to_delete:
+ if hasattr(request, 'cookies_to_delete'):
+ for cookie_key in request.cookies_to_delete:
response.delete_cookie(cookie_key)
# If a basket has had products added to it, but the user is anonymous
@@ -100,9 +100,9 @@ def get_cookie_basket(self, cookie_key, request, manager):
basket = Basket.objects.get(pk=basket_id, owner=None,
status=OPEN)
except Basket.DoesNotExist:
- self.cookies_to_delete.append(cookie_key)
+ request.cookies_to_delete.append(cookie_key)
else:
- self.cookies_to_delete.append(cookie_key)
+ request.cookies_to_delete.append(cookie_key)
return basket
def apply_offers_to_basket(self, request, basket):
View
12 oscar/apps/checkout/views.py
@@ -327,13 +327,15 @@ def get(self, request, *args, **kwargs):
messages.error(request, _("You need to add some items to your basket to checkout"))
return HttpResponseRedirect(reverse('basket:summary'))
+ shipping_required = request.basket.is_shipping_required()
+
# Check that shipping address has been completed
- if request.basket.is_shipping_required() and not self.checkout_session.is_shipping_address_set():
+ if shipping_required and not self.checkout_session.is_shipping_address_set():
messages.error(request, _("Please choose a shipping address"))
return HttpResponseRedirect(reverse('checkout:shipping-address'))
# Check that shipping method has been set
- if not self.checkout_session.is_shipping_method_set():
+ if shipping_required and not self.checkout_session.is_shipping_method_set():
messages.error(request, _("Please choose a shipping method"))
return HttpResponseRedirect(reverse('checkout:shipping-method'))
@@ -370,12 +372,14 @@ def get_error_response(self):
if self.request.basket.is_empty:
messages.error(self.request, _("You need to add some items to your basket to checkout"))
return HttpResponseRedirect(reverse('basket:summary'))
+
+ shipping_required = self.request.basket.is_shipping_required()
# Check that shipping address has been completed
- if self.request.basket.is_shipping_required() and not self.checkout_session.is_shipping_address_set():
+ if shipping_required and not self.checkout_session.is_shipping_address_set():
messages.error(self.request, _("Please choose a shipping address"))
return HttpResponseRedirect(reverse('checkout:shipping-address'))
# Check that shipping method has been set
- if not self.checkout_session.is_shipping_method_set():
+ if shipping_required and not self.checkout_session.is_shipping_method_set():
messages.error(self.request, _("Please choose a shipping method"))
return HttpResponseRedirect(reverse('checkout:shipping-method'))
View
3  oscar/apps/dashboard/catalogue/forms.py
@@ -216,7 +216,8 @@ def get_num_categories(self):
num_categories = 0
for i in range(0, self.total_form_count()):
form = self.forms[i]
- if form.has_changed():
+ if form.cleaned_data.get('category', None) and \
+ form.cleaned_data.get('DELETE', False) != True:
num_categories += 1
return num_categories
View
8 oscar/apps/dashboard/catalogue/views.py
@@ -221,9 +221,13 @@ def form_valid(self, form):
image_formset = ProductImageFormSet(self.request.POST,
self.request.FILES,
instance=self.object)
- if all([stockrecord_form.is_valid(), category_formset.is_valid(), image_formset.is_valid()]):
+ if all([stockrecord_form.is_valid(),
+ category_formset.is_valid(),
+ image_formset.is_valid()]):
form.save()
- stockrecord_form.save()
+ stockrecord = stockrecord_form.save()
+ stockrecord.product = self.object
+ stockrecord.save()
category_formset.save()
image_formset.save()
return HttpResponseRedirect(self.get_success_url())
View
2  oscar/apps/order/reports.py
@@ -58,4 +58,4 @@ def generate(self):
return self.formatter.generate_response(orders, **additional_data)
def is_available_to(self, user):
- return user.is_staff and user.has_perm('order.can_view')
+ return user.is_staff
View
1  oscar/apps/search/__init__.py
@@ -1 +0,0 @@
-import oscar.search_sites
View
35 oscar/apps/search/abstract_indexes.py
@@ -1,35 +0,0 @@
-from haystack.indexes import *
-
-from oscar.core.loading import import_module
-product_models = import_module('catalogue.models', ['Product'])
-
-
-class AbstractProductIndex(SearchIndex):
- u"""
- Base class for products solr index definition. Overide by creating your
- own copy of oscar.search_indexes.py
- """
- text = EdgeNgramField(document=True, use_template=True, template_name='search/indexes/product/item_text.txt')
- title = EdgeNgramField(model_attr='title', null=True)
- upc = CharField(model_attr="upc", null=True)
- product_score = FloatField(model_attr="score")
- date_created = DateTimeField(model_attr='date_created')
- date_updated = DateTimeField(model_attr='date_updated')
-
- def index_queryset(self):
- """
- Used when the entire index for model is updated.
-
- Orders by the most recently updated so that new objects are indexed first
- """
- return product_models.Product.objects.order_by('-date_updated')
-
- def get_updated_field(self):
- u"""
- Used to specify the field used to determine if an object has been updated
-
- Can be used to filter the query set when updating the index
- """
- return 'date_updated'
-
-
View
17 oscar/apps/search/app.py
@@ -1,30 +1,17 @@
from django.conf.urls.defaults import patterns, url
-from django.contrib.admin.views.decorators import staff_member_required
-from haystack.query import SearchQuerySet
from oscar.core.application import Application
-from oscar.apps.search.views import SuggestionsView, MultiFacetedSearchView
-from oscar.apps.search.search_indexes import ProductIndex
+from oscar.apps.search.views import MultiFacetedSearchView
from oscar.apps.search.forms import MultiFacetedSearchForm
class SearchApplication(Application):
name = 'search'
-
- suggestions_view = SuggestionsView
search_view = MultiFacetedSearchView
def get_urls(self):
- sqs = SearchQuerySet()
- for field_name, field in ProductIndex.fields.items():
- if field.faceted is True:
- # Ensure we facet the results set by the defined facetable fields
- sqs.facet(field_name)
-
urlpatterns = patterns('',
- url(r'^suggest/$', self.suggestions_view.as_view(), name='suggest'),
- url(r'^$', self.search_view(form_class=MultiFacetedSearchForm,
- searchqueryset=sqs), name='search'),
+ url(r'^$', self.search_view(form_class=MultiFacetedSearchForm), name='search'),
)
return self.post_process_urls(urlpatterns)
View
2  oscar/apps/search/forms.py
@@ -24,7 +24,7 @@ def search(self):
'''
Overriding the search method to allow for multiple facets
'''
- sqs = super(FacetedSearchForm, self).search().order_by('-score')
+ sqs = super(FacetedSearchForm, self).search()
if hasattr(self, 'cleaned_data') and 'selected_facets' in self.cleaned_data:
for f in self.cleaned_data['selected_facets'].split("|"):
sqs = sqs.narrow(f)
View
44 oscar/apps/search/search_indexes.py
@@ -1,19 +1,37 @@
-from haystack import site
-from haystack.exceptions import AlreadyRegistered
+from haystack import indexes
-from oscar.apps.search.abstract_indexes import AbstractProductIndex
-from oscar.core.loading import import_module
-product_models = import_module('catalogue.models', ['Product'])
+from django.db.models import get_model
-class ProductIndex(AbstractProductIndex):
- pass
+class ProductIndex(indexes.SearchIndex, indexes.Indexable):
+ """
+ Base class for products solr index definition. Overide by creating your
+ own copy of oscar.search_indexes.py
+ """
+ text = indexes.EdgeNgramField(document=True, use_template=True,
+ template_name='oscar/search/indexes/product/item_text.txt')
+ title = indexes.EdgeNgramField(model_attr='title', null=True)
+ upc = indexes.CharField(model_attr="upc", null=True)
+ date_created = indexes.DateTimeField(model_attr='date_created')
+ date_updated = indexes.DateTimeField(model_attr='date_updated')
+ def get_model(self):
+ return get_model('catalogue', 'Product')
+
+ def index_queryset(self):
+ """
+ Used when the entire index for model is updated.
+
+ Orders by the most recently updated so that new objects are indexed first
+ """
+ return self.get_model().objects.order_by('-date_updated')
+
+ def get_updated_field(self):
+ """
+ Used to specify the field used to determine if an object has been updated
+
+ Can be used to filter the query set when updating the index
+ """
+ return 'date_updated'
-try:
- site.register(product_models.Product, ProductIndex)
-except AlreadyRegistered:
- # If already registered, it means that a different search app is being
- # used to search products.
- pass
View
9 oscar/apps/search/views.py
@@ -3,7 +3,6 @@
from django.http import HttpResponse, HttpResponseRedirect
from django.views.generic.base import View
from django.conf import settings
-from django.template.response import TemplateResponse
from haystack.query import SearchQuerySet
from haystack.views import FacetedSearchView
@@ -55,7 +54,7 @@ def convert_context_to_json(self, context):
class MultiFacetedSearchView(FacetedSearchView):
- u"""
+ """
Search view for multifaceted searches
"""
template = 'search/results.html'
@@ -63,18 +62,16 @@ class MultiFacetedSearchView(FacetedSearchView):
def __call__(self, request, *args, **kwargs):
"""
Generates the actual response to the search.
-
+
Relies on internal, overridable methods to construct the response.
"""
-
# Look for UPC match
- query = request.GET['q'].strip()
+ query = request.GET.get('q', '').strip()
try:
item = product_models.Product._default_manager.get(upc=query)
return HttpResponseRedirect(item.get_absolute_url())
except product_models.Product.DoesNotExist:
pass
-
return super(MultiFacetedSearchView, self).__call__(request, *args, **kwargs)
@property
View
3  oscar/search_sites.py
@@ -1,3 +0,0 @@
-import haystack
-
-haystack.autodiscover()
View
BIN  oscar/static/oscar/favicon.ico
Binary file not shown
View
6 oscar/templates/oscar/catalogue/detail.html
@@ -35,7 +35,7 @@
{% if user.is_authenticated and user.is_staff %}
<div class="row-fluid">
<div class="well">
- <a href="{% url dashboard:catalogue-product product.id %}"><button class="btn btn-success" value='Edit this product'>Edit this product</button></a>
+ <a href="{% url dashboard:catalogue-product product.id %}"><button class="btn btn-success">{% trans "Edit this product" %}</button></a>
</div>
</div>
{% endif %}
@@ -55,7 +55,7 @@
{% endfor %}
</ul>
{% else %}
- <p class="star">No Rating
+ <p class="star">{% trans "No Rating" %}
<a href="{% url catalogue:reviews-add product.slug product.id %}#addreview">{% trans "Add review" %}</a>
</p>
@@ -74,7 +74,7 @@
{% block product_description %}
<div id="product_description" class="sub-header">
- <h2>Product Description</h2>
+ <h2>{% trans "Product Description" %}</h2>
</div>
<p>{{ product.description }}</p>
{% endblock %}
View
2  oscar/templates/oscar/customer/address-form.html
@@ -9,7 +9,7 @@
{% block breadcrumbs %}
<ul class="breadcrumb">
<li>
- <a href="{% url promotions:home %}">{% tran 'Home' %}</a>
+ <a href="{% url promotions:home %}">{% trans 'Home' %}</a>
<span class="divider">/</span>
</li>
<li>
View
2  oscar/templates/oscar/partials/nav_alternate.html
@@ -11,7 +11,7 @@
</a>
<a class="btn btn-navbar app-ico ico_shop_bag btn-cart hidden" href="{% url checkout:index %}">
<strong>{% trans "Basket" %}
- {% if request.basket.lines.count %}
+ {% if not request.basket.is_empty %}
{% trans "Total:" %} {{ basket.total_incl_tax|currency }}
{% endif %}
</strong>
View
55 oscar/templates/oscar/search/results.html
@@ -3,9 +3,27 @@
{% load thumbnail %}
{% load i18n %}
+{% block title %}
+"{{ query }}" | {{ block.super }}
+{% endblock %}
+
+{% block breadcrumbs %}
+<ul class="breadcrumb">
+ <li>
+ <a href="{% url promotions:home %}">{% trans "Home" %}</a>
+ <span class="divider">/</span>
+ </li>
+ <li>
+ {% trans "Search" %}
+ <span class="divider">/</span>
+ </li>
+ <li class="active"><a href=".">{% blocktrans with q=query%}"{{ q }}"{% endblocktrans %}</a></li>
+</ul>
+{% endblock %}
+
{% block header%}
<div class="page-header">
- <h1>{% trans 'Search for' %} '{{ query }}'</h1>
+ <h1>{% trans 'Search for' %} '{{ query }}'</h1>
</div>
{% endblock %}
@@ -16,38 +34,17 @@
{% endif %}
{% if page.object_list %}
- <ol class="products vertical three">
- {% for result in page.object_list %}
- <li>
- <article class="product_pod">
- {% with image=product.primary_image %}
- {% thumbnail image.original "400x400" upscale=False as thumb %}
- <a href="{{ product.get_absolute_url }}"><img class="thumbnail" src="{{ thumb.url }}" alt="{{ product.get_title }}"></a>
- {% endthumbnail %}
- {% endwith %}
- <h3><a href="{{ result.object.get_absolute_url }}">{{ result.object.get_title }}</a></h3>
- <div class="product_price">
- {% if result.object.is_group %}
- <p>From {{ result.object.min_variant_price_incl_tax|currency }}</p>
- {% else %}
- {% if result.object.has_stockrecord %}
- <h4>{{ result.object.stockrecord.price_incl_tax|currency }}</h4>
- <p class="app-ico avaliability instock">{{ result.object.stockrecord.availability }}</p>
- {% else %}
- <p class="app-ico avaliability outstock">{% trans 'Not available' %}</p>
- {% endif %}
- {% endif %}
- </div>
- </article>
- </li>
- {% endfor %}
- </ol>
+<ol class="products vertical three">
+ {% for result in page.object_list %}
+ {% include "catalogue/partials/product.html" with product=result.object %}
+ {% endfor %}
+</ol>
{% else %}
- <p>{% trans 'No search results found.' %}</p>
+<p>{% trans 'No search results found.' %}</p>
{% endif %}
{% if page_obj %}
- {% include "catalogue/partials/pagination.html" %}
+{% include "catalogue/partials/pagination.html" %}
{% endif %}
{% endblock %}
View
2  oscar/test/helpers.py
@@ -22,7 +22,7 @@ def create_product(price=None, title="Dummy title", product_class="Dummy item cl
if price is not None or partner_sku or num_in_stock is not None:
if not partner_sku:
partner_sku = 'sku_%d_%d' % (item.id, random.randint(0, 10000))
- if not price:
+ if price is None:
price = D('10.00')
partner,_ = Partner._default_manager.get_or_create(name=partner)
View
1  requirements.txt
@@ -18,3 +18,4 @@ pinocchio==0.3.1
nose-progressive==1.3
django-nose==1.1
django-rosetta==0.6.8
+Whoosh==2.4.1
View
10 sandbox/settings.py
@@ -107,7 +107,7 @@
'oscar.apps.promotions.context_processors.promotions',
'oscar.apps.checkout.context_processors.checkout',
'oscar.core.context_processors.metadata',
-)
+)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
@@ -229,8 +229,12 @@
APPEND_SLASH = True
# Haystack settings
-HAYSTACK_SITECONF = 'oscar.search_sites'
-HAYSTACK_SEARCH_ENGINE = 'dummy'
+HAYSTACK_CONNECTIONS = {
+ 'default': {
+ 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
+ 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
+ },
+}
DEBUG_TOOLBAR_CONFIG = {
'INTERCEPT_REDIRECTS': False
View
1  sandbox/update_sandbox.sh
@@ -14,6 +14,7 @@ cd sandbox
./manage.py syncdb --noinput
./manage.py migrate
./manage.py collectstatic --noinput
+./manage.py rebuild_index --noinput
# Re-compile python code
touch deploy/wsgi/sandbox.wsgi
View
3  setup.py
@@ -29,12 +29,13 @@
'PIL==1.1.7',
'South==0.7.3',
'django-extra-views==0.2.0',
- 'django-haystack==1.2.7',
+ 'django-haystack==2.0.0-beta',
'django-treebeard==1.61',
'sorl-thumbnail==11.12',
'python-memcached==1.48',
'django-sorting==0.1',
],
+ dependency_links=['http://github.com/toastdriven/django-haystack/tarball/master#egg=django-haystack-2.0.0-beta'],
# See http://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=['Environment :: Web Environment',
'Framework :: Django',
View
7 tests/config.py
@@ -50,12 +50,15 @@ def configure(nose_args):
'oscar.apps.customer.auth_backends.Emailbackend',
'django.contrib.auth.backends.ModelBackend',
),
+ HAYSTACK_CONNECTIONS={
+ 'default': {
+ 'ENGINE': 'haystack.backends.simple_backend.SimpleEngine',
+ }
+ },
ROOT_URLCONF='tests.site.urls',
LOGIN_REDIRECT_URL='/accounts/',
DEBUG=False,
SITE_ID=1,
- HAYSTACK_SEARCH_ENGINE='dummy',
- HAYSTACK_SITECONF = 'oscar.search_sites',
APPEND_SLASH=True,
NOSE_ARGS=nose_args,
**OSCAR_SETTINGS
View
48 tests/unit/basket_tests.py
@@ -1,39 +1,59 @@
import datetime
from django.test import TestCase
+from django.test.client import RequestFactory
-from oscar.apps.basket.models import Basket, Line
+from oscar.apps.basket.models import Basket
+from oscar.apps.basket.middleware import BasketMiddleware
from oscar.test.helpers import create_product
from oscar.apps.basket.reports import (
OpenBasketReportGenerator, SubmittedBasketReportGenerator)
-class BasketModelTest(TestCase):
+class TestBasketModel(TestCase):
def setUp(self):
- self.basket = Basket.objects.create()
- self.dummy_product = create_product()
+ self.basket = Basket()
+ self.product = create_product()
- def test_empty_baskets_have_zero_lines(self):
- self.assertTrue(Basket().num_lines == 0)
+ def test_an_empty_basket_has_zero_lines(self):
+ self.assertEqual(0, self.basket.num_lines)
def test_new_baskets_are_empty(self):
- self.assertTrue(Basket().is_empty)
+ self.assertTrue(self.basket.is_empty)
- def test_basket_have_with_one_line(self):
- Line.objects.create(basket=self.basket, product=self.dummy_product)
- self.assertTrue(self.basket.num_lines == 1)
+ def test_adding_product_creates_line(self):
+ self.basket.add_product(self.product)
+ self.assertEqual(1, self.basket.num_lines)
+
+ def test_adding_multiproduct_line_returns_correct_number_of_items(self):
+ self.basket.add_product(self.product, 10)
+ self.assertEqual(self.basket.num_items, 10)
+ self.assertEqual(self.basket.num_lines, 1)
def test_add_product_creates_line(self):
- self.basket.add_product(self.dummy_product)
+ self.basket.add_product(self.product)
self.assertTrue(self.basket.num_lines == 1)
- def test_adding_multiproduct_line_returns_correct_number_of_items(self):
- self.basket.add_product(self.dummy_product, 10)
+ def test_flushing_basket_removes_all_lines(self):
+ self.basket.add_product(self.product, 10)
self.assertEqual(self.basket.num_items, 10)
+ self.basket.flush()
+ self.assertEqual(self.basket.num_items, 0)
+
+
+class TestBasketMiddleware(TestCase):
+
+ def setUp(self):
+ self.middleware = BasketMiddleware()
+
+ def test_basket_is_attached_to_request(self):
+ req = RequestFactory().get('/')
+ self.middleware.process_request(req)
+ self.assertTrue(hasattr(req, 'basket'))
-class BasketReportTests(TestCase):
+class TestBasketReports(TestCase):
def test_open_report_doesnt_error(self):
data = {

Showing you all comments on commits in this comparison.

@codeinthehole

Was this added to meet a particular requirement? Or just for consistency?

@fjern

It emerged from a requirement/bug fix for CC. Putting more restrictions in the project, not in the framework seemed a right way to me. Especially, since other reports do not use any additional permissions.

Something went wrong with that request. Please try again.