基于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: 数据库连接
部署步骤
- 环境准备: 安装Python、MySQL等基础环境
- 依赖安装: 安装requirements.txt中的依赖包
- 数据库配置: 创建数据库,导入SQL文件
- 配置修改: 修改settings.py中的数据库配置
- 静态文件: 收集静态文件
- 服务启动: 启动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的二手交易校园购物系统的开发过程、技术架构、功能特性等内容,希望对您的学习和开发有所帮助。如有任何问题或建议,欢迎交流讨论。