Selaa lähdekoodia

Personalised products now work with the basket sub-app.

master
David Winterbottom 15 vuotta sitten
vanhempi
commit
a3c7301dcf

+ 33
- 9
oscar/basket/abstract_models.py Näytä tiedosto

1
 from decimal import Decimal
1
 from decimal import Decimal
2
+import zlib
2
 
3
 
3
 from django.contrib.auth.models import User
4
 from django.contrib.auth.models import User
4
 from django.db import models
5
 from django.db import models
61
     def flush(self):
62
     def flush(self):
62
         self.lines.all().delete()
63
         self.lines.all().delete()
63
     
64
     
64
-    def add_product(self, item, quantity=1):
65
+    def add_product(self, item, quantity=1, options=[]):
65
         """
66
         """
66
         Convenience method for adding products to a basket
67
         Convenience method for adding products to a basket
67
         """
68
         """
69
+        line_ref = self._get_line_reference(item, options)
68
         try:
70
         try:
69
-            line = self.lines.get(product=item)
71
+            line = self.lines.get(line_reference=line_ref)
70
             line.quantity += quantity
72
             line.quantity += quantity
71
             line.save()
73
             line.save()
72
         except ObjectDoesNotExist:
74
         except ObjectDoesNotExist:
73
-            self.lines.create(basket=self, product=item, quantity=quantity)
75
+            line = self.lines.create(basket=self, line_reference=line_ref, product=item, quantity=quantity)
76
+            for option_dict in options:
77
+                o =line.attributes.create(line=line, option=option_dict['option'], value=option_dict['value'])
74
     
78
     
75
     def add_line(self, line):
79
     def add_line(self, line):
76
         """
80
         """
77
         For adding a line from another basket to this one
81
         For adding a line from another basket to this one
78
         """
82
         """
79
-        line.basket = self
80
-        line.save()
83
+        try:
84
+            # Line already exists - bump its quantity and delete the old
85
+            existing_line = self.lines.get(line_reference=line.line_reference)
86
+            existing_line.quantity += line.quantity
87
+            existing_line.save()
88
+            line.delete()
89
+        except ObjectDoesNotExist:
90
+            # Line does not already exist - reassign its basket
91
+            line.basket = self
92
+            line.save()
93
+    
94
+    def _get_line_reference(self, item, options):
95
+        if not options:
96
+            return item.id
97
+        return "%d_%s" % (item.id, zlib.crc32(str(options)))
81
     
98
     
82
     # ==========
99
     # ==========
83
     # Properties
100
     # Properties
167
     def line_price_incl_tax(self):
184
     def line_price_incl_tax(self):
168
         return self.quantity * self.unit_price_incl_tax
185
         return self.quantity * self.unit_price_incl_tax
169
     
186
     
187
+    @property
188
+    def description(self):
189
+        d = str(self.product)
190
+        ops = []
191
+        for attribute in self.attributes.all():
192
+            ops.append("%s = '%s'" % (attribute.option.name, attribute.value))
193
+        if ops:
194
+            d = "%s (%s)" % (d, ", ".join(ops))
195
+        return d
196
+    
170
     def save(self, *args, **kwargs):
197
     def save(self, *args, **kwargs):
171
         if self.quantity == 0:
198
         if self.quantity == 0:
172
             return self.delete(*args, **kwargs)
199
             return self.delete(*args, **kwargs)
187
     An attribute of a basket line
214
     An attribute of a basket line
188
     """
215
     """
189
     line = models.ForeignKey('basket.Line', related_name='attributes')
216
     line = models.ForeignKey('basket.Line', related_name='attributes')
190
-    type = models.CharField(_("Type"), max_length=128)
217
+    option = models.ForeignKey('product.option')
191
     value = models.CharField(_("Value"), max_length=255)    
218
     value = models.CharField(_("Value"), max_length=255)    
192
     
219
     
193
-    def get_hash(self):
194
-        return zlib.crc32(self.type + self.value)
195
-    
196
     class Meta:
220
     class Meta:
197
         abstract = True
221
         abstract = True
198
     
222
     

+ 7
- 2
oscar/basket/factory.py Näytä tiedosto

39
 def _get_basket(request, cookie_key, manager):
39
 def _get_basket(request, cookie_key, manager):
40
     b = None
