使用corsheaders包跨域
设置参考:https://blog.csdn.net/m0_62520968/article/details/124376513?spm=1001.2014.3001.5501https://blog.csdn.net/m0_62520968/article/details/124376513?spm=1001.2014.3001.5501
Django后端,rest_framework+token登录验证,使用默认用户表
建立模型models.py
from django.db import models
from io import BytesIO
from PIL import Image
from django.core.files import File
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField() # 仅包含数字字母下划线,常用于网址
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
def get_absolute_url(self):
return f'/{self.slug}/'
class Product(models.Model):
category = models.ForeignKey(Category, related_name='products', on_delete=models.CASCADE)
name = models.CharField(max_length=255)
slug = models.SlugField()
description = models.TextField(blank=True, null=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
image = models.ImageField(upload_to='uploads/', blank=True, null=True)
thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True)
date_add = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-date_add',)
def __str__(self):
return self.name
def get_absolute_url(self):
return f'/{self.category.slug}/{self.slug}/'
def get_image(self):
if self.image:
return 'http://127.0.0.1:8000' + self.image.url
return ''
def get_thumbnail(self):
if self.thumbnail:
return 'http://127.0.0.1:8000' + self.thumbnail.url
else:
if self.image:
self.thumbnail = self.make_thumbnail(self.image)
self.save()
return 'http://127.0.0.1:8000' + self.thumbnail.url
else:
return ''
def make_thumbnail(self, image, size=(300, 200)):
img = Image.open(image)
img.convert('RGB')
img.thumbnail(size)
thumb_io = BytesIO()
img.save(thumb_io, 'JPEG', quality=85)
thumbnail = File(thumb_io, name=image.name)
return thumbnail
模型序列化serializers.py
from rest_framework import serializers
from .models import *
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = (
'id',
'name',
'get_absolute_url',
'description',
'price',
'get_image',
'get_thumbnail',
)
class CategorySerializer(serializers.ModelSerializer):
# Category是Product的外键,这样可以调用Product序列化的数据
products = ProductSerializer(many=True)
class Meta:
model = Category
fields = (
'id',
'name',
'get_absolute_url',
'products',
)
视图函数view.py
from django.db.models import Q
from django.http import Http404
from django.shortcuts import render
from .serializers import *
from rest_framework.views import APIView
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import *
# Create your views here.
class LatestProductList(APIView):
def get(self, request, format=None):
products = Product.objects.all()
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
class ProductDetail(APIView):
def get_object(self, category_slug, product_slug):
try:
return Product.objects.filter(category__slug=category_slug).get(slug=product_slug)
except Product.DoesNotExist:
raise Http404
def get(self, request, category_slug, product_slug, format=None):
product = self.get_object(category_slug, product_slug)
serializer = ProductSerializer(product)
return Response(serializer.data)
class CategoryDetail(APIView):
def get_object(self, category_slug):
try:
return Category.objects.get(slug=category_slug)
except Product.DoesNotExist:
raise Http404
def get(self, request, category_slug, format=None):
category = self.get_object(category_slug)
serializer = CategorySerializer(category)
return Response(serializer.data)
@api_view(['POST'])
def search(request):
query = request.data.get('query', '')
if query:
products = Product.objects.filter(Q(name__icontains=query) | Q(description__icontains=query))
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
else:
return Response({'products': []})
主urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static # static函数添加静态文件
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('djoser.urls')),
path('api/v1/', include('djoser.urls.authtoken')),
path('api/v1/', include('products.urls'), name='products'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
应用urls.py
from django.urls import path, include
from products import views
urlpatterns = [
path('latest-products/', views.LatestProductList.as_view(), name='latest-products'),
path('products/search/', views.search),
path('products///', views.ProductDetail.as_view()),
path('products//', views.CategoryDetail.as_view()),
]
Vue3:
图标引入:public->index.html
main.js设置默认访问url:axios.defaults.baseURL
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import axios from "axios"
axios.defaults.baseURL = `http://127.0.0.1:8000`
createApp(App).use(store).use(router, axios).mount('#app')
在views创建视图HomeView.vue ProductView.vue......
router->index.js加入设置路由,导入视图,默认进入home,对某个视图进行权限认证增加
meta:{ requireLogin:true } router.beforeEach对每个视图检验权限,无权限的自动进入home,有权限的放行
import HomeView from '../views/HomeView.vue'
import ProductView from "../views/ProductView.vue";
import Category from "@/views/Category";
import Search from "@/views/Search";
import SignUp from "@/views/SignUp";
import Login from "@/views/Login";
import MyAccount from "@/views/MyAccount";
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/:category_slug/:product_slug/',
name: 'productview',
component: ProductView
},
{
path: '/:category_slug',
name: 'Category',
component: Category
},
{
path: '/search',
name: 'Search',
component: Search
},
{
path: '/sign-up',
name: 'SignUp',
component: SignUp
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/myaccount',
name: 'MyAccount',
component: MyAccount,
meta:{
requireLogin:true
}
},]
router.beforeEach((to, from, next)=>{
if (to.matched.some(record=>record.meta.requireLogin) && !store.state.isAuthenticated) {
next({name:'Login', query:{to:to.path}});
}else {
next()
}
})
store->index.js保存状态加入通用方法
state: {
isAuthenticated:false,
token:'',
isLoading:false,
},
getters: {
},
mutations: {
initializeStore(state){
if (localStorage.getItem('token')){
state.token = localStorage.getItem('token')
state.isAuthenticated = true
}else {
state.token= ''
state.isAuthenticated = false
}
},
//设置加载框
setIsLoading(state,status){
state.isLoading = status
},
setToken(state, token){
state.token = token
state.isAuthenticated = true
},
removeToken(state, token){
state.token = ''
state.isAuthenticated = false
},
},
App.vue主模块,类似模板,可当做模板页面使用,其他页面都是在它基础上添加组件,需要返回渲染页面的数据在data->return声明,调用 store->index.js的初始化方法,设置ajax请求头,在'Token '+ token拼接时一定要加一个空格否则验证不通过
axios.defaults.headers.common['Authorization'] = 'Token '+ token
HomeView.vue,mounted部分在网页初始化调用,ajax的get请求及返回数据接收,url使用反引号(英文状态下的1左边的~)。一些自定义组件(例如商品介绍卡片)可以在components部分调用,但是需要在components文件夹自定义(如ProductBox组件)
HomeView.vue
Welcome to jacket
The best jacket store online
Latest products
components->ProductBox.vue,组件声明,
props:{ product:Object }
然后在其他vue里导入,就可以在页面这样调用
v-bind:key="product.id"
v-bind:product="product"/>//绑定
{{product.name}}
${{product.price}}
View details
Search.vue,post方法举例
Search
Search term:"{{query}}"