开源web框架django知识总结(十九)

开源web框架django知识总结(十九)

阿尔法商城(购物车)

购物车存储方案

新建apps->carts

  • 必须是用户登录状态下,才可以保存购物车数据。
  • 用户对购物车数据的操作包括:增、删、改、查、全选等等
  • 每个用户的购物车数据都要做唯一性的标识。

1. 购物车存储方案

1.存储数据说明

  • 如何描述一条完整的购物车记录?
    • 用户,选择了两个 iPhone8 添加到了购物车中,状态为勾选
  • 一条完整的购物车记录包括:用户商品数量勾选状态
  • 存储数据:user_id、sku_id、count、selected

2.存储位置说明

  • 购物车数据量小,结构简单,更新频繁,所以我们选择内存型数据库Redis进行存储。
  • 存储位置:dev.py文件中Redis数据库 5号库
"carts": { # 购物车
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://192.168.42.128:6379/5",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },

3.存储类型说明

  • 提示:我们很难将用户、商品、数量、勾选状态存放到一条Redis记录中。所以我们要把购物车数据合理的分开存储。
  • 用户、商品、数量:hash
    • carts_user_id: {sku_id1: count, sku_id3: count, sku_id5: count, ...}
  • 勾选状态:set
    • 只将已勾选商品的sku_id存储到set中,比如,1号和3号商品是被勾选的。
    • selected_user_id: [sku_id1, sku_id3, ...]
      注释:Redis Smembers 命令返回集合中的所有的成员。 不存在的集合 key 被视为空集合。

4.存储逻辑说明

  • 当要添加到购物车的商品已存在时,对商品数量进行累加计算。
  • 当要添加到购物车的商品不存在时,向hash中新增field和value即可。

==============================================

购物车管理

添加购物车

提示:在商品详情页添加购物车使用局部刷新的效果。

1. 添加购物车接口设计和定义

1.请求方式

选项 方案
请求方法 post
请求地址 /carts/

2.请求参数:JSON

参数名 类型 是否必传 说明
sku_id int 商品SKU编号
count int 商品数量
selected bool 是否勾选

3.响应结果:JSON

字段 说明
code 状态码
errmsg 错误信息

2. 添加购物车后端逻辑实现

1.接收和校验参数 carts.views.py

import json

from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django_redis import get_redis_connection

from utils.views import login_required  # 注意修改导包路径
from goods.models import SKU
# Create your views here.

class CartsView(View):

 # 把sku商品加入购物车
    @method_decorator(login_required)
    def post(self, request):
        # 1、提取参数
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')
        count = data.get('count')
        selected = data.get('selected', True)

        # 2、校验参数
        if not all([sku_id, count]):
            return JsonResponse({'code': 400, 'errmsg': '缺少参数!'})

        if not isinstance(selected, bool):
            return JsonResponse({'code': 400, 'errmsg': '参数有误!'})

        # 3、判断是否登陆
        user = request.user
        if user.is_authenticated:
            # 4、登陆写入redis
            conn = get_redis_connection('carts')
            # 4.1 记录sku商品数量——carts_<user_id> : {sku_id: count}
            # conn.hmset('carts_%s'%user.id, {sku_id:count}) # 我们不能使用该函数,因为他会覆盖原有数据
            conn.hincrby('carts_%s'%user.id, sku_id, amount=count) # 把sku商品数量增加count,如果不存在则新建
            # 4.2 记录选中状态——selected_<user_id> : [sku_id]
            if selected:
                conn.sadd('selected_%s'%user.id, sku_id)

            return JsonResponse({'code': 0, 'errmsg': 'ok'})

=========================================

展示购物车

1. 展示购物车接口设计和定义

1.请求方式

选项 方案
请求方法 GET
请求地址 /carts/

**2.请求参数:**无

3.响应结果:HTML cart.html

4.后端接口定义 carts.views.py

class CartsView(View):

    def get(self, request):
        # 0、初始化一个空字典,用于保存sku购物车数据,其格式和cookie购物车格式一样
        cart_dict = {} # {1: {count:xx, selected:xx}}

        user = request.user
        if user.is_authenticated:
            # 1、用户登陆,则从redis中读取sku商品信息
           pass

2. 展示购物车后端逻辑实现

1.查询Redis购物车

2.查询购物车SKU信息

3.返回响应数据

 # 展示购物车
    @method_decorator(login_required)
    def get(self, request):
        # 0、初始化一个空字典,用于保存sku购物车数据,其格式和cookie购物车格式一样
        cart_dict = {}  # {1: {count:xx, selected:xx}}

        user = request.user
        if user.is_authenticated:
            # 1、用户登陆,则从redis中读取sku商品信息
            conn = get_redis_connection('carts')
            # 2、sku商品数量、是否选中
            # cart_redis_dict = {b'1': b'8'}
            cart_redis_dict = conn.hgetall('carts_%s' % user.id)
            # cart_redis_selected = [b'1']
            cart_redis_selected = conn.smembers('selected_%s' % user.id) #smembers返回集合中的所有的成员。 不存在的集合 key 被视为空集合
            #将cart_redis_dict与cart_redis_selected合并
            for k, v in cart_redis_dict.items(): #字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。
                # k: b'1'; v: b'8'
                cart_dict[int(k)] = {
                    'count': int(v),
                    'selected': k in cart_redis_selected  # b'1' in [b'1']返回True
                }

        cart_skus = []
        # 3、构建响应返回
        for k, v in cart_dict.items():
            # k: sku_id; v: {count:xx, selected: xx}
            sku = SKU.objects.get(pk=k)
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'count': v['count'],
                'selected': v['selected'],
                'price': sku.price,
                'default_image_url': sku.default_image_url.url,
                'amount': sku.price * v['count']
            })

        return JsonResponse({'code': 0, 'errmsg': 'ok', 'cart_skus': cart_skus})

