169-Django二手交易校园购物系统开发分享

基于Django的二手交易校园购物系统开发分享

📋 项目概述

本项目是一个基于Django框架开发的二手交易校园购物系统,专为校园内的二手商品交易而设计。系统采用B/S架构,提供了完整的电商交易功能,包括用户管理、商品管理、购物车、订单处理、消息系统等核心模块。

基于Django的二手交易校园购物系统

🎯 项目目标

  • 为校园师生提供便捷的二手商品交易平台
  • 实现完整的电商交易流程
  • 提供用户友好的界面和交互体验
  • 确保交易安全和数据完整性

🏗️ 技术架构

后端技术栈

  • 框架: Django 2.0.7
  • 数据库: MySQL (通过PyMySQL连接)
  • ORM: Django ORM
  • 邮件服务: SMTP (163邮箱)
  • 图片处理: Pillow 7.1.1
  • 时区处理: pytz 2018.7
  • 富文本编辑: django-tinymce 2.7.0
  • 后台管理: django-simpleui

前端技术栈

  • HTML/CSS: 原生HTML和CSS
  • JavaScript :
    • jQuery 1.12.4
    • jQuery UI
    • jQuery Cookie
    • 自定义JS组件
  • 字体: FreeMonospaced字体
  • 响应式设计: CSS实现

数据库设计

  • 数据库类型: MySQL
  • 字符集: UTF-8
  • 存储引擎: MyISAM
  • 主要数据表: 用户、商品、购物车、订单、评论、消息等

📁 项目结构

复制代码
tiaozao_shop/
├── apps/                    # 应用模块
│   ├── df_user/            # 用户管理模块
│   ├── df_goods/           # 商品管理模块
│   ├── df_cart/            # 购物车模块
│   ├── df_order/           # 订单管理模块
│   └── utils/              # 工具模块
├── tiaozao_shop/           # 项目配置
│   ├── settings.py         # 项目设置
│   ├── urls.py            # 主路由配置
│   └── wsgi.py            # WSGI配置
├── static/                 # 静态文件
│   ├── css/               # 样式文件
│   ├── js/                # JavaScript文件
│   ├── images/            # 图片资源
│   └── asserts/           # 其他资源
├── templates/              # 模板文件
├── media/                  # 媒体文件
├── requirements.txt        # 依赖包列表
├── manage.py              # Django管理脚本
└── design_169_market.sql  # 数据库结构文件

🔧 核心功能模块

1. 用户管理模块 (df_user)

用户注册与登录
  • 用户注册: 支持用户名、密码、邮箱注册,包含邮箱唯一性验证
  • 用户登录: 集成验证码验证,支持记住用户名功能
  • 密码安全: 使用SHA1加密存储密码
  • 账号退出: 安全退出,清除session信息
用户信息管理
  • 个人信息: 支持头像上传、个人资料编辑
  • 收货地址: 收货人、地址、邮编、手机号管理
  • 实名认证: 证件类型、证件号码、证件图片上传
  • 密码管理: 密码修改、密码重置(邮件发送)
用户信息修改
python 复制代码
@user_decorator.login
def changeInformation(request):
    uid = request.session['user_id']
    user = UserInfo.objects.get(id=uid)
    cart_num = CartInfo.objects.filter(user_id=user.id).count()
    
    context = {
        'page_name': 1,
        'title': '用户中心',
        'user': user,
        'guest_cart': 1,
        'cart_num': cart_num,
    }

    if request.method == "POST":
        logo = request.FILES.get('logo')
        if logo:
            user.ulogo = logo
        else:
            user.ulogo = user.ulogo
        
        user.usex = request.POST.get('sex')
        user.uage = request.POST.get('age')
        user.upersonInf = request.POST.get('personinf')
        user.save()
    
    return render(request, 'df_user/user_changeInformation.html', context)
用户中心信息展示
python 复制代码
@user_decorator.login
def info(request):  # 用户中心
    uid = request.session['user_id']
    user = UserInfo.objects.get(id=uid)
    browser_goods = GoodsBrowser.objects.filter(user=user).order_by("-browser_time")
    cart_num = CartInfo.objects.filter(user_id=int(uid)).count()
    goods_list = []
    
    if browser_goods:
        goods_list = [browser_good.good for browser_good in browser_goods]  # 从浏览商品记录中取出浏览商品
        explain = '最近浏览'
    else:
        explain = '无最近浏览'

    context = {
        'title': '用户中心',
        'page_name': 1,
        'guest_cart': 1,
        'cart_num': cart_num,
        'user_phone': user.uphone,
        'user_address': user.uaddress,
        'user_name': user.uname,
        'user': user,
        'ucheck_passOrfail': user.ucheck_passOrfail,
        'goods_list': goods_list,
        'explain': explain,
    }
    return render(request, 'df_user/user_center_info.html', context)
收货地址管理
python 复制代码
@user_decorator.login
def site(request):
    user = UserInfo.objects.get(id=request.session['user_id'])
    cart_num = CartInfo.objects.filter(user_id=user.id).count()
    
    if request.method == "POST":
        user.ushou = request.POST.get('ushou')
        user.uaddress = request.POST.get('uaddress')
        user.uyoubian = request.POST.get('uyoubian')
        user.uphone = request.POST.get('uphone')
        user.save()
    
    context = {
        'page_name': 1,
        'title': '用户中心',
        'user': user,
        'guest_cart': 1,
        'cart_num': cart_num,
    }
    return render(request, 'df_user/user_center_site.html', context)
