創建產品管理前端頁面
建立一個新的頁面,用來管理產品,包含新增、編輯、刪除、查看產品的功能
- 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
| 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
| @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
| ... 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
了,只需回傳 loading
和 success
狀態即可
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) => {
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]);
|
後端實作 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) return Response(serializer.data)
|
另外也要製作在創建商品時,要預設圖片的路徑,要解決產品預設沒有圖片的問題,這邊就把產品預設圖放到 /backend/static/images/
資料夾下,並在 models.py
中設定該 image
預設路徑
1 2 3 4
| class Product(models.Model): ... image = models.ImageField(null=True,blank=True,default='/placeholder.png')
|
這樣就可以每次在創建商品時,都能先有一張預設圖片
以及查看 Postman
的回傳結果也能看到預設的產品圖片資料 "image": "/images/placeholder.png",
那與其創建一個 Form 來新增產品,取而代之得是可直接導向到編輯產品的頁面,並自動填入產品資料
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=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
若您覺得這篇文章對您有幫助,歡迎分享出去讓更多人看到⊂◉‿◉つ~
留言版