==============================================

修改购物车

提示:在购物车页面修改购物车使用局部刷新的效果。

1. 修改购物车接口设计和定义

1.请求方式

选项 方案
请求方法 PUT
请求地址 /carts/

2.请求参数:JSON

参数名 类型 是否必传 说明
sku_id int 商品SKU编号
count int 商品数量
selected bool 是否勾选

3.响应结果:JSON

字段 说明
sku_id 商品SKU编号
count 商品数量
selected 是否勾选

4.后端接口定义

# 修改购物车
    def put(self, request):

        user = request.user
        if user.is_authenticated:
            # 登陆,修改redis
          pass

2. 修改购物车后端逻辑实现

1.接收和校验参数

2.修改Redis购物车

# 修改购物车
    @method_decorator(login_required)
    def put(self, request):
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')
        count = data.get('count')
        selected = data.get('selected', True)

        user = request.user
        if user.is_authenticated:
            # 登陆,修改redis
            conn = get_redis_connection('carts')
            conn.hmset('carts_%s' % user.id, {sku_id: count})  # 覆盖写入
            if selected:
                conn.sadd('selected_%s' % user.id, sku_id) #选中,sadd增加
            else:
                conn.srem('selected_%s' % user.id, sku_id) #未选中,srem删除
            return JsonResponse({
                'code': 0,
                'errmsg': 'ok',
                'cart_sku': {
                    'id': sku_id,
                    'count': count,
                    'selected': selected
                }
            })

=======================================

删除购物车

提示:在购物车页面删除购物车使用局部刷新的效果。

1. 删除购物车接口设计和定义

1.请求方式

选项 方案
请求方法 DELETE
请求地址 /carts/

2.请求参数:JSON

参数名 类型 是否必传 说明
sku_id int 商品SKU编号

3.响应结果:JSON

字段 说明
code 状态码
errmsg 错误信息

4.后端接口定义

# 删除购物车
    def delete(self, request):

        user = request.user
        # 1、已登陆
        if user.is_authenticated:
          pass

2. 删除购物车后端逻辑实现

1.接收和校验参数

2.删除Redis购物车