密码重置(邮件发送)
python 复制代码
def findpwdView(request):
    context = {
        'title': '重置密码',
    }
    if request.method=="POST":
        username = request.POST.get("username")
        email = request.POST.get("email")
        user = UserInfo.objects.get(uname=username)
        context = {
            'title': '重置密码',
            'user': user,
        }
        if user.uemail == email:
            email_title = "二手商品交易平台-重置密码"
            code = random_str()  # 随机生成的验证码
            request.session["code"] = code  # 将验证码保存到session
            email_body = "您的密码已重置,为了您的账号安全,请勿将密码泄露。新的密码为:{0}".format(code)
            # send_mail的参数分别是 邮件标题,邮件内容,发件箱,收件箱列表,失败静默
            send_status = send_mail(email_title, email_body, 'woaisilaowu@163.com', [email], fail_silently=False)
            code = request.session["code"] # 获取传递过来的验证码
            # 密码加密
            s1 = sha1()
            s1.update(code.encode('utf8'))
            encrypted_pwd = s1.hexdigest()
            user.upwd = encrypted_pwd
            user.save()
            del request.session["code"]  # 删除session
            messages.success(request, "密码已重置,请登录邮箱接受重置密码!")
        else:
            messages.success(request, "用户邮箱与输入邮件不匹配,重置失败!")

        return render(request,"df_user/change_password1.html",context)
    return render(request, "df_user/change_password1.html",context)
用户权限控制
  • 登录验证: 使用装饰器进行登录状态验证
  • 账号封禁: 支持违规账号封禁功能
  • 认证审批: 实名认证审批流程

2. 商品管理模块 (df_goods)

商品展示
  • 首页展示: 按分类展示最新和热门商品
  • 商品列表: 分页展示,支持排序(最新、价格、人气)
  • 商品详情: 完整商品信息展示
  • 商品搜索: 支持标题、内容、简介关键词搜索
商品发布
  • 商品发布: 支持图片上传、价格设置、库存管理
  • 商品分类: 书籍、家电、衣物、出行、乐器、智能
  • 商品编辑: 商品信息修改和更新
商品发布实现
python 复制代码
@user_decorator.login
def publishers(request):
    user = UserInfo.objects.get(id=request.session['user_id'])
    cart_num = CartInfo.objects.filter(user_id=user.id).count()
    typeinfos = TypeInfo.objects.all()
    
    if request.method == "POST":
        gtitle = request.POST.get('title')
        gpic = request.FILES.get('pic')
        gunit = user.uname
        gprice = request.POST.get('price')
        gjianjie = request.POST.get('jianjie')
        gkucun = request.POST.get('kucun')
        gcontent = request.POST.get('content')
        gtype_id = request.POST.get('type_id')
        
        if gtitle=="" or gpic=="" or gprice=="" or gjianjie=="" or gkucun=="" or gcontent=="":
            messages.success(request, "请完整填充信息!")
        elif int(gprice) >=100000:
            messages.success(request, "价格不能大于100000!")
        else:
            GoodsInfo.objects.create(gtitle=gtitle, gpic=gpic, gunit=gunit, gprice=gprice,
                                   gjianjie=gjianjie, gkucun=gkucun, gcontent=gcontent, gtype_id=gtype_id)
            messages.success(request, "发布商品成功")

    context = {
        'page_name': 1,
        'title': '用户中心',
        'user': user,
        'typeinfos': typeinfos,
        'guest_cart': 1,
        'cart_num': cart_num,
    }
    return render(request, 'df_user/user_publishers.html', context)
首页商品展示实现
python 复制代码
def index(request):
    # 查询各个分类的最新4条,最热4条数据
    username = request.session.get('user_name')
    user = UserInfo.objects.filter(uname=username).first()
    
    # 联系客服功能
    username1 = "customer"
    informations = Information.objects.filter()
    # 判断用户是否给客服发过信息
    informations1 = Information.objects.filter(cusername1=username, cusername=username1)
    import datetime
    nowTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')  # 现在
    # 消息回复
    user_name = UserInfo.objects.get(uname=username1)  # 获取当前消息用户信息
    
    if request.method == "POST":
        cusername = user.uname
        cusername1 = user_name.uname
        ccontent_chart = request.POST.get('title')
        cinformation_id = user_name.id
        if ccontent_chart == "":
            messages.success(request, "请输入内容!")
        else:
            Information.objects.create(cusername=cusername, cusername1=cusername1,
                                   ccontent_chart=ccontent_chart, cinformation_id=cinformation_id)
            messages.success(request, "消息发送成功")
            return redirect(reverse("df_goods:index"))
    
    typelist = TypeInfo.objects.all()
    # _set 连表操作
    type0 = typelist[0].goodsinfo_set.order_by('-id')[0:4]  # 按照上传顺序
    type01 = typelist[0].goodsinfo_set.order_by('-gclick')[0:4]  # 按照点击量
    type1 = typelist[1].goodsinfo_set.order_by('-id')[0:4]
    type11 = typelist[1].goodsinfo_set.order_by('-gclick')[0:4]
    type2 = typelist[2].goodsinfo_set.order_by('-id')[0:4]
    type21 = typelist[2].goodsinfo_set.order_by('-gclick')[0:4]
    type3 = typelist[3].goodsinfo_set.order_by('-id')[0:4]
    type31 = typelist[3].goodsinfo_set.order_by('-gclick')[0:4]
    type4 = typelist[4].goodsinfo_set.order_by('-id')[0:4]
    type41 = typelist[4].goodsinfo_set.order_by('-gclick')[0:4]
    type5 = typelist[5].goodsinfo_set.order_by('-id')[0:4]
    type51 = typelist[5].goodsinfo_set.order_by('-gclick')[0:4]

    cart_num = 0
    # 判断是否存在登录状态
    if 'user_id' in request.session:
        user_id = request.session['user_id']
        cart_num = CartInfo.objects.filter(user_id=int(user_id)).count()

    context = {
        'title': '首页',
        'cart_num': cart_num,
        'guest_cart': 1,
        'type0': type0, 'type01': type01,
        'type1': type1, 'type11': type11,
        'type2': type2, 'type21': type21,
        'type3': type3, 'type31': type31,
        'type4': type4, 'type41': type41,
        'type5': type5, 'type51': type51,
        'user': user,
        'informations': informations,
        'informations1': informations1,
        'username1': username1,
        'user_name': user_name,
        'nowTime': nowTime,
    }

    return render(request, 'df_goods/index.html', context)