40
     b = None
41
     if request.user.is_authenticated():
41
     if request.user.is_authenticated():
42
-        b = manager.get(owner=request.user)
42
+        try:
43
+            b = manager.get(owner=request.user)
44
+        except basket_models.Basket.DoesNotExist, e:
45
+            pass
43
     else:
46
     else:
44
         b = _get_cookie_basket(request, cookie_key, manager)
47
         b = _get_cookie_basket(request, cookie_key, manager)
45
     return b    
48
     return b    
61
         # new one and store the id and hash in a cookie.
64
         # new one and store the id and hash in a cookie.
62
         basket = manager.create()
65
         basket = manager.create()
63
         cookie = "%s_%s" % (basket.id, _get_basket_hash(basket.id))
66
         cookie = "%s_%s" % (basket.id, _get_basket_hash(basket.id))
64
-        response.set_cookie(cookie_key, cookie, max_age=COOKIE_LIFETIME)
67
+        response.set_cookie(cookie_key, cookie, max_age=COOKIE_LIFETIME, httponly=True)
65
     else:
68
     else:
66
         basket = anon_basket
69
         basket = anon_basket
67
     return basket 
70
     return basket 
78
                 b = manager.get(pk=basket_id)
81
                 b = manager.get(pk=basket_id)
79
             except basket_models.Basket.DoesNotExist, e:
82
             except basket_models.Basket.DoesNotExist, e:
80
                 b = None
83
                 b = None
84
+        else:
85
+            response.delete_cookie(cookie_key)
81
     return b  
86
     return b  
82
 
87
 
83
 def _get_basket_hash(id):
88
 def _get_basket_hash(id):

+ 43
- 4
oscar/basket/forms.py Näytä tiedosto

1
 from django import forms
1
 from django import forms
2
 
2
 
3
-class AddToBasketForm(forms.Form):
4
-    action = forms.CharField(widget=forms.HiddenInput(), initial='add')
5
-    product_id = forms.IntegerField(widget=forms.HiddenInput())
6
-    quantity = forms.IntegerField(min_value=1)
3
+
4
+class FormFactory(object):
5
+    
6
+    def create(self, item, values=None):
7
+        """
8
+        For dynamically creating add-to-basket forms for a given product
9
+        """
10
+        self.values = values
11
+        if item.is_group:
12
+            return self._create_group_product_form(item)
13
+        return self._create_product_form(item)
14
+
15
+    def _create_group_product_form(self, item):
16
+        pass
17
+    
18
+    def _create_product_form(self, item):
19
+        # See http://www.b-list.org/weblog/2008/nov/09/dynamic-forms/ for 
20
+        # advice on how this works.
21
+        self.fields = {'action': forms.CharField(widget=forms.HiddenInput(), initial='add'),
22
+                       'product_id': forms.IntegerField(widget=forms.HiddenInput(), min_value=1),
23
+                       'quantity': forms.IntegerField(min_value=1)}
24
+        if not self.values:
25
+            self.values = {'action': 'add', 
26
+                           'product_id': item.id, 
27
+                           'quantity': 1}
28
+            
29
+        for option in item.options.all():
30
+            self._add_option_field(item, option)
31
+            
32
+        form_class = type('AddToBasketForm', (forms.BaseForm,), {'base_fields': self.fields})
33
+        return form_class(self.values)
34
+    
35
+    def _add_option_field(self, item, option):
36
+        """
37
+        Creates the appropriate form field for the product option.
38
+        
39
+        This is designed to be overridden so that specific widgets can be used for 
40
+        certain types of options.
41
+        """
42
+        self.fields[option.code] = forms.CharField()
43
+    
44
+
45
+    

+ 12
- 1
oscar/basket/managers.py Näytä tiedosto

2
 
2
 
3
 
3
 
4
 class OpenBasketManager(models.Manager):
4
 class OpenBasketManager(models.Manager):
5
+    
5
     def get_query_set(self):
6
     def get_query_set(self):
6
         return super(OpenBasketManager, self).get_query_set().filter(status="Open")
7
         return super(OpenBasketManager, self).get_query_set().filter(status="Open")
7
     
8
     
9
+    def get_or_create(self, **kwargs):
10
+        return self.get_query_set().get_or_create(status="Open", **kwargs)
11
+    
8
 class SavedBasketManager(models.Manager):
