# [django-oscar]商品頁面新增尺寸選項
###### django-oscar
## 1. 什麼是"option"
option讓賣家可以針對商品性質新增選項,請看以下範例:
> 衣服:尺寸、顏色(S/M/L、紅/黑/粉)
食物:辣度、口味(辣/不辣、草莓口味/巧克力口味)
有了這些選項,消費者就可以選擇特定類型加入購物車
賣家也能更善用頁面空間呈現商品
## 2. 知道什麼是option後,就來新增尺寸的option吧!
django-oscar預設是沒有任何option出現的(見下圖)
---

(預設的oscar-django是沒有紅框處的)
---
為了讓商品頁出現尺寸的option
我們要先到catalogue/models.py覆寫Option類別!
> 如果還沒fork要覆寫的app,記得先去fork catalogue和basket這兩個app
> 這樣子你才有catalogue/models.py可以覆寫喔
> 所有customize的第一步都是先fork要改的app!(畫重點)
```python=
#project/mainsite/catalogue/models.py
from oscar.apps.catalogue.abstract_models import AbstractOption
class Option(AbstractOption):#覆寫原本的Option
# Option types
TEXT = "text"
INTEGER = "integer"
FLOAT = "float"
BOOLEAN = "boolean"
DATE = "date"
CHOICES = "choices"
TYPE_CHOICES = (
(TEXT, _("Text")),
(INTEGER, _("Integer")),
(BOOLEAN, _("True / False")),
(FLOAT, _("Float")),
(DATE, _("Date")),
(CHOICES,_("Choices")),#讓選項可以用choice的方式呈現
)
type = models.CharField(_("Type"), max_length=255, default=TEXT, choices=TYPE_CHOICES)
```
修改好程式碼後
到 `admin/catalogue/option` 的頁面按下add按鈕
應該就可以看到下拉式選單中出現了第20行打的Choices

我們順手新增一下「尺寸」這個option吧~

接著我們到 `admin/catalogue/Product` 隨便點一個已經新增好的商品

往下滑,找到`Product Options`,選擇我們剛剛新增好的「尺寸」

接下來,因為衣服不可能永遠什麼尺寸都有,有些尺寸會比較快賣完
我們希望可以在 `Attribute` 新增尺寸這個特徵
某尺寸賣完時,我們就到 `Attribute value` 把該尺寸拿掉
而Option只要負責從 `Attribute value` 中抓值顯示就好
所以我們接下來到 `admin/catalogue/Product attribute`
新增一個名為尺寸的Attribute
(Option Group的部分選multiple options是因為尺寸有很多種)

接下來回到 `admin/catalogue/Product`
一樣先隨便點一個商品,進入編輯頁面,到 `PRODUCT ATTRIBUTE VALUES` 的地方
應該就可以選擇有哪些尺寸了!
以這張圖片為例,這個商品就是只有S、L和XL這三種尺寸

寫到這邊,商品頁面還沒辦法好好呈現我們的Option
我們還要再修改一下程式碼!
請回到你的程式編輯器,到basket資料夾下建一個forms.py檔案,然後把下面的程式碼放上去
(細節請見註解)
```python=
# basket/forms.py
from oscar.apps.basket.forms import AddToBasketForm, SimpleAddToBasketForm
from django.db.models import Sum
from django.forms.utils import ErrorDict
from django.utils.translation import gettext_lazy as _
from oscar.core.loading import get_model
from oscar.forms import widgets
Line = get_model('basket', 'line')
Basket = get_model('basket', 'basket')
Option = get_model('catalogue', 'Option')
Product = get_model('catalogue', 'product')
ProductAttribute = get_model('catalogue', 'ProductAttribute')
ProductAttributeValue = get_model('catalogue', 'ProductAttributeValue')
def _option_text_field(option, product):
return forms.CharField(label=option.name, required=option.required)
def _option_integer_field(option, product):
return forms.IntegerField(label=option.name, required=option.required)
def _option_boolean_field(option, product):
return forms.BooleanField(label=option.name, required=option.required)
def _option_float_field(option, product):
return forms.FloatField(label=option.name, required=option.required)
def _option_date_field(option, product):
return forms.DateField(label=option.name, required=option.required, widget=forms.widgets.DateInput)
#把抓到的資料轉成tuple,供CHOICES顯示,你可以依照自己的資料狀況寫一個自己的trim方法
def trimAttribueValue(attr):
tmp = attr.replace('尺寸: ','')
size_list = tmp.split(', ')
for i in range(0, len(size_list)):
size_list[i] = (size_list[i],size_list[i])
# print('DIY',tuple(size_list))
size_tuple = tuple(size_list)
return size_tuple
#重點!就是這裡讓選項跑出來的!
#記得把參數從只有option改成(option, product)
def _option_choices_field(option, product):
#抓「尺寸」這個Attribute
attribute = ProductAttribute.objects.filter(name='尺寸')
#再去抓這個「product」裡的「attribute」尺寸裡面有什麼值
product_attribute_value = ProductAttributeValue.objects.filter(attribute=attribute[0],product=product)
#抓到資料後,處理一下,把內容轉換成tuple
size_tuple = trimAttribueValue(str(product_attribute_value[0]))
#將tuple設為choices的參數,就差不多大功告成了!
return forms.ChoiceField(label=option.name, required=option.required, choices=size_tuple)
class AddToBasketForm(AddToBasketForm):
OPTION_FIELD_FACTORIES = {
Option.TEXT: _option_text_field,
Option.INTEGER: _option_integer_field,
Option.BOOLEAN: _option_boolean_field,
Option.FLOAT: _option_float_field,
Option.DATE: _option_date_field,
Option.CHOICES: _option_choices_field,
}
def _add_option_field(self, product, option):
option_field = self.OPTION_FIELD_FACTORIES.get(option.type, Option.TEXT) (option, product)#新增product這個參數
self.fields[option.code] = option_field
#這個class一定要覆寫然後pass,不然系統不會抓到你上面覆寫AddToBasketForm的內容喔!
class SimpleAddToBasketForm(SimpleAddToBasketMixin, AddToBasketForm):
pass
```
噹噹!回到商品頁面,應該就可以看到尺寸的選項出現了!

選個L號的加入購物車吧!

大功告成!

---
最後附上這個專案我的github
https://github.com/Felice1004/onehalf-clothing-platform/
如有任何錯誤請不吝賜教!
© Felice Hsiao 2021