商品搜索实现
python 复制代码
def ordinary_search(request):
    username = request.session.get('user_name')
    user = UserInfo.objects.filter(uname=username).first()
    from django.db.models import Q

    search_keywords = request.GET.get('q', '')
    pindex = request.GET.get('pindex', 1)
    search_status = 1
    cart_num, guest_cart = 0, 0

    try:
        user_id = request.session['user_id']
    except:
        user_id = None

    if user_id:
        guest_cart = 1
        cart_num = CartInfo.objects.filter(user_id=int(user_id)).count()

    # 使用Q对象进行复杂查询
    goods_list = GoodsInfo.objects.filter(
        Q(gtitle__icontains=search_keywords) |
        Q(gcontent__icontains=search_keywords) |
        Q(gjianjie__icontains=search_keywords)).order_by("gclick")

    if goods_list.count() == 0:
        # 商品搜索结果为空,返回推荐商品
        search_status = 0
        goods_list = GoodsInfo.objects.all().order_by("gclick")[:4]

    paginator = Paginator(goods_list, 4)
    page = paginator.page(int(pindex))

    context = {
        'title': '搜索列表',
        'search_status': search_status,
        'guest_cart': guest_cart,
        'cart_num': cart_num,
        'page': page,
        'paginator': paginator,
        'user': user,
    }
    return render(request, 'df_goods/ordinary_search.html', context)
商品互动
  • 商品评论: 支持图片上传的评论系统
  • 评论管理: 评论分页展示和管理
  • 点击统计: 商品点击量统计
  • 浏览记录: 用户浏览历史记录
商品详情与点击统计
python 复制代码
def detail(request, gid):
    if 'user_id' in request.session:
        uid = request.session['user_id']
        user = UserInfo.objects.get(id=uid)
        good_id = gid
        goods = GoodsInfo.objects.get(pk=int(good_id))
        goods.gclick = goods.gclick + 1  # 商品点击量
        goods.save()

        news = goods.gtype.goodsinfo_set.order_by('-id')[0:2]
        context = {
            'title': goods.gtype.ttitle,
            'guest_cart': 1,
            'cart_num': cart_count(request),
            'goods': goods,
            'news': news,
            'id': good_id,
            'user': user,
        }
        response = render(request, 'df_goods/detail.html', context)

        # 记录用户浏览历史
        try:
            browsed_good = GoodsBrowser.objects.get(user_id=int(uid), good_id=int(good_id))
        except Exception:
            browsed_good = None
        
        if browsed_good:
            from datetime import datetime
            browsed_good.browser_time = datetime.now()
            browsed_good.save()
        else:
            GoodsBrowser.objects.create(user_id=int(uid), good_id=int(good_id))
            browsed_goods = GoodsBrowser.objects.filter(user_id=int(uid))
            browsed_good_count = browsed_goods.count()
            if browsed_good_count > 5:
                ordered_goods = browsed_goods.order_by("-browser_time")
                for _ in ordered_goods[5:]:
                    _.delete()
        return response
商品评论发布
python 复制代码
def content(request, gid, pindex):
    if 'user_id' in request.session:
        uid = request.session['user_id']
        user = UserInfo.objects.get(id=uid)
        good_id = gid
        goods = GoodsInfo.objects.get(pk=int(good_id))
        
        # 获取当前货物信息
        news = goods.gtype.goodsinfo_set.order_by('-id')[0:2]
        # 条件查询
        goodsContents = GoodsContent.objects.filter(cgoodsname_id=good_id).order_by('-date_publish')
        
        # 创建Paginator一个分页对象
        paginator = Paginator(goodsContents, 2)
        # 返回Page对象,包含商品信息
        page = paginator.page(int(pindex))
        
        context = {
            'title': goods.gtype.ttitle,
            'guest_cart': 1,
            'cart_num': cart_count(request),
            'goods': goods,
            'id': good_id,
            'news': news,
            'user': user,
            'goodsContents': goodsContents,
            'paginator': paginator,
            'page': page,
        }
        
        if request.method == "POST":
            ctitle = goods.gtitle
            cpic = request.FILES.get('pic')
            cusername = user.uname
            clogo = user.ulogo
            cuser_content = request.POST.get('text')
            cgoodsname_id = goods.id
            
            if cpic == "":
                GoodsContent.objects.create(ctitle=ctitle, cusername=cusername, 
                                         cuser_content=cuser_content, cgoodsname_id=cgoodsname_id, clogo=clogo)
                messages.success(request, "评论成功!")
            else:
                GoodsContent.objects.create(ctitle=ctitle, cpic=cpic, cusername=cusername, 
                                         cuser_content=cuser_content, cgoodsname_id=cgoodsname_id, clogo=clogo)
                messages.success(request, "评论成功!")

        return render(request, 'df_goods/content.html', context)

3. 购物车模块 (df_cart)

购物车功能
  • 添加商品: 支持数量选择的商品添加
  • 数量修改: 实时修改购物车商品数量
  • 商品删除: 从购物车中删除商品
  • 购物车展示: 购物车商品列表和统计
购物车管理
  • 用户关联: 购物车与用户账号关联
  • 数据持久化: 购物车数据数据库存储
  • AJAX交互: 异步更新购物车信息