12
 class SavedBasketManager(models.Manager):
13
+    
9
     def get_query_set(self):
14
     def get_query_set(self):
10
-        return super(SavedBasketManager, self).get_query_set().filter(status="Saved")
15
+        return super(SavedBasketManager, self).get_query_set().filter(status="Saved")
16
+    
17
+    def create(self, **kwargs):
18
+        return self.get_query_set().create(status="Saved", **kwargs)
19
+    
20
+    def get_or_create(self, **kwargs):
21
+        return self.get_query_set().get_or_create(status="Saved", **kwargs)

+ 2
- 2
oscar/basket/templates/basket/summary.html Näytä tiedosto

31
     </tr>
31
     </tr>
32
     {% for line in basket.lines.all %}
32
     {% for line in basket.lines.all %}
33
     <tr>
33
     <tr>
34
-        <td><a href="{{ line.product.get_absolute_url }}">{{ line.product }}</a></td>
34
+        <td><a href="{{ line.product.get_absolute_url }}">{{ line.description }}</a></td>
35
         <td>
35
         <td>
36
             <form action="{% url oscar-basket-line line.line_reference %}" method="post">
36
             <form action="{% url oscar-basket-line line.line_reference %}" method="post">
37
                 {% csrf_token %}
37
                 {% csrf_token %}
96
     </tr>
96
     </tr>
97
     {% for line in saved_basket.lines.all %}
97
     {% for line in saved_basket.lines.all %}
98
     <tr>
98
     <tr>
99
-        <td><a href="{{ line.product.get_absolute_url }}">{{ line.product }}</a></td>
99
+        <td><a href="{{ line.product.get_absolute_url }}">{{ line.description }}</a></td>
100
         <td>
100
         <td>
101
             <form action="{% url oscar-saved-basket-line line.line_reference %}" method="post">
101
             <form action="{% url oscar-saved-basket-line line.line_reference %}" method="post">
102
                 {% csrf_token %}
102
                 {% csrf_token %}

+ 1
- 1
oscar/basket/urls.py Näytä tiedosto

12
     return BasketView()(request, *args, **kwargs)
12
     return BasketView()(request, *args, **kwargs)
13
 
13
 
14
 urlpatterns = patterns('oscar.basket.views',
14
 urlpatterns = patterns('oscar.basket.views',
15
-    url(r'^line/(?P<line_reference>\w+)/$', line_view, name='oscar-basket-line'),
15
+    url(r'^line/(?P<line_reference>[\w-]+)/$', line_view, name='oscar-basket-line'),
16
     url(r'^saved-line/(?P<line_reference>\w+)/$', saved_line_view, name='oscar-saved-basket-line'),
16
     url(r'^saved-line/(?P<line_reference>\w+)/$', saved_line_view, name='oscar-saved-basket-line'),
17
     url(r'^$', basket_view, name='oscar-basket'),
17
     url(r'^$', basket_view, name='oscar-basket'),
18
 )
18
 )

+ 13
- 6
oscar/basket/views.py Näytä tiedosto

8
 
8
 
9
 # Using dynamic loading
9
 # Using dynamic loading
10
 basket_models = import_module('basket.models', ['Basket', 'Line', 'InvalidBasketLineError'])
10
 basket_models = import_module('basket.models', ['Basket', 'Line', 'InvalidBasketLineError'])
11
-basket_forms = import_module('basket.forms', ['AddToBasketForm'])
11
+basket_forms = import_module('basket.forms', ['FormFactory'])
12
 basket_factory = import_module('basket.factory', ['get_or_create_open_basket', 'get_open_basket', 
12
 basket_factory = import_module('basket.factory', ['get_or_create_open_basket', 'get_open_basket', 
13
                                                   'get_or_create_saved_basket', 'get_saved_basket'])
13
                                                   'get_or_create_saved_basket', 'get_saved_basket'])
14
 product_models = import_module('product.models', ['Item'])
14
 product_models = import_module('product.models', ['Item'])
55
         messages.info(self.request, "Your basket has been emptied")
55
         messages.info(self.request, "Your basket has been emptied")
56
         
56
         
57
     def do_add(self, basket):
57
     def do_add(self, basket):