# 删除购物车
    @method_decorator(login_required)
    def delete(self, request):
        # {"sku_id": xxx}}
        data = json.loads(
            request.body.decode()
        )
        sku_id = data.get('sku_id')

        user = request.user
        # 已登陆
        if user.is_authenticated:
            conn = get_redis_connection('carts')
            # 1.1 删除购物车哈希数据——carts_user_id :  {sku_id : count}
            # Hdel 命令用于删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略。
            conn.hdel('carts_%s' % user.id, sku_id)
            # 1.2 删除集合中的sku_id
            # Srem 命令用于移除集合中的一个或多个成员元素,不存在的成员元素会被忽略。
            #https://www.runoob.com/redis/redis-commands.html
            conn.srem('selected_%s' % user.id, sku_id)
            return JsonResponse({'code': 0, 'errmsg': 'ok'})

===========================================

全选购物车

提示:在购物车页面修改购物车使用局部刷新的效果。

1. 全选购物车接口设计和定义

1.请求方式

选项 方案
请求方法 PUT
请求地址 /carts/selection/

2.请求参数:JSON

参数名 类型 是否必传 说明
selected bool 是否全选

3.响应结果:JSON

字段 说明
code 状态码
errmsg 错误信息

4.后端接口定义

class CartSelectAllView(View):

    # 设置全选/全取消
    def put(self, request):
        # 1、获取参数
        if user.is_authenticated:
            # 已登陆:把所有的sku_id加入/删除到selected_user_id的集合中
           pass

2. 全选购物车后端逻辑实现

1.接收和校验参数

2.全选Redis购物车

class CartSelectAllView(View):

    # 设置全选/全取消
    @method_decorator(login_required)
    def put(self, request):
        # 1、获取参数
        data = json.loads(request.body.decode())
        selected = data.get("selected") # True or False

        user = request.user
        if user.is_authenticated:
            # 已登陆:把所有的sku_id加入/删除到selected_user_id的集合中
            conn = get_redis_connection('carts')
            # 1、获取用户的购物车数据
            # {b'1': b'5'}
            cart_dict = conn.hgetall('carts_%s'%user.id)
            # [b'1', b'2']
            sku_ids = cart_dict.keys()
            # 2、设置全/全取消
            if selected:
                conn.sadd('selected_%s'%user.id, *sku_ids)
            else:
                conn.srem('selected_%s'%user.id, *sku_ids)
            return JsonResponse({'code': 0, 'errmsg': 'ok'})

=============================================

展示商品页面简单购物车

需求:用户鼠标悬停在商品页面右上角购物车标签上,以下拉框形式展示当前购物车数据。

1. 简单购物车数据接口设计和定义

1.请求方式

选项 方案
请求方法 GET
请求地址 /carts/simple/

**2.请求参数:**无

3.响应结果:JSON

字段 说明
code 状态码
errmsg 错误信息
cart_skus[ ] 简单购物车SKU列表
id 购物车SKU编号
name 购物车SKU名称
count 购物车SKU数量
default_image_url 购物车SKU图片

4.后端接口定义

class CartsSimpleView(View):
    """商品页面右上角购物车"""
 
    def get(self, request):
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 用户已登录,查询Redis购物车
            pass
        # 构造简单购物车JSON数据
        pass

2. 简单购物车数据后端逻辑实现

1.查询Redis购物车

2.构造简单购物车JSON数据

class CartsSimpleView(View):

    def get(self, request):
        # 读取购物车数据,把具体的sku商品信息返回
        user = request.user

        # {1: {"count":xx, "selected":xxx}}
        cart_dict = {} # 购物车数据——格式和cookie格式是一样的

        if user.is_authenticated:
            # 已登陆,从redis中读商品数据
            conn = get_redis_connection('carts')
            # {b'1': b'4'}
            carts_redis = conn.hgetall('carts_%s'%user.id)
            # [b'1']
            carts_selected = conn.smembers('selected_%s'%user.id)
            for k,v in carts_redis.items():
                # k: b'1'; v: b'4'
                cart_dict[int(k)] = {
                    'count': int(v),
                    'selected': k in carts_selected
                }


        # 构造响应数据
        cart_skus = []
        for k,v in cart_dict.items():
            # k: sku_id; v: {"count":xx, "selected":xxx}
            sku = SKU.objects.get(pk=k)
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'count': v['count'],
                'default_image_url': sku.default_image_url.url
            })

        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'cart_skus': cart_skus
        })

carts.urls.py

from django.urls import re_path
from .views import *