购物车添加商品
python 复制代码
@user_decorator.login
def add(request, gid, count):
    uid = request.session['user_id']
    gid, count = int(gid), int(count)
    # 查询购物车中是否已经有此商品,如果有则数量增加,如果没有则新增
    carts = CartInfo.objects.filter(user_id=uid, goods_id=gid)
    if len(carts) >= 1:
        cart = carts[0]
        cart.count = cart.count + count
    else:
        cart = CartInfo()
        cart.user_id = uid
        cart.goods_id = gid
        cart.count = count
    cart.save()
    
    # 如果是ajax提交则直接返回json,否则转向购物车
    if request.is_ajax():
        count = CartInfo.objects.filter(user_id=request.session['user_id']).count()
        return JsonResponse({'count': count})
    else:
        return redirect(reverse("df_cart:cart"))
购物车商品数量修改
python 复制代码
@user_decorator.login
def edit(request, cart_id, count):
    data = {}
    try:
        cart = CartInfo.objects.get(pk=int(cart_id))
        cart.count = int(count)
        cart.save()
        data['count'] = 0
    except Exception:
        data['count'] = count
    return JsonResponse(data)
购物车商品删除
python 复制代码
@user_decorator.login
def delete(request, cart_id):
    data = {}
    try:
        cart = CartInfo.objects.get(pk=int(cart_id))
        cart.delete()
        data['ok'] = 1
    except Exception:
        data['ok'] = 0
    return JsonResponse(data)
购物车展示
python 复制代码
@user_decorator.login
def user_cart(request):
    uid = request.session['user_id']
    username = request.session.get('user_name')
    user = UserInfo.objects.filter(uname=username).first()
    carts = CartInfo.objects.filter(user_id=uid)
    cart_num = CartInfo.objects.filter(user_id=uid).count()
    
    context = {
        'title': '购物车',
        'page_name': 1,
        'guest_cart': 1,
        'carts': carts,
        'cart_num': cart_num,
        'user': user,
    }
    
    if request.is_ajax():
        count = CartInfo.objects.filter(user_id=request.session['user_id']).count()
        # 求当前用户购买了几件商品
        return JsonResponse({'count': count})
    else:
        return render(request, 'df_cart/cart.html', context)

4. 订单管理模块 (df_order)

订单处理
  • 订单创建: 从购物车创建订单
  • 订单信息: 收货地址、联系方式管理
  • 价格计算: 商品总价、运费、总金额计算
  • 库存检查: 下单时检查商品库存
订单管理
  • 订单状态: 订单状态跟踪和管理
  • 订单历史: 用户订单历史记录
  • 事务处理: 使用数据库事务确保数据一致性
订单创建与事务处理
python 复制代码
@user_decorator.login
@transaction.atomic()  # 事务
def order_handle(request):
    uid = request.session['user_id']
    user = UserInfo.objects.get(id=uid)
    tran_id = transaction.savepoint()  # 保存事务发生点
    cart_ids = request.POST.get('cart_ids')  # 用户提交的订单购物车
    user_id = request.session['user_id']
    data = {}
    
    try:
        order_info = OrderInfo()  # 创建一个订单对象
        now = datetime.now()
        order_info.oid = '%s%d' % (now.strftime('%Y%m%d%H%M%S'), user_id)  # 订单号
        order_info.odate = now  # 订单时间
        order_info.user_id = int(user_id)  # 订单的用户id
        order_info.ototal = Decimal(request.POST.get('total'))  # 订单总价
        order_info.oaddress = user.uaddress
        order_info.save()

        for cart_id in cart_ids.split(','):  # 逐个处理购物车商品
            cart = CartInfo.objects.get(pk=cart_id)
            order_detail = OrderDetailInfo()
            order_detail.order = order_info
            goods = cart.goods
            
            if cart.count <= goods.gkucun:  # 判断库存是否满足订单
                goods.gkucun = goods.gkucun - cart.count
                goods.save()
                order_detail.goods = goods
                order_detail.price = goods.gprice
                order_detail.count = cart.count
                order_detail.username = user.uname
                order_detail.shopername = goods.gunit
                order_detail.save()
                cart.delete()  # 删除购物车
            else:  # 库存不足,事务回滚
                transaction.savepoint_rollback(tran_id)
                return HttpResponse('库存不足')
        
        data['ok'] = 1
        transaction.savepoint_commit(tran_id)
    except Exception as e:
        print("%s" % e)
        print('未完成订单提交')
        transaction.savepoint_rollback(tran_id)  # 事务回滚
    
    return JsonResponse(data)

5. 消息系统

用户消息
  • 消息发送: 用户间消息发送功能
  • 消息中心: 消息列表和详情展示
  • 已读状态: 消息已读状态管理
  • 消息历史: 消息历史记录保存
客服功能
  • 客服消息: 与客服的沟通功能
  • 消息回复: 消息回复和对话功能
消息中心实现
python 复制代码
@user_decorator.login
def message(request):
    user = UserInfo.objects.get(id=request.session['user_id'])
    cart_num = CartInfo.objects.filter(user_id=user.id).count()
    # 消息用户名去重
    persons = Information.objects.filter(cinformation_id=user.id).values('cusername','ccheck').distinct().order_by('cusername')
    # 查询发消息者的头像
    imgs = UserInfo.objects.filter()
    
    context = {
        'title': '消息中心',
        'page_name': 1,
        'user': user,
        'persons': persons,
        'imgs': imgs,
        'guest_cart': 1,
        'cart_num': cart_num,
        'username': user.uname,
    }
    return render(request, 'df_user/user_messages.html', context)