58
-        form = basket_forms.AddToBasketForm(self.request.POST)
58
+        item = get_object_or_404(product_models.Item.objects, pk=self.request.POST['product_id'])
59
+        factory = basket_forms.FormFactory()
60
+        form = factory.create(item, self.request.POST)
59
         if not form.is_valid():
61
         if not form.is_valid():
60
-            messages.error("Unable to add your item to the basket - submission not valid")
62
+            # @todo Put form errors in session and redirect back to product page
63
+            messages.error(self.request, "Unable to add your item to the basket - submission not valid")
61
         else:
64
         else:
62
-            product = get_object_or_404(product_models.Item.objects, pk=form.cleaned_data['product_id'])
63
-            basket.add_product(product, form.cleaned_data['quantity'])
65
+            # Extract product options from POST
66
+            options = []
67
+            for option in item.options.all():
68
+                if option.code in form.cleaned_data:
69
+                    options.append({'option': option, 'value': form.cleaned_data[option.code]})
70
+            basket.add_product(item, form.cleaned_data['quantity'], options)
64
             messages.info(self.request, "'%s' (quantity %d) has been added to your basket" %
71
             messages.info(self.request, "'%s' (quantity %d) has been added to your basket" %
65
-                          (product.get_title(), form.cleaned_data['quantity']))
72
+                          (item.get_title(), form.cleaned_data['quantity']))
66
  
73
  
67
 
74
 
68
 class LineView(object):
75
 class LineView(object):

+ 34
- 3
oscar/product/abstract_models.py Näytä tiedosto

7
 from django.utils.translation import ugettext_lazy as _
7
 from django.utils.translation import ugettext_lazy as _
8
 from django.template.defaultfilters import slugify
8
 from django.template.defaultfilters import slugify
9
 from django.core.urlresolvers import reverse
9
 from django.core.urlresolvers import reverse
10
+from django.core.exceptions import ObjectDoesNotExist
10
 
11
 
11
 from oscar.product.managers import BrowsableItemManager
12
 from oscar.product.managers import BrowsableItemManager
12
 
13
 
15
     For converting a string in CamelCase or normal text with spaces
16
     For converting a string in CamelCase or normal text with spaces
16
     to the normal underscored variety
17
     to the normal underscored variety
17
     """
18
     """
18
-    without_whitespace = re.sub('\s*', '_', str.strip())
19
+    without_whitespace = re.sub('\s+', '_', str.strip())
19
     s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', without_whitespace)
20
     s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', without_whitespace)
20
     return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
21
     return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
21
 
22
 
26
     """
27
     """
27
     name = models.CharField(_('name'), max_length=128)
28
     name = models.CharField(_('name'), max_length=128)
28
     slug = models.SlugField(max_length=128, unique=True)
29
     slug = models.SlugField(max_length=128, unique=True)
29
-    options = models.ManyToManyField('product.Option')
30
+    options = models.ManyToManyField('product.Option', blank=True)
30
 
31
 
31
     class Meta:
32
     class Meta:
32
         abstract = True
33
         abstract = True
72
     item_class = models.ForeignKey('product.ItemClass', verbose_name=_('item class'), null=True,
73
     item_class = models.ForeignKey('product.ItemClass', verbose_name=_('item class'), null=True,
73
         help_text="""Choose what type of product this is""")
74
         help_text="""Choose what type of product this is""")
74
     attribute_types = models.ManyToManyField('product.AttributeType', through='ItemAttributeValue')
75
     attribute_types = models.ManyToManyField('product.AttributeType', through='ItemAttributeValue')
75
-    options = models.ManyToManyField('product.Option')
76
+    options = models.ManyToManyField('product.Option', blank=True)
76
     date_created = models.DateTimeField(auto_now_add=True)
77
     date_created = models.DateTimeField(auto_now_add=True)
77
     date_updated = models.DateTimeField(auto_now=True, null=True, default=None)
78
     date_updated = models.DateTimeField(auto_now=True, null=True, default=None)
78
 
79
 
79
     objects = models.Manager()
80
     objects = models.Manager()
80
     browsable = BrowsableItemManager()
81
     browsable = BrowsableItemManager()
81
 
82
 
83
+    # Properties
84
+
82
     @property
85
     @property
83
     def is_top_level(self):
86
     def is_top_level(self):
