Day28:28 - 後端&前端 - 按赞收藏

Moni,我是Charlie!

在Day27当中我们完成了recaptcha验证,而今天我们将实作按赞收藏的部分。

================================◉‿◉=================================

首先是後端的部分,按赞收藏需要另外一个资料表,这个资料表会包含以下栏位:

  1. id 自增长,主要栏位
  2. user_id 外键,连接users表
  3. product_id 外键,连接product表
  4. created_time 创建时间
  5. modified_time 修改时间
  6. status 状态

所以我们先新建一个app叫做favorite:

*$ python manage.py startapp favorite

接着在settings.py当中新增INSTALLED_APPS:

INSTALLED_APPS = [
….
    'users',
    'login',
    'resetPWD',
    'favorite'...
]

并在favorite APP当中建立models:

from django.db import models
from users.models import User
from product.models import Product

# Create your models here.

class Favorite(models.Model):
	id = models.AutoField(primary_key = True)
	user = models.ForeignKey(User,on_delete = models.CASCADE,verbose_name = "用户")
	product = models.ForeignKey(Product,on_delete = models.CASCADE,verbose_name = "商品")
	created_time = models.DateTimeField(auto_now = True,verbose_name = "创建时间")
	modified_time = models.DateTimeField(auto_now = True,verbose_name = "修改时间")
	status = models.IntegerField(verbose_name = "状态")

	class Meta:
		db_table = "favorite"

并使用指令迁移:

$ python manage.py makemigrations favorite
$ python manage.py migrate favorite

接着建立urls.py,并且修改keyboardmarket\urls.py,新增favorite app url:

keyboardmarket\urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    url('favorite',include('favorite.urls')),
    url('reset',include('resetPWD.urls')),
    url('usercart',include('usercart.urls')),
    url('userorder',include('userorder.urls')),
    url('user',include('users.urls')),
    url('login',include('login.urls')),
    url('product',include('product.urls')),
]


favorite\urls.py

from django.conf.urls import url
from . import views

urlpatterns = [
	url(r'^$',views.favorite)
]

favorite APP会处理三种请求:

  1. POST 处理按赞请求,当使用者按下赞会打此API,此API会判断按赞状态,如果没有此笔纪录则新增按赞,如果有此笔纪录且状态为1则取消,如果有此笔纪录且状态为0则按赞
  2. GET 返回按赞物品,参数为username
  3. GET + product id 返回该商品该用户的按赞资料

所以在views.py当中建立基本程序码:

from tools.login_check import logincheck
from tools.R import R
from favorite.models import Favorite
from users.models import User
from product.models import Product
from tools.db import FavoriteStatus
import json

# Create your views here.

@logincheck("GET","POST")
def favorite(request,productID = None):
	if request.method == "GET" and productID:
		pass
	if request.method == "GET":
		pass
	if request.method == "POST":
		pass
	return R.methodNotAllowed("method not allowed")

接着在tools\db.py当中建立FavoriteStatus:

class FavoriteStatus(Enum):
	deactivate = 0
	activate = 1

然後在Favorite model建立toJson方法:

def toJson(self):
	data = {}
	data["id"] = self.id
	data["username"] = self.user.name
	data["product"] = self.product.toJson()
	data["created_time"] = self.created_time
	data["modified_time"] = self.modified_time
	data["status"] = self.status
	return data

先修改GET的部分,GET会读到username的参数,如果有读到的话就会查询按赞纪录,并返回资料:

if request.method == "GET":
	req = request.GET
	if "username" not in req:
		return R.badRequest("username does not exist")
	username = req["username"]
	user = User.objects.filter(name = username)
	if not user:
		return R.badRequest("User not found")
	user = user[0]
	favorites = Favorite.objects.filter(user = user).filter(status = FavoriteStatus.activate.value)
	favorites = [i.toJson() for i in favorites]
	return R.ok(favorites)

再来是POST的部分,这部分需要判断的比较多,如果没有此笔资料的话要新增按赞资料,如果有此笔资料的话如果状态为1就更改为状态为0,如果状态为0则更改状态为1:

if request.method == "POST":
	req = request.body
	data = json.loads(req)
	if "username" not in data or "pid" not in data:
		return R.badRequest("not enough parameters!")
	username = data["username"]
	user = User.objects.filter(name = username)
	if not user:
		return R.badRequest("User not found")
	user = user[0]
	pid = data["pid"]
	product = Product.objects.filter(id = pid)
	if not product:
		return R.badRequest("product does not exist!")
	product = product[0]
	favorite = Favorite.objects.filter(user = user).filter(product = product)
	if not favorite:
		favorite = Favorite.objects.create(
			user = user,
			product = product,
			status = FavoriteStatus.activate.value
		)
	else:
		favorite = favorite[0]
		if favorite.status == FavoriteStatus.activate.value:
			favorite.status = FavoriteStatus.deactivate.value
		else:
			favorite.status = FavoriteStatus.activate.value
	favorite.save()
	return R.ok({"status":favorite.status})

再来是个别商品的部分:

if request.method == "GET" and productID:
	req = request.GET
	if "username" not in req:
		return R.badRequest("username does not exist")
	username = req["username"]
	user = User.objects.filter(name = username)
	if not user:
		return R.badRequest("User not found")
	user = user[0]
	product = Product.objects.filter(id = productID)
	if not product:
		return R.badRequest("Product does not exist")
	product = product[0]
	favorite = Favorite.objects.filter(user = user).filter(product = product)
	data = {
		"status":0
	}
	if favorite:
		data["status"] = favorite[0].status
	return R.ok(data)

再来是前端的部分,到apis资料夹当中新增favorite.js,并且新增加载商品按赞状态跟按赞的API:

import { host,port } from '@/apis/constant.js'
import axios from 'axios'

export function getProductFavorite(pid,token,username){
  return axios.get(`http://${host()}:${port()}/favorite/${pid}`,{
    params:{
      "username":username
    },
    headers:{
      "AUTHORIZATION":token
    }
  })
}

export function addFavorite(pid,token,username){
  return axios.post(`http://${host()}:${port()}/favorite`,{
    "username":username,
    "pid":pid
  },{
    headers:{
    	"AUTHORIZATION":token
    }
  })
}

到productDetail中新增按赞按纽:

<b-button variant="info">
                  {{ favoriteText }}
</b-button>

并在created方法当中新增获取商品按赞状态的程序码:

var token = window.localStorage.getItem("token")
var username = window.localStorage.getItem("username")
if(token == null || username == null){
  this.favoriteText = "登入以按赞"
}else{
  getProductFavorite(pid,token,username).then((response) => {
    if(response.data.code == STATUS_OK){
      var status = response.data.data.status
      this.favoriteText = status == 0 ? "按赞" : "已按赞" 
    }
  })
}

接着在按赞按钮上新增onclick function:

<b-button variant="info" @click="addToFavorite">
  {{ favoriteText }}
</b-button>

并且新增addToFavorite方法,打後端的按赞API,并且根据状态显示不同按钮文字:

addToFavorite(){
  var pid = this.$route.params.pid
  var username = window.localStorage.getItem("username")
  var token = window.localStorage.getItem("token")
  if(username == null || token == null){
    this.$fire({type:"error",text:"请登入!"}).then(() => {
      location.href = "/#/login"
    })
  }else{
    addFavorite(pid,token,username).then((response) => {
      if(response.data.code == STATUS_OK){
        this.favoriteText = response.data.data.status == 0 ? "按赞" : "已按赞"
      }
    })
  }
},

就可以测试看看是否可以按赞了:
https://ithelp.ithome.com.tw/upload/images/20211012/20141666mDMAvmFuOX.png

接下来是按赞好物的部分,在headers当中新增:

<b-nav-item-dropdown text="会员" right>
	<b-dropdown-item href="/#/login" v-show="!isLogin">登入</b-dropdown-item>
	<b-dropdown-item href="/#/register" v-show="!isLogin">注册</b-dropdown-item>
	<b-dropdown-item href="/#/self" v-show="isLogin">个人资料</b-dropdown-item>
  <b-dropdown-item href="/#/favorite" v-show="isLogin">按赞好物</b-dropdown-item>
	<b-dropdown-item href="/#/order" v-show="isLogin">订单</b-dropdown-item>
	<b-dropdown-item v-show="isLogin" @click="logout">登出</b-dropdown-item>
</b-nav-item-dropdown>

另外新增components\favorite.vue,做出模板:

<template>
	<html lang="zh-Hant-TW">
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
		<link rel="icon" type="image/x-con" href="@/assets/favicon.ico">
		<div id="app">
			<headerComponent></headerComponent>
			<div id="favoriteHeader" style="width:100%;height:100px;background-image: linear-gradient(to right,#C9C9C9,#F2FFFF,#00E6E6);padding-top: 40px;">
				<h3>按赞好物</h3>
			</div>
			<b-row>
				<b-col cols="2" v-for="item in items" :key="item.id" style="margin: 20px;border:1px solid #5B5B5B;padding: 10px;text-align: center;box-shadow:3px 3px 12px #4F4F4F;border-radius: 30px;">
					<a :href="'/#/productDetail/' + item.product.id">
						<img :src="'http://localhost:8000' + item.product.img" alt="" style="width: 200px;height: 200px;">
					</a>
					<h4 class="productTitle">
						{{ item.product.name }}
					</h4>
					<h5 class="productPrice">
						{{ item.product.price }}
					</h5>
					<div>
						<b-button variant="danger" @click="cancel(item.product.id)">
							取消按赞
						</b-button>
					</div>
				</b-col>
			</b-row>
		</div>

	</html>
</template>

<script>
  export default{
    name: "favoritePage",
    components:{
      'headerComponent':() => import('@/components/header.vue')
    },
    data(){
      return {
        items:[
          {
            "id":1,
            "username":"admin123",
            "product":{
              "id":3,
              "name":"耳机",
              "price":123,
              "stored_amount":12,
              "img":"/media/productImage/airpod.png"
            }
          }
        ]
      }
    },
    methods:{
      cancel(data){
        console.log(data)
      }
    }
  }
</script>

呈现出来的页面:
https://ithelp.ithome.com.tw/upload/images/20211012/20141666YIS3b34GaP.png

在favorite.js当中新增getAllFavorite方法:

export function getAllFavorite(username,token){
  return axios.get(`http://${host()}:${port()}/favorite`,{
    "username":username
  },{
    headers:{
      "AUTHORIZATION":token
    }
  })
}

并新增created方法,使用getAllFavorite方法取得资料:

created(){
  var token = window.localStorage.getItem("token")
  var username = window.localStorage.getItem("username")
  if(token == null || username == null){
    this.$fire({type:"error",text:"请登入"}).then(() => {
      location.href = "/#/login"
    })
  }
  getAllFavorite(username,token).then((response) => {
    if(response.data.code == STATUS_OK){
      this.items = response.data.data
    }else{
      this.$fire({type:"error",text:response.data.data})
    }
  })
},

接着新增取消方法,这里使用的是在商品详情页面用过的addFavorite API,让後端自动判定状态:

cancel(data){
  var token = window.localStorage.getItem("token")
  var username = window.localStorage.getItem("username")
  var pid = data
  addFavorite(pid,token,username).then((response) => {
    if(response.data.code == STATUS_OK){
      this.items.forEach((item,index) => {
        if(item.product.id == pid){
          this.items.splice(index,1)
        }
      })
    }
  })
}

即可测试是否可以按赞跟收回,还有取消後会不会从按赞好物清单中消失。

================================◉‿◉=================================

Day28结束了!在今天我们完成了按赞跟取消按赞还有收藏的机制,而明天我们将加上像是Facebook分享、Line分享的按纽,See ya next day!


<<:  Day27 Router useParams 小实作

>>:  Log Agent - Fluent Bit Input元件 与 Tail浅谈

建立第一个单元测试(golang)-2(Day21)

接下来就是我要将测试放入现在正在进行的api中了 在这次的测试中,我想测试mRequest.Get(...

Day23 - 针对 Metasploitable 3 进行渗透测试(4) - 认识 Metasploit

复习 透过 nmap 扫描 port 利用版本查询已知漏洞 透过人家撰写好 exploit 进行攻击...

Progressive Web App 开箱看看渐进式增强到底有多强 (1)

Progressive Web App (PWA) 使用当下最新潮的 Web API,Progres...

DAY 12『 利用安装套件管理工具 ( CocoaPods ) 下载资料库( Realm Studio ) 』

打开 terminal ( command + space )输入以下指令安装 CocoaPods...

Day 16:「宝藏,都藏在那里了!」- Tailwind JIT 模式

JIT、JIT 的叫了这麽多天,终於就是今天了! 今天就是要来讲解 JIT 模式哦~~ JIT 模...