消息详情与回复
python 复制代码
@user_decorator.login
def person_message(request):
    user = UserInfo.objects.get(id=request.session['user_id'])  # 当前登录用户
    cart_num = CartInfo.objects.filter(user_id=user.id).count()
    # 消息用户名去重
    persons = Information.objects.filter(cinformation_id=user.id).values('cusername','ccheck').distinct().order_by('cusername')
    # 查询发消息者的头像
    imgs = UserInfo.objects.filter()
    # 展示消息
    username = request.GET['username']
    informations = Information.objects.filter()
    logo = UserInfo.objects.get(uname=username)
    
    # 展示消息后使消息变为已读状态
    for information in informations:
        if information.cusername == username:
            information.ccheck = True
            information.save()
    
    # 消息回复
    user_name = UserInfo.objects.get(uname=username)  # 获取当前消息用户信息
    
    if request.method == "POST":
        cusername = user.uname
        cusername1 = user_name.uname
        ccontent_chart = request.POST.get('title')
        cinformation_id = user_name.id
        if ccontent_chart == "":
            messages.success(request, "请输入内容!")
        else:
            Information.objects.create(cusername=cusername, cusername1=cusername1,
                                   ccontent_chart=ccontent_chart, cinformation_id=cinformation_id)
            messages.success(request, "消息发送成功")
            return redirect(reverse("df_user:message"))
    
    context = {
        'title': '消息中心',
        'page_name': 1,
        'user': user,
        'informations': informations,
        'persons': persons,
        'imgs': imgs,
        'logo': logo,
        'username': username,
        'user_name': user_name,
        'guest_cart': 1,
        'cart_num': cart_num,
    }
    return render(request, 'df_user/user_messages.html', context)

6. 退货退款功能

退货申请
  • 退货提交: 退货申请信息填写
  • 退货信息: 快递信息、地址、退货理由
  • 审批流程: 退货申请审批管理
  • 状态跟踪: 退货状态跟踪
退货申请实现
python 复制代码
@user_decorator.login
def tuihuo(request):
    uid = request.session['user_id']
    user = UserInfo.objects.get(id=uid)
    
    if request.method == "POST":
        title = request.POST.get('title')
        username = request.POST.get('username')
        username1 = request.POST.get('username1')
        person_number = request.POST.get('person_number')
        order_number = request.POST.get('order_number')
        kuaidi = request.POST.get('kuaidi')
        kuaidi_number = request.POST.get('kuaidi_number')
        address = request.POST.get('address')
        address1 = request.POST.get('address1')
        text = request.POST.get('text')
        
        if title == "" or username == "" or username1 == "" or person_number == "" or order_number == "" or kuaidi == "" or kuaidi_number == "" or address == "" or address1 == "":
            messages.success(request, "请填写完整信息!")
        else:
            tuihuoInfo.objects.create(title=title, username=username, username1=username1,
                                    person_number=person_number, order_number=order_number,
                                    kuaidi=kuaidi, kuaidi_number=kuaidi_number,
                                    address=address, address1=address1, text=text)
            messages.success(request, "提交成功,等待审批!")
            return redirect(reverse("df_user:info"))

    context = {
        'title': '填写退货信息',
        'page_name': 1,
        'user': user,
    }
    return render(request, 'df_user/tuihuo.html', context)
订单管理实现
python 复制代码
@user_decorator.login
def order(request, index):
    user_id = request.session['user_id']
    orders_list = OrderInfo.objects.filter(user_id=int(user_id)).order_by('-odate')
    cart_num = CartInfo.objects.filter(user_id=int(user_id)).count()
    tuohuo_infos = tuihuoInfo.objects.filter()
    paginator = Paginator(orders_list, 2)
    page = paginator.page(int(index))
    user = UserInfo.objects.get(id=request.session['user_id'])
    
    context = {
        'paginator': paginator,
        'page': page,
        'title': "用户中心",
        'user': user,
        'page_name': 1,
        'guest_cart': 1,
        'cart_num': cart_num,
        'tuohuo_infos': tuohuo_infos,
    }
    return render(request, 'df_user/user_center_order.html', context)

🎨 界面设计

首页设计

  • 轮播图: 商品轮播展示
  • 分类导航: 商品分类快速导航
  • 热门商品: 按点击量排序的热门商品
  • 最新商品: 按时间排序的最新商品

用户界面

  • 响应式设计: 适配不同屏幕尺寸
  • 用户友好: 简洁直观的操作界面
  • 交互反馈: 操作成功/失败提示
  • 加载优化: 页面加载性能优化

🔒 安全特性

数据安全

  • 密码加密: SHA1加密存储用户密码
  • 验证码: 图片验证码防止恶意注册
  • Session管理: 安全的用户会话管理
  • SQL注入防护: 使用Django ORM防止SQL注入
密码加密实现
python 复制代码
def register_handle(request):
    username = request.POST.get('user_name')
    password = request.POST.get('pwd')
    confirm_pwd = request.POST.get('confirm_pwd')
    email = request.POST.get('email')

    # 判断两次密码一致性
    if password != confirm_pwd:
        return redirect('/user/register/')
    
    # 密码加密
    s1 = sha1()
    s1.update(password.encode('utf8'))
    encrypted_pwd = s1.hexdigest()

    # 创建对象
    UserInfo.objects.create(uname=username, upwd=encrypted_pwd, uemail=email)
验证码生成实现
python 复制代码
def verify_code(request):
    import random
    # 定义变量,用于画面的背景色、宽、高
    bgcolor = (224, 224, 224)
    width = 100
    height = 34
    # 创建画面对象
    im = Image.new('RGB', (width, height), bgcolor)
    # 创建画笔对象
    draw = ImageDraw.Draw(im)
    
    # 调用画笔的point()函数绘制噪点
    for i in range(0, 100):
        xy = (random.randrange(0, width), random.randrange(0, height))
        fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
        draw.point(xy, fill=fill)
    
    # 定义验证码的备选值
    str1 = 'abcdefghijklmnopqrstuvwsyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
    # 随机选取4个值作为验证码
    rand_str = ''
    for i in range(0, 4):
        rand_str += str1[random.randrange(0, len(str1))]
    
    # 构造字体对象
    font = ImageFont.truetype('arial.ttf', 23)
    # 构造字体颜色
    fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
    
    # 绘制4个字
    draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
    draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
    draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
    draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
    
    # 存入session,用于做进一步验证
    request.session['verifycode'] = rand_str
    
    # 内存文件操作
    buf = BytesIO()
    # 将图片保存在内存中,文件类型为png
    im.save(buf, 'png')
    # 将内存中的图片数据返回给客户端,MIME类型为图片png
    return HttpResponse(buf.getvalue(), 'image/png')