84
         return self.parent == None
87
         return self.parent == None
91
     def is_variant(self):
94
     def is_variant(self):
92
         return not self.is_top_level
95
         return not self.is_top_level
93
 
96
 
97
+    @property
98
+    def min_variant_price_incl_tax(self):
99
+        return self._min_variant_price('price_incl_tax')
100
+    
101
+    @property
102
+    def min_variant_price_excl_tax(self):
103
+        return self._min_variant_price('price_excl_tax')
104
+
105
+    @property
106
+    def has_stockrecord(self):
107
+        try:
108
+            sr = self.stockrecord
109
+            return True
110
+        except ObjectDoesNotExist:
111
+            return False
112
+
94
     def attribute_summary(self):
113
     def attribute_summary(self):
95
         return ", ".join([attribute.__unicode__() for attribute in self.attributes.all()])
114
         return ", ".join([attribute.__unicode__() for attribute in self.attributes.all()])
96
 
115
 
107
             return self.parent.item_class
126
             return self.parent.item_class
108
         return None
127
         return None
109
 
128
 
129
+    # Helpers
130
+    
131
+    def _min_variant_price(self, property):
132
+        prices = []
133
+        for variant in self.variants.all():
134
+            if variant.has_stockrecord:
135
+                prices.append(getattr(variant.stockrecord, property))
136
+        if not prices:
137
+            return None
138
+        prices.sort()
139
+        return prices[0]
140
+
110
     class Meta:
141
     class Meta:
111
         abstract = True
142
         abstract = True
112
         ordering = ['-date_created']
143
         ordering = ['-date_created']

+ 1
- 4
oscar/product/admin.py Näytä tiedosto

7
 class ItemClassAdmin(admin.ModelAdmin):
7
 class ItemClassAdmin(admin.ModelAdmin):
8
     prepopulated_fields = {"slug": ("name",)}
8
     prepopulated_fields = {"slug": ("name",)}
9
     
9
     
10
-class OptionInline(admin.TabularInline):
11
-    model = Item.options.through    
12
-
13
 class ItemAdmin(admin.ModelAdmin):
10
 class ItemAdmin(admin.ModelAdmin):
14
     list_display = ('get_title', 'upc', 'get_item_class', 'is_top_level', 'is_group', 'is_variant', 'attribute_summary', 'date_created')
11
     list_display = ('get_title', 'upc', 'get_item_class', 'is_top_level', 'is_group', 'is_variant', 'attribute_summary', 'date_created')
15
     prepopulated_fields = {"slug": ("title",)}
12
     prepopulated_fields = {"slug": ("title",)}
16
-    inlines = [AttributeInline, OptionInline]
13
+    inlines = [AttributeInline]
17
 
14
 
18
 admin.site.register(ItemClass, ItemClassAdmin)
15
 admin.site.register(ItemClass, ItemClassAdmin)
19
 admin.site.register(Item, ItemAdmin)
16
 admin.site.register(Item, ItemAdmin)

+ 23
- 10
oscar/product/templates/product/browse.html Näytä tiedosto

7
 
7
 
8
 {% block content %}
8
 {% block content %}
9
 
9
 
10
+{% if products.count %}
11
+
10
 <ol>
12
 <ol>
11
-{% for product in products.object_list %}
13
+{% for product in products %}
12
 <li>
14
 <li>
13
     <a href="{{ product.get_absolute_url }}">{{ product.get_title }}</a><br/>
15
     <a href="{{ product.get_absolute_url }}">{{ product.get_title }}</a><br/>
14
-    {% if product.stockrecord %} 
15
-        &pound;{{ product.stockrecord.price_incl_tax }}<br/>
16
-        {{ product.stockrecord.availability }}
16
+    {% if product.is_group %}
17
+        From &pound;{{ product.min_variant_price_incl_tax }}
17
     {% else %}
18
     {% else %}
18
-        Not available
19
+        {% if product.has_stockrecord %} 
20
+            &pound;{{ product.stockrecord.price_incl_tax }}<br/>
21
+            {{ product.stockrecord.availability }}
22
+        {% else %}
23
+            Not available    
24
+        {% endif %}
19
     {% endif %}    
25
     {% endif %}    
20
 </li>
26
 </li>
21
 {% endfor %}