urlpatterns = [
    re_path(r'^carts/$', CartsView.as_view()),
    re_path(r'^carts/selection/$', CartSelectAllView.as_view()),
    re_path(r'^carts/simple/$', CartsSimpleView.as_view()),
]

修改前端文件

1、1.html 修改动态加载“简单购物车”显示

<!--      <div class="guest_cart fr">
            <a href="#" class="cart_name fl">我的购物车</a>
            <div class="goods_count fl" id="show_count">15</div>

            <ul class="cart_goods_show">
                <li>
                    <img src="#" alt="商品图片">
                    <h4>商品名称手机</h4>
                    <div>4</div>
                </li>
                <li>
                    <img src="#" alt="商品图片">
                    <h4>商品名称手机</h4>
                    <div>5</div>
                </li>
                <li>
                    <img src="#" alt="商品图片">
                    <h4>商品名称手机</h4>
                    <div>6</div>
                </li>
                <li>
                    <img src="#" alt="商品图片">
                    <h4>商品名称手机</h4>
                    <div>6</div>
                </li>
            </ul>
        </div>  -->
        
            <div class="guest_cart fr">
            <a href="#" class="cart_name fl">我的购物车</a>
            <div class="goods_count fl" id="show_count">[[ cart_total_count ]]</div>
            <ul class="cart_goods_show">
                <li v-for="sku in carts">
                    <img :src="sku.default_image_url" alt="商品图片">
                    <h4>[[ sku.name ]]</h4>
                    <div>[[ sku.count ]]</div>
                </li>
            </ul>
            </div>