权限控制

  • 登录验证: 严格的登录状态验证
  • 权限装饰器: 自定义权限验证装饰器
  • 账号封禁: 违规账号封禁机制
  • 实名认证: 用户身份验证机制
实名认证实现
python 复制代码
@user_decorator.login
def check_user(request):
    user = UserInfo.objects.get(id=request.session['user_id'])
    cart_num = CartInfo.objects.filter(user_id=user.id).count()
    
    if request.method == "POST":
        user.urealname = request.POST.get('name')
        user.uzhengjian_type = request.POST.get('type_id')
        user.uzhengjian_tel = request.POST.get('tel')
        user.uzhengjian_img = request.FILES.get('pic')
        
        if user.urealname == None or user.uzhengjian_type == None or user.uzhengjian_tel == None or user.uzhengjian_img == None:
            print("error:请填写完整信息")
            messages.success(request, "请填写完整信息")
        else:
            user.save()
            print("请等待审批")
            messages.success(request, "提交成功,请等待审批")

    context = {
        'page_name': 1,
        'title': '用户中心',
        'user': user,
        'guest_cart': 1,
        'cart_num': cart_num,
    }
    return render(request, 'df_user/user_check_username.html', context)
密码修改实现
python 复制代码
@user_decorator.login
def changeInPwd(request):
    uid = request.session['user_id']
    user = UserInfo.objects.get(id=uid)
    cart_num = CartInfo.objects.filter(user_id=user.id).count()
    
    context = {
        'page_name': 1,
        'title': '用户中心',
        'user': user,
        'guest_cart': 1,
        'cart_num': cart_num,
    }

    if request.method == "POST":
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        if password == "" or password2 == "":
            messages.success(request, "请输入要修改的密码!")
        elif password == password2:
            # 密码加密
            s1 = sha1()
            s1.update(password.encode('utf8'))
            encrypted_pwd = s1.hexdigest()
            user.upwd = encrypted_pwd
            user.save()
            messages.success(request, "修改成功!")
        else:
            messages.success(request, "两次密码输入不正确!")
    
    return render(request, 'df_user/user_changePwd.html', context)
登录验证装饰器实现
python 复制代码
def login(func):
    def login_fun(request, *args, **kwargs):
        if 'user_id' in request.session:
            return func(request, *args, **kwargs)
        else:
            red = HttpResponseRedirect(reverse("df_user:login"))
            red.set_cookie('url', request.get_full_path())
            # 保证用户再登陆验证之后仍点击到希望的页面
            return red
    return login_fun
用户登录处理
python 复制代码
def login_handle(request):
    # 接受请求信息
    uname = request.POST.get('username')
    upwd = request.POST.get('pwd')
    jizhu = request.POST.get('jizhu', 0)
    vc = request.POST.get('vc')
    verifycode = request.session['verifycode']
    user = UserInfo.objects.filter(uname=uname)
    
    if len(user) == 1:  # 判断用户密码并跳转
        s1 = sha1()
        s1.update(upwd.encode('utf8'))
        if s1.hexdigest() == user[0].upwd and vc == verifycode and user[0].uname_passOrfail == True:
            url = request.COOKIES.get('url', '/')
            red = HttpResponseRedirect(url)
            # 是否勾选记住用户名,设置cookie
            if jizhu != 0:
                red.set_cookie('uname', uname)
            else:
                red.set_cookie('uname', '', max_age=-1)
            request.session['user_id'] = user[0].id
            request.session['user_name'] = uname
            return red
        elif user[0].uname_passOrfail == False:
            messages.success(request, "你的账号存在违规行为,已被封禁。")

📊 数据库设计

核心数据表

用户相关表
  • df_user_userinfo: 用户基本信息
  • df_user_goodsbrowser: 用户浏览记录
  • df_user_information: 用户消息
  • df_user_tuihuoinfo: 退货信息
商品相关表
  • df_goods_goodsinfo: 商品信息
  • df_goods_typeinfo: 商品分类
  • df_goods_goodscontent: 商品评论
  • df_goods_contentchart: 评论回复
交易相关表
  • df_cart_cartinfo: 购物车信息
  • df_order_orderinfo: 订单信息
  • df_order_orderdetailinfo: 订单详情

数据关系

  • 用户与商品:多对多关系(通过购物车、订单、评论)
  • 商品与分类:多对一关系
  • 订单与用户:多对一关系
  • 评论与商品:多对一关系

数据模型设计

用户信息模型 (UserInfo)
python 复制代码
class UserInfo(models.Model):
    uname = models.CharField(max_length=20, verbose_name="用户名", unique=True)
    usex = models.CharField(max_length=10, verbose_name="性别", default="")
    uage = models.CharField(max_length=10, verbose_name="年龄", default="")
    upersonInf = models.CharField(max_length=200, verbose_name="个人简介", default="")
    ulogo = models.FileField(verbose_name="用户头像", upload_to='images', default='default.jpg')
    upwd = models.CharField(max_length=40, verbose_name="用户密码", blank=False)
    uemail = models.EmailField(verbose_name="邮箱", unique=True)
    urealname = models.CharField(max_length=20, default="", verbose_name="真实姓名")
    uzhengjian_type = models.CharField(max_length=20, default="", verbose_name="证件类型")
    uzhengjian_tel = models.CharField(max_length=18, default="", verbose_name="证件号码")
    uzhengjian_img = models.FileField(upload_to='images/zhengjian_img', default="", verbose_name="证件图片")
    ucheck_passOrfail = models.BooleanField(verbose_name="认证审批", default=False)
    ushou = models.CharField(max_length=20, default="", verbose_name="收货名称")
    uaddress = models.CharField(max_length=100, default="", verbose_name="地址")
    uyoubian = models.CharField(max_length=6, default="", verbose_name="邮编")
    uphone = models.CharField(max_length=11, default="", verbose_name="手机号")
    uname_passOrfail = models.BooleanField(verbose_name="允许登录", default=True)

    class Meta:
        verbose_name = "用户信息"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.uname
