Django + React 電商專案練習 [???] - 後台實作 - 產品管理 CRUD 頁面

Posted by Young on 2022-12-29
Estimated Reading Time 5 Minutes
Words 1k In Total

創建產品管理前端頁面

建立一個新的頁面,用來管理產品,包含新增、編輯、刪除、查看產品的功能

  • import 先前寫好的 listProduct 的 Action
  • 調用 ProductList 的 state,取得所有產品
  • map 迭代遍歷 products 陣列,並顯於產品列表上加上新增、刪除按鈕
    -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// ProductListPage.js
import { listProducts } from "../actions/productActions";
...
const ProductList = useSelector((state) => state.ProductList);
const { loading, error, products } = ProductList;
...
return(
...
<Row className='align-items-center'>
<Col className='text-right'>
<Button className='my-3' variant="success" onClick={createProductHandler}>
<i className='fas fa-plus'></i> 新增產品
</Button>
</Col>
<Col className='text-right'>
<Button className='my-3' variant="danger" onClick={deleteProductHandler}>
<i className='fas fa-plus'></i> 刪除產品
</Button>
</Col>
</Row>
...
<Table striped hover responsive className='table-sm table-light'>
...
{products.map((product) => {
...
})}
</Table>
)

後端實作 CRUD API - 刪除

一樣先建立對應的 views 實作功能,再建立對應的 urls 給 前端的 axios 調用

views.py

1
2
3
4
5
6
7
# product_views.py
@api_view(['DELETE'])
@permission_classes([IsAdminUser])
def deleteProduct(request,id):
product = Product.objects.get(id=id)
product.delete()
return Response('產品刪除成功!')

urls.py

1
2
3
# product_urls.py
...
path('delete/<str:id>', views.deleteProduct,name="deleteProduct"),

前端實作刪除功能

constants

1
2
3
export const PRODUCT_DELETE_REQUEST = "PRODUCT_DELETE_REQUEST";
export const PRODUCT_DELETE_SUCCESS = "PRODUCT_DELETE_SUCCESS";
export const PRODUCT_DELETE_FAIL = "PRODUCT_DELETE_FAIL";

reducers

所有刪除相關動作就不需要回傳 product: action.payload了,只需回傳 loadingsuccess 狀態即可

1
2
3
4
5
6
7
8
9
10
11
12
13
export const ProductDeleteReducer = (state = {}, action) => {
// 刪除產品
switch (action.type) {
case PRODUCT_DELETE_REQUEST:
return { loading: true };
case PRODUCT_DELETE_SUCCESS:
return { loading: false, success: true }; // 刪除不需要回傳資料
case PRODUCT_DELETE_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};

actions

由於刪除產品需要 admin 權限,所以要在 actions 中加入 token 來驗證

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
export const deleteProduct = (id) => async (dispatch, getState) => {
// action 的函式命名一般都是 動詞在前+名詞

try {
dispatch({
type: PRODUCT_DELETE_REQUEST,
});

const {
userLogin: { userInfo },
} = getState();

const config = {
headers: {
"Content-type": "application/json",
Authorization: `Bearer ${userInfo.token}`,
},
};

const { data } = await axios.delete(`/api/products/delete/${id}`, config);

dispatch({
type: PRODUCT_DELETE_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: PRODUCT_DELETE_FAIL,
payload: error.response && error.response.data.detail ? error.response.data.detail : error.message,
});
}
};

前端頁面功能實作

每當刪除完產品時,應自動重新載入產品列表,所以這邊將 useEffect 加入 successDelete 來監聽是否刪除成功,若成功就「重新載入產品列表」。

1
2
3
4
5
6
const productDelete = useSelector((state) => state.productDelete);
const { loading: loadingDelete, error: errorDelete, success: successDelete } = productDelete;

useEffect(() => {
dispatch(listProducts());
}, [dispatch, successDelete]); // 這邊放入 successDelete,因為要在刪除使用者後自動重新載入頁面

後端實作 CRUD API - 新增

views.py

這邊可以先創立一個測試用的產品資料,並至 Postman 測試是否能成功發送此新增產品的請求,直接先填入固定的資料,之後再來改成動態填入的資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@api_view(['POST'])
@permission_classes([IsAdminUser])
def createProduct(request):
user = request.user
data = request.data
product = Product.objects.create(
user=user,
name='Sample Name',
price=0,
brand='Sample Brand',
countInStock=0,
category='Sample Category',
description = 'Sample Description',
)
serializer = ProductSerializer(product, many=False)
# print("Request Data:", serializer.data)
return Response(serializer.data)

另外也要製作在創建商品時,要預設圖片的路徑,要解決產品預設沒有圖片的問題,這邊就把產品預設圖放到 /backend/static/images/ 資料夾下,並在 models.py 中設定該 image 預設路徑

1
2
3
4
# models.py
class Product(models.Model):
...
image = models.ImageField(null=True,blank=True,default='/placeholder.png')

這樣就可以每次在創建商品時,都能先有一張預設圖片

image_placeholder

以及查看 Postman 的回傳結果也能看到預設的產品圖片資料 "image": "/images/placeholder.png",

postman_create_product

那與其創建一個 Form 來新增產品,取而代之得是可直接導向到編輯產品的頁面,並自動填入產品資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# product_views.py
@api_view(['POST'])
@permission_classes([IsAdminUser])
def createProduct(request):
user = request.user
data = request.data
product = Product.objects.create(
user=user,
name=data['name'],
price=data['price'],
brand=data['brand'],
countInStock=data['countInStock'],
category=data['category'],
description=data['description'],
)
serializer = ProductSerializer(product,many=False)
return Response(serializer.data)

urls.py

1


若您覺得這篇文章對您有幫助,歡迎分享出去讓更多人看到⊂◉‿◉つ~


留言版