美多商城完整教程(附代码资料)主要内容讲述:欢迎来到美多商城!,项目准备。展示用户注册页面,创建用户模块子应用。用户注册业务实现,用户注册前端逻辑。图形验证码,图形验证码接口设计和定义。短信验证码,避免频繁发送短信验证码。账号登录,用户名登录。登录,登录开发文档。用户基本信息,查询并渲染用户基本信息。收货地址,省市区三级联动。收货地址,展示地址前后端逻辑。商品数据库表设计,SPU和SKU。准备商品数据,容器化方案Docker。首页广告,展示首页商品频道分类。商品列表页,列表页面包屑导航。商品搜索,Haystack扩展建立索引。商品详情页,统计分类商品访问量。购物车管理,添加购物车。购物车管理,删除购物车。订单,结算订单。提交订单,使用乐观锁并发下单。对接系统,订单支付功能。页面静态化,首页广告页面静态化。MySQL读写分离,MySQL主从同步。
全套笔记资料代码移步: 前往gitee仓库查看
感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~
全套教程部分目录:
部分文件图片:
账号登录
用户名登录
1. 用户名登录逻辑分析
2. 用户名登录接口设计
1.请求方式
选项 | 方案 |
---|---|
请求方法 | POST |
请求地址 | /login/ |
> | |
2.请求参数:表单 |
参数名 | 类型 | 是否必传 | 说明 |
---|---|---|---|
username | string | 是 | 用户名 |
password | string | 是 | 密码 |
remembered | string | 是 | 是否记住用户 |
> | |||
3.响应结果:HTML |
字段 | 说明 |
---|---|
登录失败 | 响应错误提示 |
登录成功 | 重定向到首页 |
3. 用户名登录接口定义
class LoginView(View):"""用户名登录"""def get(self, request):"""提供登录界面:param request: 请求对象:return: 登录界面"""passdef post(self, request):"""实现登录逻辑:param request: 请求对象:return: 登录结果"""pass
4. 用户名登录后端逻辑
class LoginView(View):"""用户名登录"""def get(self, request):"""提供登录界面:param request: 请求对象:return: 登录界面"""return render(request, 'login.html')def post(self, request):"""实现登录逻辑:param request: 请求对象:return: 登录结果"""# 接受参数username = request.POST.get('username')password = request.POST.get('password')remembered = request.POST.get('remembered')# 校验参数# 判断参数是否齐全if not all([username, password]):return http.HttpResponseForbidden('缺少必传参数')# 判断用户名是否是5-20个字符if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):return http.HttpResponseForbidden('请输入正确的用户名或手机号')# 判断密码是否是8-20个数字if not re.match(r'^[0-9A-Za-z]{8,20}$', password):return http.HttpResponseForbidden('密码最少8位,最长20位')# 认证登录用户user = authenticate(username=username, password=password)if user is None:return render(request, 'login.html', {'account_errmsg': '用户名或密码错误'})# 实现状态保持login(request, user)# 设置状态保持的周期if remembered != 'on':# 没有记住用户:浏览器会话结束就过期request.session.set_expiry(0)else:# 记住用户:None表示两周后过期request.session.set_expiry(None)# 响应登录结果return redirect(reverse('contents:index'))
5. 知识要点
-
登录的核心思想:认证和状态保持
-
通过用户的认证,确定该登录用户是美多商场的注册用户。
- 通过状态保持缓存用户的唯一标识信息,用于后续是否登录的判断。
多账号登录
- Django自带的用户认证后端默认是使用用户名实现用户认证的。
-
用户认证后端位置:django.contrib.auth.backends.ModelBackend。
-
如果想实现用户名和手机号都可以认证用户,就需要自定义用户认证后端。
-
自定义用户认证后端步骤
-
在users应用中新建utils.py文件
- 新建类,继承自ModelBackend
- 重写认证authenticate()方法
- 分别使用用户名和手机号查询用户
- 返回查询到的用户实例
1. 自定义用户认证后端
users.utils.py
from django.contrib.auth.backends import ModelBackend
import re
from .models import Userdef get_user_by_account(account):"""根据account查询用户:param account: 用户名或者手机号:return: user"""try:if re.match('^1[3-9]\d{9}$', account):# 手机号登录user = User.objects.get(mobile=account)else:# 用户名登录user = User.objects.get(username=account)except User.DoesNotExist:return Noneelse:return userclass UsernameMobileAuthBackend(ModelBackend):"""自定义用户认证后端"""def authenticate(self, request, username=None, password=None, **kwargs):"""重写认证方法,实现多账号登录:param request: 请求对象:param username: 用户名:param password: 密码:param kwargs: 其他参数:return: user"""# 根据传入的username获取user对象。username可以是手机号也可以是账号user = get_user_by_account(username)# 校验user是否存在并校验密码是否正确if user and user.check_password(password):return user
2. 配置自定义用户认证后端
1.Django自带认证后端源码
2.配置自定义用户认证后端
# 指定自定义的用户认证后端AUTHENTICATION_BACKENDS = ['users.utils.UsernameMobileAuthBackend']
3. 测试自定义用户认证后端
4. 知识要点
-
Django自带的用户认证系统只会使用用户名去认证一个用户。
-
所以我们为了实现多账号登录,就可以自定义认证后端,采用其他的唯一信息去认证一个用户。
首页用户名展示
1. 首页用户名展示方案
方案一
- 模板中 request 变量直接渲染用户名
- 缺点:不方便做首页静态化
{% if user.is_authenticated %}<div class="login_btn fl">欢迎您:<em>{{ user.username }}</em><span>|</span><a href="#">退出</a></div>{% else %}<div class="login_btn fl"><a href="login.html">登录</a><span>|</span><a href="register.html">注册</a></div>
{% endif %}
方案二
- 发送ajax请求获取用户信息
- 缺点:需要发送网络请求
<div class="login_btn fl">{# ajax渲染 #}
</div>
方案三
- Vue读取cookie渲染用户信息
<div v-if="username" class="login_btn fl">欢迎您:<em>[[ username ]]</em><span>|</span><a href="#">退出</a>
</div>
<div v-else class="login_btn fl"><a href="login.html">登录</a><span>|</span><a href="register.html">注册</a>
</div>
结论:
- 对比此三个方案,我们在本项目中选择 方案三
实现步骤:
- 注册或登录后,用户名写入到cookie
- Vue渲染主页用户名
2. 用户名写入到cookie
# 响应注册结果response = redirect(reverse('contents:index'))# 注册时用户名写入到cookie,有效期15天response.set_cookie('username', user.username, max_age=3600 * 24 * 15)return response
# 响应登录结果response = redirect(reverse('contents:index'))# 登录时用户名写入到cookie,有效期15天response.set_cookie('username', user.username, max_age=3600 * 24 * 15)return response
3. Vue渲染首页用户名
1.index.html
<div v-if="username" class="login_btn fl">欢迎您:<em>[[ username ]]</em><span>|</span><a href="#">退出</a>
</div>
<div v-else class="login_btn fl"><a href="login.html">登录</a><span>|</span><a href="register.html">注册</a>
</div>
2.index.js
mounted(){// 获取cookie中的用户名this.username = getCookie('username');
},
退出登录
1. logout()方法介绍
-
退出登录:
-
回顾登录:将通过认证的用户的唯一标识信息,写入到当前session会话中
-
退出登录:正好和登录相反(清理session会话信息)
-
logout()方法:
-
Django用户认证系统提供了
logout()
方法 -
封装了清理session的操作,帮助我们快速实现登出一个用户
-
logout()位置:
-
django.contrib.auth.__init__.py
文件中
logout(request)
2. logout()方法使用
class LogoutView(View):"""退出登录"""def get(self, request):"""实现退出登录逻辑"""# 清理sessionlogout(request)# 退出登录,重定向到登录页response = redirect(reverse('contents:index'))# 退出登录时清除cookie中的usernameresponse.delete_cookie('username')return response
3. 知识要点
- 退出登录的核心思想就是清理登录时缓存的状态保持信息。
- 由于首页中用户名是从cookie中读取的。所以退出登录时,需要将cookie中用户名清除。
判断用户是否登录
1. 展示用户中心界面
class UserInfoView(View):"""用户中心"""def get(self, request):"""提供个人信息界面"""return render(request, 'user_center_info.html')
需求:
- 当用户登录后,才能访问用户中心。
- 如果用户未登录,就不允许访问用户中心,将用户引导到登录界面。
实现方案:
- 需要判断用户是否登录。
- 根据是否登录的结果,决定用户是否可以访问用户中心。
2. is_authenticate
判断用户是否登录
介绍:
- Django用户认证系统提供了方法
request.user.is_authenticated()
来判断用户是否登录。 - 如果通过登录验证则返回True。反之,返回False。
- 缺点:登录验证逻辑很多地方都需要,所以该代码需要重复编码好多次。
class UserInfoView(View):"""用户中心"""def get(self, request):"""提供个人信息界面"""if request.user.is_authenticated():return render(request, 'user_center_info.html')else:return redirect(reverse('users:login'))
3. login_required装饰器
判断用户是否登录
-
Django用户认证系统提供了装饰器
login_required
来判断用户是否登录。 -
内部封装了
is_authenticate
-
位置:
django.contrib.auth.decorators
-
如果通过登录验证则进入到视图内部,执行视图逻辑。
-
如果未通过登录验证则被重定向到
LOGIN_URL
配置项指定的地址。 -
如下配置:表示当用户未通过登录验证时,将用户重定向到登录页面。
LOGIN_URL = '/login/'
1.装饰
as_view()
方法返回值
提示:
login_required装饰器
可以直接装饰函数视图,但是本项目使用的是类视图。as_view()
方法的返回值就是将类视图转成的函数视图。
结论:
- 要想使用
login_required装饰器
装饰类视图,可以间接的装饰as_view()
方法的返回值,以达到预期效果。
url(r'^info/$', login_required(views.UserInfoView.as_view()), name='info'),
class UserInfoView(View):"""用户中心"""def get(self, request):"""提供个人信息界面"""return render(request, 'user_center_info.html')
2.定义View子类封装
login_required装饰器
- 提示:
LoginRequired(object)
依赖于视图类View
,复用性很差。
url(r'^info/$', views.UserInfoView.as_view(), name='info'),
class LoginRequired(View):"""验证用户是否登陆"""@classmethoddef as_view(cls, **initkwargs):# 自定义as_view()方法中,调用父类的as_view()方法view = super().as_view()return login_required(view)class UserInfoView(LoginRequired):"""用户中心"""def get(self, request):"""提供个人信息界面"""return render(request, 'user_center_info.html')
3.定义obejct子类封装
login_required装饰器
- 提示:
LoginRequired(object)
不依赖于任何视图类,复用性更强。
url(r'^info/$', views.UserInfoView.as_view(), name='info'),
class LoginRequired(object):"""验证用户是否登陆"""@classmethoddef as_view(cls, **initkwargs):# 自定义as_view()方法中,调用父类的as_view()方法view = super().as_view()return login_required(view)class UserInfoView(LoginRequired, View):"""用户中心"""def get(self, request):"""提供个人信息界面"""return render(request, 'user_center_info.html')
4.定义验证用户是否登录扩展类
- 提示:定义扩展类方便项目中导入和使用(
meiduo_mall.utils.views.py
)
class LoginRequiredMixin(object):"""验证用户是否登录扩展类"""@classmethoddef as_view(cls, **initkwargs):# 自定义的as_view()方法中,调用父类的as_view()方法view = super().as_view()return login_required(view)
class UserInfoView(LoginRequiredMixin, View):"""用户中心"""def get(self, request):"""提供个人信息界面"""return render(request, 'user_center_info.html')
4. 登录时next参数的使用
1.next参数的效果
2.next参数的作用
- 由Django用户认证系统提供,搭配
login_required装饰器
使用。 - 记录了用户未登录时访问的地址信息,可以帮助我们实现在用户登录成功后直接进入未登录时访问的地址。
# 响应登录结果next = request.GET.get('next')
if next:response = redirect(next)
else:response = redirect(reverse('contents:index'))
5. 知识要点
- 判断用户是否登录依然使用状态保持信息实现。
- 项目中很多接口都是需要用户登录才能访问的,所以为了方便编码,我们将判断用户登录的操作封装到装饰器中。
- 登录时next参数的作用是为了方便用户从哪里进入到登录页面,登录成功后就回到哪里。