商品信息模型 (GoodsInfo)
python 复制代码
class GoodsInfo(models.Model):
    isDelete = models.BooleanField(default=False)  # 逻辑删除
    gtitle = models.CharField(max_length=20, verbose_name="商品名称", unique=True)
    gpic = models.ImageField(verbose_name='商品图片', upload_to='df_goods/image/%Y/%m', null=True, blank=True)
    gprice = models.DecimalField(max_digits=7, decimal_places=2, verbose_name="商品价格")
    gunit = models.CharField(max_length=20, verbose_name="卖家昵称")
    gclick = models.IntegerField(verbose_name="点击量", default=0, null=False)
    gjianjie = models.CharField(max_length=200, verbose_name="简介")
    gkucun = models.IntegerField(verbose_name="库存", default=0)
    gcontent = HTMLField(max_length=200, verbose_name="详情")
    gtype = models.ForeignKey(TypeInfo, on_delete=models.CASCADE, verbose_name="分类")

    class Meta:
        verbose_name = "商品"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.gtitle
订单信息模型 (OrderInfo)
python 复制代码
class OrderInfo(models.Model):
    oid = models.CharField(max_length=20, primary_key=True, verbose_name="大订单号")
    user = models.ForeignKey(UserInfo, on_delete=models.CASCADE, verbose_name="订单用户")
    odate = models.DateTimeField(auto_now=True, verbose_name="时间")
    oIsPay = models.BooleanField(default=False, verbose_name="是否支付")
    ototal = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="总价")
    oaddress = models.CharField(max_length=150, verbose_name="订单地址")

    class Meta:
        verbose_name = "未付款订单"
        verbose_name_plural = verbose_name

    def __str__(self):
        return "{0}在的订单{1}".format(self.user.uname, self.odate)

💻 项目演示


















🚀 部署方案

环境要求

  • Python: 3.6+
  • Django: 2.0.7
  • MySQL: 5.7+
  • Pillow: 7.1.1
  • PyMySQL: 数据库连接

部署步骤

  1. 环境准备: 安装Python、MySQL等基础环境
  2. 依赖安装: 安装requirements.txt中的依赖包
  3. 数据库配置: 创建数据库,导入SQL文件
  4. 配置修改: 修改settings.py中的数据库配置
  5. 静态文件: 收集静态文件
  6. 服务启动: 启动Django开发服务器

配置说明

python 复制代码
# 数据库配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'design_169_market',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT': '3306'
    }
}

# 邮件配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = False
EMAIL_USE_SSL = True
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 465
EMAIL_HOST_USER = 'woaisilaowu@163.com'
EMAIL_HOST_PASSWORD = 'PZDOHBIYHAJSCVPS'

# 静态文件配置
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

# 媒体文件配置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# 富文本编辑器配置
TINYMCE_DEFAULT_CONFIG = {
    'theme': 'advanced',
    'width': 600,
    'height': 400,
}

# 时区和语言配置
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False

📈 性能优化

数据库优化

  • 索引优化: 为常用查询字段添加索引
  • 查询优化: 使用select_related减少查询次数
  • 分页处理: 大数据量分页展示
查询优化示例
python 复制代码
# 优化前:N+1查询问题
goods_list = GoodsInfo.objects.all()
for goods in goods_list:
    print(goods.gtype.ttitle)  # 每次都会查询数据库

# 优化后:使用select_related预加载关联数据
goods_list = GoodsInfo.objects.select_related('gtype').all()
for goods in goods_list:
    print(goods.gtype.ttitle)  # 只查询一次数据库
分页处理示例
python 复制代码
from django.core.paginator import Paginator

def good_list(request, tid, pindex, sort):
    # 根据排序方式获取商品列表
    if sort == '1':  # 默认最新
        goods_list = GoodsInfo.objects.filter(gtype_id=int(tid)).order_by('-id')
    elif sort == '2':  # 按照价格
        goods_list = GoodsInfo.objects.filter(gtype_id=int(tid)).order_by('-gprice')
    elif sort == '3':  # 按照人气点击量
        goods_list = GoodsInfo.objects.filter(gtype_id=int(tid)).order_by('-gclick')

    # 创建Paginator分页对象
    paginator = Paginator(goods_list, 4)  # 每页4个商品
    # 返回Page对象,包含商品信息
    page = paginator.page(int(pindex))
    
    context = {
        'title': '商品列表',
        'page': page,
        'paginator': paginator,
        'sort': sort,
    }
    return render(request, 'df_goods/list.html', context)

前端优化

  • 静态文件: 静态文件CDN加速
  • 图片优化: 图片压缩和格式优化
  • 缓存策略: 浏览器缓存和服务器缓存
AJAX异步加载示例
javascript 复制代码
// 购物车数量异步更新
$(function () {
    $('.input_btn').click(function () {
        q = $('.input_text').val();
        if(q==""){
            alert("请输入搜索内容");
        } else {
            location.href = '{% url "df_goods:ordinary_search" %}?q='+q+'&page=1'
        }
    })
})

// 购物车商品数量修改
function edit_cart(cart_id, count) {
    $.get('/cart/edit' + cart_id + '_' + count + '/', function(data) {
        if(data.count == 0) {
            // 更新成功
            update_cart_display();
        } else {
            alert('修改失败');
        }
    });
}

