标签:order mysq return 详细信息 任务队列 car 异步任务 错误 member
目的:知道天天生鲜项目属于那种商业模式
B2B2C 是一种电子商务类型的网络购物商业模式,B 是 BUSINESS 的简称,C 是 CUSTOMER 的简称,第一个 B 指的是商品或服务的供应商,第二个 B 指的是从事电子商务的企业,C 则是表示消费者。第一个 BUSINESS,并不仅仅局限于品牌供应商、影视制作公司和图书出版商,任何的商品供应商或服务供应商都能可以成为第一个 BUSINESS;第二 B 是 B2B2C 模式的电子商务企业,通过统一的经营管理对商品和服务、消费者终端同时进行整合,是广大供应商和消费 者之间的桥梁,为供应商和消费者提供优质的服务,是互联网电子商务服务供应商。C 表示 消费者,在第二个 B 构建的统一电子商务平台购物的消费者。B2B2C 的来源于目前的 B2B、B2C 模式的演变和完善,把 B2C 和 C2C 完美地结合起来,通过 B2B2C 模式的电子商务企业构建自己的物流供应链系统,提供统一的服务。
案例:京东商城、天猫商城
register.html
login.html
user_center_info.html
user_center_order.html
user_center_site.html
index.html
list.html
detail.html
cart.html
place_order.html
字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
create_time | DateTimeField | auto_now_add=True | 创建时间 |
update_time | DateTimeField | auto_now=True | 更新时间 |
User
;表名:df_users
AbstractUser
,导包 from django.contrib.auth.models import AbstractUser
迁移前,需要在settings.py
文件中设置:AUTH_USER_MODEL = ‘应用.用户模型类‘
Address
;表名:df_address
User
模型类作为外键约束地址信息属于哪个用户字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
id | IntegerField | primary_key=True | 主键字段 |
user | ForeignKey | 外键为User模型 | 约束地址属于哪个用户 |
receiver_name | CharField | max_length=20 | 收件人 |
receiver_mobile | CharField | max_length=11 | 联系电话 |
detail_addr | CharField | max_length=256 | 详细地址 |
zip_code | CharField | max_length=6 | 邮政编码 |
GoodsCategory
;表名:df_goods_category
字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
id | IntegerField | primary_key=True | 主键字段 |
name | CharField | max_length=20 | 商品类别名称 |
logo | CharField | max_length=100 | 商品类别标识 |
image | ImageField | upload_to="category" | 商品类别图片 |
Goods
;表名:df_goods
字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
id | IntegerField | primary_key=True | 主键字段 |
name | CharField | max_length=100 | 商品名称 |
desc | HTMLField | blank=True | 商品详细介绍 |
GoodsSKU
;表名:df_goods_sku
字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
id | IntegerField | primary_key=True | 主键字段 |
category | ForeignKey | 外键为GoodsCategory模型 | 约束该商品的类别 |
goods | ForeignKey | 外键为Goods模型 | 约束该商品的SPU |
name | CharField | max_length=100 | 商品名称 |
title | CharField | max_length=200 | 商品简介 |
unit | CharField | max_length=10 | 销售单位 |
price | DecimalField | max_digits=10, decimal_places=2 | 商品价格 |
stock | IntegerField | default=0 | 商品库存 |
sales | IntegerField | default=0 | 商品销量 |
default_image | ImageField | upload_to="goods" | 商品默认图片 |
status | BooleanField | default=True | 是否上线 |
GoodsImage
;表名:df_goods_image
字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
id | IntegerField | primary_key=True | 主键字段 |
sku | ForeignKey | 外键为GoodsSKU模型 | 约束图片属于哪个商品的 |
image | ImageField | upload_to="goods" | 商品图片 |
IndexGoodsBanner
;表名:df_index_goods
字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
id | IntegerField | primary_key=True | 主键字段 |
sku | ForeignKey | 外键为GoodsSKU模型 | 约束该商品的sku |
image | ImageField | upload_to="banner" | 商品图片 |
index | SmallIntegerField | default=0 | 轮播顺序 |
IndexCategoryGoodsBanner
;表名:df_index_category_goods
字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
id | IntegerField | primary_key=True | 主键字段 |
category | ForeignKey | 外键为GoodsCategory模型 | 约束该商品类型 |
sku | ForeignKey | 外键为GoodsSKU模型 | 约束该商品的sku |
display_type | SmallIntegerField | choices=DISPLAY_TYPE_CHOICES | 展示类型:图片或标题 |
index | SmallIntegerField | default=0 | 展示顺序 |
IndexCategoryGoodsBanner
;表名:df_index_category_goods
字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
id | IntegerField | primary_key=True | 主键字段 |
name | CharField | max_length=50 | 活动名称 |
url | URLField | 活动链接 | |
image | ImageField | upload_to="banner" | 活动商品图片 |
index | SmallIntegerField | default=0 | 活动商品顺序 |
OrderInfo
;表名:df_order_info
字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
order_id | IntegerField | primary_key=True | 主键字段 |
user | ForeignKey | 外键为User模型 | 下单用户 |
address | ForeignKey | 外键为Address模型 | 下单地址 |
total_count | IntegerField | default=1 | 商品总数 |
total_amount | DecimalField | max_digits=10, decimal_places=2 | 商品总金额 |
trans_cost | DecimalField | max_digits=10, decimal_places=2 | 运费 |
pay_method | SmallIntegerField | choices=PAY_METHOD_CHOICES | 支付方式,定义支付选项 |
status | SmallIntegerField | choices=ORDER_STATUS_CHOICES | 订单状态,自定义状态 |
trade_id | CharField | max_length=100, unique=True | 订单编号 |
OrderGoods
;表名:df_order_goods
字段名 | 字段类型 | 字段选项 | 字段说明 |
---|---|---|---|
id | IntegerField | primary_key=True | 主键字段 |
order | ForeignKey | 外键为OrderInfo模型 | 约束是哪个商品订单 |
sku | ForeignKey | 外键为GoodsSKU模型 | 约束订单商品的sku |
count | IntegerField | default=1 | 订单商品数量 |
price | DecimalField | max_digits=10, decimal_places=2 | 商品单价 |
comment | TextField | default="" | 评价信息 |
User
;表名:df_users
AbstractUser
,导包 from django.contrib.auth.models import AbstractUser
settings.py
文件中设置:AUTH_USER_MODEL = ‘应用.用户模型类‘
from django.db import models from django.contrib.auth.models import AbstractUser from utils.models import BaseModel from django.conf import settings from goods.models import GoodsSKU from itsdangerous import TimedJSONWebSignatureSerializer as Serializer class User(AbstractUser, BaseModel): """用户""" class Meta: db_table = "df_users" def generate_active_token(self): """生成激活令牌""" serializer = Serializer(settings.SECRET_KEY, 3600) token = serializer.dumps({"confirm": self.id}) # 返回bytes类型 return token.decode() class Address(BaseModel): """地址""" user = models.ForeignKey(User, verbose_name="所属用户") receiver_name = models.CharField(max_length=20, verbose_name="收件人") receiver_mobile = models.CharField(max_length=11, verbose_name="联系电话") detail_addr = models.CharField(max_length=256, verbose_name="详细地址") zip_code = models.CharField(max_length=6, verbose_name="邮政编码") class Meta: db_table = "df_address"
from django.db import models from utils.models import BaseModel from tinymce.models import HTMLField class GoodsCategory(BaseModel): """商品类别表""" name = models.CharField(max_length=20, verbose_name="名称") logo = models.CharField(max_length=100, verbose_name="标识") image = models.ImageField(upload_to="category", verbose_name="图片") class Meta: db_table = "df_goods_category" verbose_name = "商品类别" # admin站点使用 verbose_name_plural = verbose_name def __str__(self): return self.name class Goods(BaseModel): """商品SPU表""" name = models.CharField(max_length=100, verbose_name="名称") desc = HTMLField(verbose_name="详细介绍", default="", blank=True) class Meta: db_table = "df_goods" verbose_name = "商品" verbose_name_plural = verbose_name def __str__(self): return self.name class GoodsSKU(BaseModel): """商品SKU表""" category = models.ForeignKey(GoodsCategory, verbose_name="类别") goods = models.ForeignKey(Goods, verbose_name="商品") name = models.CharField(max_length=100, verbose_name="名称") title = models.CharField(max_length=200, verbose_name="简介") unit = models.CharField(max_length=10, verbose_name="销售单位") price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="价格") stock = models.IntegerField(default=0, verbose_name="库存") sales = models.IntegerField(default=0, verbose_name="销量") default_image = models.ImageField(upload_to="goods", verbose_name="图片") status = models.BooleanField(default=True, verbose_name="是否上线") class Meta: db_table = "df_goods_sku" verbose_name = "商品SKU" verbose_name_plural = verbose_name def __str__(self): return self.name class GoodsImage(BaseModel): """商品图片""" sku = models.ForeignKey(GoodsSKU, verbose_name="商品SKU") image = models.ImageField(upload_to="goods", verbose_name="图片") class Meta: db_table = "df_goods_image" verbose_name = "商品图片" verbose_name_plural = verbose_name def __str__(self): return str(self.sku) class IndexGoodsBanner(BaseModel): """主页轮播商品展示""" sku = models.ForeignKey(GoodsSKU, verbose_name="商品SKU") image = models.ImageField(upload_to="banner", verbose_name="图片") index = models.SmallIntegerField(default=0, verbose_name="顺序") class Meta: db_table = "df_index_goods" verbose_name = "主页轮播商品" verbose_name_plural = verbose_name def __str__(self): return str(self.sku) class IndexCategoryGoodsBanner(BaseModel): """主页分类商品展示""" DISPLAY_TYPE_CHOICES = ( (0, "标题"), (1, "图片") ) category = models.ForeignKey(GoodsCategory, verbose_name="商品类别") sku = models.ForeignKey(GoodsSKU, verbose_name="商品SKU") display_type = models.SmallIntegerField(choices=DISPLAY_TYPE_CHOICES, verbose_name="展示类型") index = models.SmallIntegerField(default=0, verbose_name="顺序") class Meta: db_table = "df_index_category_goods" verbose_name = "主页分类展示商品" verbose_name_plural = verbose_name def __str__(self): return str(self.sku) class IndexPromotionBanner(BaseModel): """主页促销活动展示""" name = models.CharField(max_length=50, verbose_name="活动名称") url = models.URLField(verbose_name="活动连接") image = models.ImageField(upload_to="banner", verbose_name="图片") index = models.SmallIntegerField(default=0, verbose_name="顺序") class Meta: db_table = "df_index_promotion" verbose_name = "主页促销活动" verbose_name_plural = verbose_name def __str__(self): return self.name
from django.db import models from utils.models import BaseModel from users.models import User, Address from goods.models import GoodsSKU class OrderInfo(BaseModel): """订单信息""" PAY_METHODS = { 1: "货到付款", 2: "支付宝", } PAY_METHODS_ENUM = { "CASH": 1, "ALIPAY": 2 } PAY_METHOD_CHOICES = ( (1, "货到付款"), (2, "支付宝"), ) ORDER_STATUS = { 1: "待支付", 2: "待发货", 3: "待收货", 4: "待评价", 5: "已完成", } ORDER_STATUS_ENUM = { "UNPAID": 1, "UNSEND": 2, "UNRECEIVED": 3, "UNCOMMENT": 4, "FINISHED": 5 } ORDER_STATUS_CHOICES = ( (1, "待支付"), (2, "待发货"), (3, "待收货"), (4, "待评价"), (5, "已完成"), ) order_id = models.CharField(max_length=64, primary_key=True, verbose_name="订单号") user = models.ForeignKey(User, verbose_name="下单用户") address = models.ForeignKey(Address, verbose_name="收获地址") total_count = models.IntegerField(default=1, verbose_name="商品总数") total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="商品总金额") trans_cost = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="运费") pay_method = models.SmallIntegerField(choices=PAY_METHOD_CHOICES, default=1, verbose_name="支付方式") status = models.SmallIntegerField(choices=ORDER_STATUS_CHOICES, default=1, verbose_name="订单状态") trade_id = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name="支付编号") class Meta: db_table = "df_order_info" class OrderGoods(BaseModel): """订单商品""" order = models.ForeignKey(OrderInfo, verbose_name="订单") sku = models.ForeignKey(GoodsSKU, verbose_name="订单商品") count = models.IntegerField(default=1, verbose_name="数量") price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="单价") comment = models.TextField(default="", verbose_name="评价信息") class Meta: db_table = "df_order_goods"
apps
:应用目录,包含用户、商品、订单、购物车四个应用dailyfresh
:项目同名目录static
:静态文件目录,包含images、css、js、html ...templates
:模板文件目录utils
:实用用工具类,包含模型基类 ...注意:该项目的应用在apps文件目录下,不是在项目根目录下
# 以用户模块为例 cd Desktop/dailyfresh/apps/ python ../manage.py startapp users
DATABASES = { ‘default‘: { ‘ENGINE‘: ‘django.db.backends.mysql‘, ‘NAME‘: ‘dailyfresh‘, ‘HOST‘: ‘192.168.24.136‘, # MySQL数据库地址 ‘PORT‘: ‘3306‘, ‘USER‘: ‘root‘, ‘PASSWORD‘: ‘mysql‘, } }
users
、goods
、orders
应用中定义好对应的模型类
cart
应用中暂时不定义模型类,其中的数据是使用redis数据库维护的users
应用中的模型类User
是使用Django自带的用户认证系统维护的
迁移前,需要在settings.py
文件中设置:AUTH_USER_MODEL = ‘应用.用户模型类‘
settings.py
中设置AUTH_USER_MODEL
时,编码规则为‘应用.用户模型类‘
apps/
文件目录下,为了保证正确的编码,我们需要增加导包路径同时,为了配合AUTH_USER_MODEL
的配置,应用的安装直接使用users
,不要使用apps.users
import sys sys.path.insert(1, os.path.join(BASE_DIR, ‘apps‘))
STATIC_URL = ‘/static/‘ STATICFILES_DIRS = [os.path.join(BASE_DIR, ‘static‘)]
users
中
def register(request): """返回注册页面""" return render(request, ‘register.html‘)
项目中的urls.py
urlpatterns = [ url(r‘^admin/‘, include(admin.site.urls)), # 访问用户模块的路由配置 url(r‘^users/‘, include(‘apps.users.urls‘,namespace=‘users‘)), ]
users应用中的urls.py
urlpatterns = [from django.conf.urls import url from apps.users import views urlpatterns = [ # 注册 url(r‘^register$‘, views.register, name=‘register‘), ]
register
后面是否加/
,根据公司需求而定
思考:一个register视图,是否可以处理两种逻辑?比如get和post请求逻辑。
def register(request): """处理注册""" # 获取请求方法,判断是GET/POST请求 if request.method == ‘GET‘: # 处理GET请求,返回注册页面 return render(request, ‘register.html‘) else: # 处理POST请求,实现注册逻辑 return HttpResponse(‘这里实现注册逻辑‘)
View
from django.views.generic import View
或 from django.views.generic.base import View
应用/urls.py
中配置路由时,使用类视图的as_view()
方法 ,将类视图转成视图函数dispatch()
方法将具体的request分发至对应请求方式的处理方法中,比如get、post ...class RegisterView(View): """类视图:处理注册""" def get(self, request): """处理GET请求,返回注册页面""" return render(request, ‘register.html‘) def post(self, request): """处理POST请求,实现注册逻辑""" return HttpResponse(‘这里实现注册逻辑‘)
urlpatterns = [ # 视图函数:注册 # url(r‘^register$‘, views.register, name=‘register‘), # 类视图:注册 url(r‘^register$‘, views.RegisterView.as_view(), name=‘register‘), ]
如果发现模板中,静态文件是以前端的方式处理的,Django程序猿在使用时,需要修改成Django的处理方式
提示:用户注册逻辑中,包含了用户邮件激活逻辑
class RegisterView(View): """类视图:处理注册""" def get(self, request): """处理GET请求,返回注册页面""" return render(request, ‘register.html‘) def post(self, request): """处理POST请求,实现注册逻辑""" return HttpResponse(‘这里实现注册逻辑‘)
class RegisterView(View): """类视图:处理注册""" def get(self, request): """处理GET请求,返回注册页面""" return render(request, ‘register.html‘) def post(self, request): """处理POST请求,实现注册逻辑""" # 获取注册请求参数 user_name = request.POST.get(‘user_name‘) password = request.POST.get(‘pwd‘) email = request.POST.get(‘email‘) allow = request.POST.get(‘allow‘) return HttpResponse(‘这里实现注册逻辑‘)
class RegisterView(View): """类视图:处理注册""" def get(self, request): """处理GET请求,返回注册页面""" return render(request, ‘register.html‘) def post(self, request): """处理POST请求,实现注册逻辑""" # 获取注册请求参数 user_name = request.POST.get(‘user_name‘) password = request.POST.get(‘pwd‘) email = request.POST.get(‘email‘) allow = request.POST.get(‘allow‘) # 参数校验:缺少任意一个参数,就不要在继续执行 if not all([user_name, password, email]): return redirect(reverse(‘users:register‘)) # 判断邮箱 if not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$", email): return render(request, ‘register.html‘, {‘errmsg‘:‘邮箱格式不正确‘}) # 判断是否勾选协 if allow != ‘on‘: return render(request, ‘register.html‘, {‘errmsg‘: ‘没有勾选用户协议‘}) return HttpResponse(‘这里实现注册逻辑‘)
class RegisterView(View): """类视图:处理注册""" def get(self, request): """处理GET请求,返回注册页面""" return render(request, ‘register.html‘) def post(self, request): """处理POST请求,实现注册逻辑""" # 获取注册请求参数 user_name = request.POST.get(‘user_name‘) password = request.POST.get(‘pwd‘) email = request.POST.get(‘email‘) allow = request.POST.get(‘allow‘) # 参数校验:缺少任意一个参数,就不要在继续执行 if not all([user_name, password, email]): return redirect(reverse(‘users:register‘)) # 判断邮箱 if not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$", email): return render(request, ‘register.html‘, {‘errmsg‘:‘邮箱格式不正确‘}) # 判断是否勾选协 if allow != ‘on‘: return render(request, ‘register.html‘, {‘errmsg‘: ‘没有勾选用户协议‘}) # 保存数据到数据库 try: # 隐私信息需要加密,可以直接使用django提供的用户认证系统完成 user = User.objects.create_user(user_name, email, password) except db.IntegrityError: return render(request, ‘register.html‘, {‘errmsg‘: ‘用户已注册‘}) # 手动的将用户认证系统默认的激活状态is_active设置成False,默认是True user.is_active = False # 保存数据到数据库 user.save() return HttpResponse(‘这里实现注册逻辑‘)
查看保存用户注册信息结果
# 查询出数据,并以列表形式展示 select * from df_users \G
1.服务器激活邮件如何发送?
激活邮件需要异步发送,集成Celery模块可以实现异步任务
2.服务器如何才能知道是谁要激活?
http://127.0.0.1:8000/users/active/user_id
token用于客户端向服务器发送激活请求时,服务器对用户身份的识别
class ActiveView(View): """邮件激活""" def get(self, request, token): """处理激活请求""" pass
# 邮件激活 url(r‘^active/(?P<token>.+)$‘, views.ActiveView.as_view(), name=‘active‘),
1.安装 itsdangerous 模块
pip install itsdangerous
2.生成用户激活token的方法封装在User模型类中
loads()解出token字符串,得到用户id明文
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer from django.conf import settings class User(AbstractUser, BaseModel): """用户""" class Meta: db_table = "df_users" def generate_active_token(self): """生成激活令牌""" serializer = Serializer(settings.SECRET_KEY, 3600) token = serializer.dumps({"confirm": self.id}) # 返回bytes类型 return token.decode()
3.生成激活token方法的调用
token = user.generate_active_token()
签名过期的异常
提示:Django需要知道是谁在帮它发邮件,所以需要提前配置邮箱服务器
settings.py
中配置邮件服务器参数dailyfreshzxc@yeah.net
发件人邮箱授权
settings.py
中配置邮件服务器参数
EMAIL_BACKEND = ‘django.core.mail.backends.smtp.EmailBackend‘ # 导入邮件模块 EMAIL_HOST = ‘smtp.yeah.net‘ # 发邮件主机 EMAIL_PORT = 25 # 发邮件端口 EMAIL_HOST_USER = ‘dailyfreshzxc@yeah.net‘ # 授权的邮箱 EMAIL_HOST_PASSWORD = ‘dailyfresh123‘ # 邮箱授权时获得的密码,非注册登录密码 EMAIL_FROM = ‘天天生鲜<dailyfreshzxc@yeah.net>‘ # 发件人抬头
from django.core.mail import send_mail from django.conf import settings def send_active_email(to_email, user_name, token): """封装发送邮件方法""" subject = "天天生鲜用户激活" # 标题 body = "" # 文本邮件体 sender = settings.EMAIL_FROM # 发件人 receiver = [to_email] # 接收人 html_body = ‘<h1>尊敬的用户 %s, 感谢您注册天天生鲜!</h1>‘ ‘<br/><p>请点击此链接激活您的帐号<a href="http://127.0.0.1:8000/users/active/%s">‘ ‘http://127.0.0.1:8000/users/active/%s</a></p>‘ % (user_name, token, token) send_mail(subject, body, sender, receiver, html_message=html_body)
3.安装Celery
# 进入虚拟环境 pip install celery
4.Celery组成结构
Celery组成结构是生产者消费者模型的一种体现
调用python的send_mail()将激活邮件发送出去
from celery import Celery from django.core.mail import send_mail from django.conf import settings # 创建celery应用对象 app = Celery(‘celery_tasks.tasks‘, broker=‘redis://192.168.243.191:6379/4‘) @app.task def send_active_email(to_email, user_name, token): """发送激活邮件""" subject = "天天生鲜用户激活" # 标题 body = "" # 文本邮件体 sender = settings.EMAIL_FROM # 发件人 receiver = [to_email] # 接收人 html_body = ‘<h1>尊敬的用户 %s, 感谢您注册天天生鲜!</h1>‘ ‘<br/><p>请点击此链接激活您的帐号<a href="http://127.0.0.1:8000/users/active/%s">‘ ‘http://127.0.0.1:8000/users/active/%s</a></p>‘ %(user_name, token, token) send_mail(subject, body, sender, receiver, html_message=html_body)
作为中间人,我们有几种方案可选择:
1.RabbitMQ
使用RabbitMQ的细节参照以下链接: http://docs.celeryproject.org/en/latest/getting-started/brokers/rabbitmq.html#broker-rabbitmq
如果使用的是Ubuntu或者Debian发行版的Linux,可以直接通过命令安装RabbitMQ: sudo apt-get install rabbitmq-server
2.Redis
Celery服务器创建worker步骤
1.把项目代码拷贝一份到ubuntu虚拟机中
作用:让Celery的worker能够加载Django配置环境
import os os.environ["DJANGO_SETTINGS_MODULE"] = "dailyfresh.settings" # 放到Celery服务器上时添加的代码 import django django.setup()
2.终端创建worker
celery -A celery_tasks.tasks worker -l info
3.开启redis-server,查看broker
4.测试发邮件
5.查看worker收到的异步任务消息
class RegisterView(View): """类视图:处理注册""" def get(self, request): """处理GET请求,返回注册页面""" return render(request, ‘register.html‘) def post(self, request): """处理POST请求,实现注册逻辑""" # 获取注册请求参数 user_name = request.POST.get(‘user_name‘) password = request.POST.get(‘pwd‘) email = request.POST.get(‘email‘) allow = request.POST.get(‘allow‘) # 参数校验:缺少任意一个参数,就不要在继续执行 if not all([user_name, password, email]): return redirect(reverse(‘users:register‘)) # 判断邮箱 if not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$", email): return render(request, ‘register.html‘, {‘errmsg‘:‘邮箱格式不正确‘}) # 判断是否勾选协 if allow != ‘on‘: return render(request, ‘register.html‘, {‘errmsg‘: ‘没有勾选用户协议‘}) # 保存数据到数据库 try: # 隐私信息需要加密,可以直接使用django提供的用户认证系统完成 user = User.objects.create_user(user_name, email, password) except db.IntegrityError: return render(request, ‘register.html‘, {‘errmsg‘: ‘用户已注册‘}) # 手动的将用户认证系统默认的激活状态is_active设置成False,默认是True user.is_active = False # 保存数据到数据库 user.save() # 生成激活token token = user.generate_active_token() # celery发送激活邮件:异步完成,发送邮件不会阻塞结果的返回 send_active_email.delay(email, user_name, token) # 返回结果:比如重定向到首页 return redirect(reverse(‘goods:index‘))
class ActiveView(View): """用户激活""" def get(self, request, token): # 创建序列化器 serializer = Serializer(settings.SECRET_KEY, 3600) try: # 使用序列化器,获取token明文信息,需要判断签名是否过期 result = serializer.loads(token) except SignatureExpired: # 提示激活链接已过期 return HttpResponse(‘激活链接已过期‘) # 获取用户id user_id = result.get(‘confirm‘) try: # 查询需要激活的用户,需要判断查询的用户是否存在 user = User.objects.get(id=user_id) except User.DoesNotExist: # 提示用户不存在 return HttpResponse(‘用户不存在‘) # 设置激活用户的is_active为Ture user.is_active = True # 保存数据到数据库 user.save() # 响应信息给客户端 return redirect(reverse(‘users:login‘))
login(request, user)
django_session
数据库表中django-redis-sessions
来辅助完成django-redis
来辅助完成(功能更加丰富,推荐使用)
class LoginView(View): """登陆""" def get(self, request): """响应登陆页面""" return render(request, ‘login.html‘) def post(self, request): """处理登陆逻辑""" # 获取用户名和密码 user_name = request.POST.get(‘username‘) password = request.POST.get(‘pwd‘) # 参数校验 if not all([user_name, password]): return redirect(reverse(‘users:login‘)) # django用户认证系统判断是否登陆成功 user = authenticate(username=user_name, password=password) # 验证登陆失败 if user is None: # 响应登录页面,提示用户名或密码错误 return render(request, ‘login.html‘, {‘errmsg‘:‘用户名或密码错误‘}) # 验证登陆成功,并判断是否是激活用户 if user.is_active is False: # 如果不是激活用户 return render(request, ‘login.html‘, {‘errmsg‘:‘用户未激活‘}) # 使用django的用户认证系统,在session中保存用户的登陆状态 login(request, user) # 登陆成功,重定向到主页 return redirect(reverse(‘goods:index‘))
以下配置是配合
Django用户认证系统
的login()
方法,完成session信息
存储
1.安装django-redis
pip install django-redis
2.settings.py
文件配置django-redis
# 缓存 CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://192.168.243.193:6379/5", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } } } # Session # http://django-redis-chs.readthedocs.io/zh_CN/latest/#session-backend SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "default"
登陆-->没有记住用户名-->退出浏览器-->再打开网页-->状态没有保持-->需要再登陆
登陆-->记住用户名-->退出浏览器-->再打开网页-->状态依然保持-->不用再登陆直接访问网站
结论:记住用户就是设置session有效期
request.session.set_expiry(value)
# 获取是否勾选‘记住用户名‘ remembered = request.POST.get(‘remembered‘) # 登入用户 login(request, user) # 判断是否是否勾选‘记住用户名‘ if remembered != ‘on‘: # 没有勾选,不需要记住cookie信息,浏览会话结束后过期 request.session.set_expiry(0) else: # 已勾选,需要记住cookie信息,两周后过期 request.session.set_expiry(None) # 响应结果: 重定向到主页 return redirect(reverse(‘goods:index‘))
结论:从request中可以获取到user信息,request.user
class LogoutView(View): """退出登录""" def get(self, request): """处理退出登录逻辑""" # 由Django用户认证系统完成:需要清理cookie和session,request参数中有user对象 logout(request) # 退出后跳转:由产品经理设计 return redirect(reverse(‘goods:index‘))
配置URL正则
url(r‘^logout$‘, views.LogoutView.as_view(), name=‘logout‘),
标签:order mysq return 详细信息 任务队列 car 异步任务 错误 member
原文地址:https://www.cnblogs.com/kaiping23/p/9736841.html