27
 {% endfor %}
23
 
29
 
24
 <div class="pagination">
30
 <div class="pagination">
25
     <span class="step-links">
31
     <span class="step-links">
26
-        {% if products.has_previous %}
27
-            <a href="?page={{ products.previous_page_number }}">previous</a>
32
+    
33
+        {% if page_obj.has_previous %}
34
+            <a href="?page={{ page_obj.previous_page_number }}">previous</a>
28
         {% endif %}
35
         {% endif %}
29
 
36
 
30
         <span class="current">
37
         <span class="current">
31
-            Page {{ products.number }} of {{ products.paginator.num_pages }}.
38
+            Page {{ page_obj.number }} of {{ paginator.num_pages }}.
32
         </span>
39
         </span>
33
 
40
 
34
-        {% if products.has_next %}
35
-            <a href="?page={{ products.next_page_number }}">next</a>
41
+        {% if page_obj.has_next %}
42
+            <a href="?page={{ page_obj.next_page_number }}">next</a>
36
         {% endif %}
43
         {% endif %}
37
     </span>
44
     </span>
38
 </div>
45
 </div>
39
 
46
 
47
+{% else %}
48
+
49
+<p>No products found.</p>
50
+
51
+{% endif %}
52
+
40
 {% endblock content %}
53
 {% endblock content %}
41
 
54
 

+ 1
- 1
oscar/product/templates/product/item.html Näytä tiedosto

55
     </form>
55
     </form>
56
 {% else %}
56
 {% else %}
57
 
57
 
58
-<form action="/shop/basket/" method="post">
58
+<form action="{% url oscar-basket %}" method="post">
59
     {% csrf_token %}
59
     {% csrf_token %}
60
     {{ form.as_p }}
60
     {{ form.as_p }}
61
     <input type="submit" value="Add to basket" />
61
     <input type="submit" value="Add to basket" />

+ 4
- 3
oscar/product/urls.py Näytä tiedosto

1
 from django.conf.urls.defaults import *
1
 from django.conf.urls.defaults import *
2
+from oscar.product.views import ItemDetailView, ProductListView, ItemClassListView
2
 
3
 
3
 urlpatterns = patterns('oscar.product.views',
4
 urlpatterns = patterns('oscar.product.views',
4
-    url(r'(?P<item_class_slug>[\w-]+)/(?P<item_slug>[\w-]*)-(?P<item_id>\d+)/$', 'item', name='oscar-product-item'),
5
-    url(r'(?P<item_class_slug>[\w-]+)/$', 'item_class', name='oscar-product-item-class'),
6
-    url(r'$', 'all', name='oscar-products'),
5
+    url(r'(?P<item_class_slug>[\w-]+)/(?P<item_slug>[\w-]*)-(?P<item_id>\d+)/$', ItemDetailView.as_view(), name='oscar-product-item'),
6
+    url(r'(?P<item_class_slug>[\w-]+)/$', ItemClassListView.as_view(), name='oscar-product-item-class'),
7
+    url(r'^$', ProductListView.as_view(), name='oscar-products'),
7
 )
8
 )

+ 58
- 47
oscar/product/views.py Näytä tiedosto

1
 from django.conf import settings
1
 from django.conf import settings
2
 from django.http import HttpResponse, Http404, HttpResponseRedirect
2
 from django.http import HttpResponse, Http404, HttpResponseRedirect
3
 from django.template import Context, loader, RequestContext
3
 from django.template import Context, loader, RequestContext
4
-from django.shortcuts import render_to_response, get_object_or_404
4
+from django.shortcuts import render, get_object_or_404
5
 from django.core.urlresolvers import reverse
5
 from django.core.urlresolvers import reverse
6
 from django.core.paginator import Paginator, InvalidPage, EmptyPage
6
 from django.core.paginator import Paginator, InvalidPage, EmptyPage
7
+from django.views.generic import ListView, DetailView
7
 
8
 
8
 from oscar.services import import_module
9
 from oscar.services import import_module
9
 
10
 
10
 product_models = import_module('product.models', ['Item', 'ItemClass'])
11
 product_models = import_module('product.models', ['Item', 'ItemClass'])
11
-basket_forms = import_module('basket.forms', ['AddToBasketForm'])
12
+basket_forms = import_module('basket.forms', ['FormFactory'])
12
 