代码优化

  • 代码复用: 提取公共组件和函数
  • 异常处理: 完善的异常处理机制
  • 日志记录: 系统运行日志记录
异常处理示例
python 复制代码
@user_decorator.login
def edit(request, cart_id, count):
    data = {}
    try:
        cart = CartInfo.objects.get(pk=int(cart_id))
        cart.count = int(count)
        cart.save()
        data['count'] = 0
    except Exception:
        data['count'] = count
    return JsonResponse(data)

@user_decorator.login
def delete(request, cart_id):
    data = {}
    try:
        cart = CartInfo.objects.get(pk=int(cart_id))
        cart.delete()
        data['ok'] = 1
    except Exception:
        data['ok'] = 0
    return JsonResponse(data)

🐛 常见问题与解决方案

1. 数据库连接问题

问题 : PyMySQL连接MySQL失败
解决方案:

python 复制代码
import pymysql
pymysql.install_as_MySQLdb()

2. 用户注册验证问题

问题 : 用户名和邮箱重复验证
解决方案:

python 复制代码
def register_exist(request):
    username = request.GET.get('uname')
    uemail = request.GET.get('uemail')
    count = UserInfo.objects.filter(uname=username).count()
    email_count = UserInfo.objects.filter(uemail=uemail).count()
    return JsonResponse({'count': count, 'email_count': email_count})

3. 静态文件问题

问题 : 静态文件无法加载
解决方案:

python 复制代码
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

4. 媒体文件上传问题

问题 : 上传文件无法访问
解决方案:

python 复制代码
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# urls.py配置
from django.views.static import serve
urlpatterns = [
    # ... 其他URL配置
    url(r'^media/(?P<path>.*)$', serve, {"document_root": MEDIA_ROOT})
]

5. Session管理问题

问题 : 用户登录状态丢失
解决方案:

python 复制代码
# 登录时设置session
request.session['user_id'] = user[0].id
request.session['user_name'] = uname

# 退出时清除session
def logout(request):
    request.session.flush()
    return redirect(reverse("df_goods:index"))

6. 事务处理问题

问题 : 订单创建时数据不一致
解决方案:

python 复制代码
@transaction.atomic()
def order_handle(request):
    tran_id = transaction.savepoint()
    try:
        # 订单处理逻辑
        transaction.savepoint_commit(tran_id)
    except Exception as e:
        transaction.savepoint_rollback(tran_id)
        return HttpResponse('订单创建失败')

7. 验证码问题

问题 : 验证码图片无法显示
解决方案: 确保Pillow库正确安装,字体文件存在

🔮 项目扩展建议

功能扩展

  • 支付集成: 集成支付宝、微信支付
  • 物流跟踪: 集成快递查询API
  • 消息推送: 集成推送通知服务
  • 数据分析: 添加数据统计和分析功能

技术升级

  • Django版本: 升级到最新稳定版本
  • 数据库: 考虑使用PostgreSQL
  • 缓存: 集成Redis缓存
  • 异步处理: 使用Celery处理异步任务

安全增强

  • HTTPS: 启用HTTPS协议
  • CSRF防护: 增强CSRF防护
  • XSS防护: 加强XSS攻击防护
  • SQL注入: 进一步防止SQL注入

📚 学习资源

官方文档

相关技术

最佳实践

🎉 项目总结

这个基于Django的二手交易校园购物系统成功实现了完整的电商交易功能,具有以下特点:

技术亮点

  • 完整的电商流程: 从用户注册到订单完成的完整流程
  • 模块化设计: 清晰的模块划分和代码组织
  • 安全可靠: 完善的安全机制和权限控制
  • 用户友好: 直观的界面设计和交互体验

功能特色

  • 校园特色: 专为校园环境设计的交易平台
  • 互动功能: 评论、消息、浏览记录等互动功能
  • 管理完善: 完整的后台管理系统
  • 扩展性强: 良好的代码结构便于功能扩展

学习价值

  • Django框架: 深入学习Django框架的使用
  • 数据库设计: 学习关系型数据库设计和优化
  • 前端交互: 学习前后端交互和用户体验设计
  • 项目架构: 学习大型项目的架构设计和模块化开发

这个项目不仅是一个功能完整的电商系统,更是一个很好的学习案例,涵盖了Web开发的各个方面,对于学习Django框架和Web开发具有很高的参考价值。


本文档详细介绍了基于Django的二手交易校园购物系统的开发过程、技术架构、功能特性等内容,希望对您的学习和开发有所帮助。如有任何问题或建议,欢迎交流讨论。

相关推荐
崎岖Qiu4 分钟前
【JVM篇11】:分代回收与GC回收范围的分类详解
java·jvm·后端·面试
深海潜水员2 小时前
【Python】 切割图集的小脚本
开发语言·python
27669582922 小时前
东方航空 m端 wasm req res分析
java·python·node·wasm·东方航空·东航·东方航空m端
许苑向上2 小时前
Spring Boot 自动装配底层源码实现详解
java·spring boot·后端
星月昭铭3 小时前
Spring AI调用Embedding模型返回HTTP 400:Invalid HTTP request received分析处理
人工智能·spring boot·python·spring·ai·embedding
Dreamsi_zh3 小时前
Python爬虫02_Requests实战网页采集器
开发语言·爬虫·python
weixin_448617054 小时前
疏老师-python训练营-Day30模块和库的导入
开发语言·python
超级小忍4 小时前
深入浅出:在 Spring Boot 中构建实时应用 - 全面掌握 WebSocket
spring boot·后端·websocket
没有bug.的程序员5 小时前
《Spring Security源码深度剖析:Filter链与权限控制模型》
java·后端·spring·security·filter·权限控制
程序员三藏5 小时前
Web UI自动化测试之PO篇
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例