修改

  • 2、修改detail.js

    改19行

    mounted: function()中添加:this.get_hot_goods();

    把注释部分打开

    var vm = new Vue({
        el: '#app',
        delimiters: ['[[', ']]'],
        data: {
            host,
            username: '',  //改
            user_id: sessionStorage.user_id || localStorage.user_id,
            token: sessionStorage.token || localStorage.token,
            tab_content: {
                detail: true,
                pack: false,
                comment: false,
                service: false
            },
            sku_id: '',
            sku_count: 1,
            sku_price: price,
            cart_total_count: 0, // 购物车总数量
            carts: [], // 购物车数据
            hot_skus: [], // 热销商品
            cat: cat, // 商品类别
            comments: [], // 评论信息
            score_classes: {
                1: 'stars_one',
                2: 'stars_two',
                3: 'stars_three',
                4: 'stars_four',
                5: 'stars_five',
            }
        },
        computed: {
            sku_amount: function(){
                return (this.sku_price * this.sku_count).toFixed(2);
            }
        },
        mounted: function(){
            // 获取cookie中的用户名 (增)
        	this.username = getCookie('username');
            // 添加用户浏览历史记录
            this.get_sku_id();
    
            axios.post(this.host+'/browse_histories/', {
                sku_id: this.sku_id
            },{
                    responseType: 'json',
                    withCredentials:true,
                })
                .then(response=>{
                    console.log(response)
    
                })
                .catch(error=>{
                    console.log(error)
                })
    
            this.get_cart();
            this.get_hot_goods();
            this.get_comments();
        },
        methods: {
             // 退出登录按钮
            logoutfunc: function () {
                var url = this.host + '/logout/';
                axios.delete(url, {
                    responseType: 'json',
                    withCredentials:true,
                })
                    .then(response => {
                        location.href = 'http://192.168.42.128/login.html';//改退出页面404错误
                    })
                    .catch(error => {
                        console.log(error.response);
                    })
            },
            // 控制页面标签页展示
            on_tab_content: function(name){
                this.tab_content = {
                    detail: false,
                    pack: false,
                    comment: false,
                    service: false
                };
                this.tab_content[name] = true;
            },
            // 从路径中提取sku_id
            get_sku_id: function(){
                var re = /^\/goods\/(\d+).html$/;
                this.sku_id = document.location.pathname.match(re)[1];
            },
            // 减小数值
            on_minus: function(){
                if (this.sku_count > 1) {
                    this.sku_count--;
                }
            },
            // 获取用户所有的资料 (增:校验用户是否登陆状态)
            get_person_info: function () {
                var url = 'http://192.168.42.128:8000'+ '/info/';
                console.log(url)
                axios.get(url, {
                    responseType: 'json',
                    withCredentials: true
                })
                    .then(response => {
                        if (response.data.code == 400) {
                            window.location.href = 'http://192.168.42.128/login.html'
                            return
                        }
                        this.username = response.data.info_data.username;
    
                    })
                    .catch(error => {
                        location.href = 'http://192.168.42.128/login.html'
                    })
            },
             // 添加购物车
            add_cart: function(){
                this.get_person_info()
                var url = this.host + '/carts/'
                axios.post(url, {
                        sku_id: parseInt(this.sku_id),
                        count: this.sku_count
                    }, {
                        responseType: 'json',
                        withCredentials: true
                    })
                    .then(response => {
                        alert('添加购物车成功');
                        this.cart_total_count += response.data.count;
                        window.location.reload()   //新增刷新页面,解决购物车数据变更显示bug
                    })
                    .catch(error => {
                        console.log(error);
                    })
            },
           get_cart(){
            let url = this.host + '/carts/simple/';
            axios.get(url, {
                responseType: 'json',
                withCredentials:true,
            })
                .then(response => {
                    this.carts = response.data.cart_skus;
                    this.cart_total_count = 0;
                    for(let i=0;i<this.carts.length;i++){
                        if (this.carts[i].name.length>25){
                            this.carts[i].name = this.carts[i].name.substring(0, 25) + '...';
                        }
                        this.cart_total_count += this.carts[i].count;
                    }
                })
                .catch(error => {
                    console.log(error);
                })
        },
            // 获取热销商品数据(补全)
            get_hot_goods: function(){
                // 请求获取热销商品数据
                var url = this.host+'/hot/'+this.cat + '/'
                axios.get(url, {
                        responseType: 'json',
                        withCredentials: true
                    })
                    .then(response => {
                         this.hot_skus = response.data.hot_skus
                         for(let i=0; i<this.hot_skus.length; i++){
                            this.hot_skus[i].url = '/goods/' + this.skus[i].id + ".html";
                        }
                    })
                    .catch(error => {
                        console.log(error);
                    })
            }
        ,
            // 获取商品评价信息
            get_comments: function(){
    
            }
        }
    });
    

    3、修改cart.js

    var vm = new Vue({
        el: '#app',
         delimiters: ['[[', ']]'],
        data: {
            host,
            username: '',
            user_id: sessionStorage.user_id || localStorage.user_id,
            token: sessionStorage.token || localStorage.token,
            cart: [],
            total_selected_count: 0,
            origin_input: 0 // 用于记录手动输入前的值
        },
        computed: {
            total_count: function(){
                var total = 0;
                for(var i=0; i<this.cart.length; i++){
                    total += (this.cart[i].count);
                    this.cart[i].amount = ((this.cart[i].price) * (this.cart[i].count)).toFixed(2);
                }
                return total;
            },
            total_selected_amount: function(){
                var total = 0;
                this.total_selected_count = 0;
                for(var i=0; i<this.cart.length; i++){
                    if(this.cart[i].selected) {
                        total += ((this.cart[i].price) * (this.cart[i].count));
                        this.total_selected_count += (this.cart[i].count);
                    }
                }
                return total.toFixed(2);
            },
            selected_all: function(){
                var selected=true;
                for(var i=0; i<this.cart.length; i++){
                    if(this.cart[i].selected==false){
                        selected=false;
                        break;
                    }
                }
                return selected;
            }
        },
        mounted: function(){
    
            this.username = getCookie('username')
            // 获取个人信息:  (新增)
            this.get_person_info()
    
            // 获取购物车数据
            axios.get(this.host+'/carts/', {
                    responseType: 'json',
                    withCredentials: true
                })
                .then(response => {
                    this.cart = response.data.cart_skus;
                    for(var i=0; i<this.cart.length; i++){
                        this.cart[i].amount = ((this.cart[i].price) * this.cart[i].count).toFixed(2);
                    }
                })
                .catch(error => {
                    console.log(error.response.data);
                })
        },
        methods: {
            // 退出
            logoutfunc: function(){
                sessionStorage.clear();
                localStorage.clear();
                location.href = '/login.html';
            },
            // 获取用户所有的资料(新增)
            get_person_info: function () {
                var url = this.host + '/info/';
                axios.get(url, {
                    responseType: 'json',
                    withCredentials: true
                })
                    .then(response => {
                        if (response.data.code == 400) {
                            location.href = 'login.html'
                            return
                        }
                        this.username = response.data.info_data.username;
                        this.mobile = response.data.info_data.mobile;
                        this.email = response.data.info_data.email;
                        this.email_active = response.data.info_data.email_active;
                    })
                    .catch(error => {
                        this.set_email = false
                        location.href = 'login.html'
                    })
            },
            // 减少操作
            on_minus: function(index){
                if (this.cart[index].count > 1) {
                    var count = this.cart[index].count - 1;
                    this.update_count(index, count);
                }
            },
            on_add: function(index){
                var count = this.cart[index].count + 1;
                this.update_count(index, count);
            },
            // 删除购物车数据
            on_delete: function(index){
                axios.delete(this.host+'/carts/', {
                        data: {
                            sku_id: this.cart[index].id
                        },
                        responseType: 'json',
                        withCredentials: true
                    })
                    .then(response => {
                        if (response.data.code == 0) {
                            this.cart.splice(index, 1);
                        }
                    })
                    .catch(error => {
                        console.log(error);
                    })
            },
            on_input: function(index){
                var val = parseInt(this.cart[index].count);
                if (isNaN(val) || val <= 0) {
                    this.cart[index].count = this.origin_input;
                } else {
                    // 更新购物车数据
                    axios.put(this.host+'/carts/', {
                            sku_id: this.cart[index].id,
                            count: val,
                            selected: this.cart[index].selected
                        }, {
                            responseType: 'json',
                            withCredentials: true
                        })
                        .then(response => {
                            this.cart[index].count = response.data.count;
                        })
                        .catch(error => {
                            console.log(error)
                        })
                }
            },
            // 更新购物车数据
            update_count: function(index, count){
                axios.put(this.host+'/carts/', {
                        sku_id: this.cart[index].id,
                        count,
                        selected: this.cart[index].selected
                    }, {
                        responseType: 'json',
                        withCredentials: true
                    })
                    .then(response => {
                        this.cart[index].count = response.data.cart_sku.count;
                    })
                    .catch(error => {
                        console.log(error)
                    })
            },
            // 更新购物车数据
            update_selected: function(index) {
                axios.put(this.host+'/carts/', {
                        sku_id: this.cart[index].id,
                        count: this.cart[index].count,
                        selected: this.cart[index].selected
                    }, {
                        responseType: 'json',
                        withCredentials: true
                    })
                    .then(response => {
                        this.cart[index].selected = response.data.cart_sku.selected;
                    })
                    .catch(error => {
                        console.log(error);
                    })
            },
            // 购物车全选
            on_selected_all: function(){
                var selected = !this.selected_all;
                axios.put(this.host + '/carts/selection/', {
                        selected
                    }, {
                        responseType: 'json',
                        withCredentials: true
                    })
                    .then(response => {
                        for (var i=0; i<this.cart.length;i++){
                            this.cart[i].selected = selected;
                        }
                    })
                    .catch(error => {
                        console.log(error);
                    })
            },
        }
    });
    

    查看redis

    (aerf_mall) pyvip@VIP:~/df17/aerf_mall/aerf_mall/aerf_mall/apps$ redis-cli
    127.0.0.1:6379> select 5
    OK
    127.0.0.1:6379[5]> keys *
    1) "carts_1"
    2) "selected_1"
    127.0.0.1:6379[5]> hgetall carts_1
    1) "1"
    2) "2"
    127.0.0.1:6379[5]> smembers selected_1
    1) "1"
    127.0.0.1:6379[5]> keys *
    1) "carts_1"
    2) "selected_1"
    127.0.0.1:6379[5]> smembers selected_1
    1) "1"
    2) "3"
    127.0.0.1:6379[5]> hgetall carts_1
    1) "1"
    2) "2"
    3) "3"
    4) "1"
    127.0.0.1:6379[5]>
    
    

    开源web框架django知识总结(十九)

    祝大家学习python顺利!

上一篇:python测试开发django-184.bootstrap-table 前端分页搜索相关配置


下一篇:python测试开发django-185.bootstrap-table 后端搜索功能实现(queryParams)