13
 
13
 
14
 
14
-def item(request, item_class_slug, item_slug, item_id, template_file='product/item.html'):
15
-    """ 
16
-    Single product page
15
+class ItemDetailView(DetailView):
17
     """
16
     """
18
-    item = get_object_or_404(product_models.Item, pk=item_id)
19
-    form = basket_forms.AddToBasketForm({'product_id': item_id, 'quantity': 1, 'action': 'add'})
20
-    return render_to_response(template_file, locals(), context_instance=RequestContext(request))
21
-
22
-
23
-def item_class(request, item_class_slug, template_file='product/browse-all.html', results_per_page=20):
24
-    item_class = get_object_or_404(product_models.ItemClass, slug=item_class_slug)
25
-    product_list = product_models.Item.browsable.filter(item_class=item_class)
26
-    paginator = Paginator(product_list, results_per_page)
27
-    
28
-    # Make sure page request is an int. If not, deliver first page.
29
-    try:
30
-        page = int(request.GET.get('page', '1'))
31
-    except ValueError:
32
-        page = 1
33
-    try:
34
-        products = paginator.page(page)
35
-    except (EmptyPage, InvalidPage):
36
-        products = paginator.page(paginator.num_pages)
37
-    
38
-    return render_to_response(template_file, locals())
39
-
40
-
41
-def all(request, template_file='product/browse.html', results_per_page=20):
42
-    if 'q' in request.GET and request.GET['q']:
43
-        query = request.GET['q'].strip()
44
-        product_list = product_models.Item.browsable.filter(title__icontains=query)
45
-        summary = "Products matching '%s'" % query
46
-    else:
47
-        product_list = product_models.Item.browsable.all()
48
-        summary = "All products"
49
-    paginator = Paginator(product_list, results_per_page)
17
+    View a single product.
18
+    """
19
+    template_name = "product/item.html"
50
     
20
     
51
-    # Make sure page request is an int. If not, deliver first page.
52
-    try:
53
-        page = int(request.GET.get('page', '1'))
54
-    except ValueError:
55
-        page = 1
56
-    try:
57
-        products = paginator.page(page)
58
-    except (EmptyPage, InvalidPage):
59
-        products = paginator.page(paginator.num_pages)
21
+    def get_object(self):
22
+        return get_object_or_404(product_models.Item, pk=self.kwargs['item_id'])
60
     
23
     
61
-    return render_to_response(template_file, locals())
24
+    def get_context_data(self, **kwargs):
25
+        context = super(ItemDetailView, self).get_context_data(**kwargs)
26
+        
27
+        # Add add-to-basket form for this product
28
+        factory = basket_forms.FormFactory()
29
+        context['form'] = factory.create(self.object)
30
+        
31
+        return context
32
+
33
+
34
+class ItemClassListView(ListView):
35
+    """
36
+    View products filtered by item-class.
37
+    """
38
+    context_object_name = "products"
39
+    template_name = 'product/browse.html'
40
+    paginate_by = 20
41
+
42
+    def get_queryset(self):
43
+        item_class = get_object_or_404(product_models.ItemClass, slug=self.kwargs['item_class_slug'])
44
+        return product_models.Item.browsable.filter(item_class=item_class)
45
+
46
+
47
+class ProductListView(ListView):
48
+
49
+    context_object_name = "products"
50
+    template_name = 'product/browse.html'
51
+    paginate_by = 20
52
+
53
+    def get_search_query(self):
54
+        q = None
55
+        if 'q' in self.request.GET and self.request.GET['q']:
56
+            q = self.request.GET['q'].strip()
57
+        return q
62
 
58
 
59
+    def get_queryset(self):
60
+        q = self.get_search_query()
61
+        if q:
62
+            return product_models.Item.browsable.filter(title__icontains=q)
63
+        else:
64
+            return product_models.Item.browsable.all()
65
+        
66
+    def get_context_data(self, **kwargs):
67
+        context = super(ProductListView, self).get_context_data(**kwargs)
68
+        q = self.get_search_query()
69
+        if not q:
70
+            context['summary'] = 'All products'
71
+        else:
72
+            context['summary'] = "Products matching '%s'" % q
73
+        return context

Loading…
Peruuta
Tallenna