• <Django>天天生鲜项目(一)


    1.认识电商

    1. B2B---企业对企业:阿里巴巴    

    2. C2C---个人对个人:淘宝,瓜子二手车
    3. B2C---企业对个人:唯品会
    4. C2B---个人对企业:海尔商城(定制)----需要做的项目
    5. O2O---线上到线下:美团,饿了么
    6. F2C---工厂到个人:戴尔的一部分
    7. B2B2C---企业-企业-个人:京东商城,天猫商城

    2.Web项目开发流程

      

    1. 项目立项:领导决定做这个项目
    2. 需求分析:需求人提出自己的需求--客户,老板等,分析到底实现什么功能
      1. 分析功能能不能实现,
      2. 功能技术实现难度  
    3. 原型设计:产品经理根据需求画产品的原型图(Axure) 
    4. 后端进行架构设计---架构师
      1. 模块划分
      2. 功能架构
      3. 开发环境的选择
      4. 项目中用到的其他技术
      5. 项目部署架构
      6. 网址开发模式
        1. 前后端分离:Flask
        2. 前后端不分离:Django  
    5. 数据库设计
      1. 分析数据表和表字段
      2. 表之间的关系     
    6. 模块代码实现和单元测试----我们需要做的
      1. 根据分工,实现模块代码
      2. 对你写的代码进行单元测试
    7. 代码整合
      1. 前端UI设计,根据UI形成前端页面, 
      2. 整合前后端   
    8. 集成测试:代码合起来,检测整个流程有没有问题
    9. 网址发布:项目部署上线   

    3.需求分析

    3.1 用户模块

    1)  注册页

    • 注册时校验用户名是否已被注册。
    • 完成用户信息的注册。
    • 给用户的注册邮箱发送邮件,用户点击邮件中的激活链接完成用户账户的激活。

    2)  登录页

    • 实现用户的登录功能。

    3)  用户中心

    • 用户中心信息页:显示登录用户的信息,包括用户名、电话和地址,同时页面下方显示出用户最近浏览的商品信息。
    • 用户中心地址页:显示登录用户的默认收件地址,页面下方的表单可以新增用户的收货地址。
    • 用户中心订单页:显示登录用户的订单信息。

    4)  其他

    • 如果用户已经登录,页面顶部显示登录用户的信息。

    3.2 商品相关

    1)  首页

    • 动态指定首页轮播商品信息。
    • 动态指定首页活动信息。
    • 动态获取商品的种类信息并显示。
    • 动态指定首页显示的每个种类的商品(包括图片商品和文字商品)。
    • 点击某一个商品时跳转到商品的详情页面。

    2)  商品详情页

    • 显示出某个商品的详情信息。
    • 页面的左下方显示出该种类商品的2个新品信息。

    3)商品列表页

    • 显示出某一个种类商品的列表数据,分页显示并支持按照默认、价格、和人气进行排序。
    • 页面的左下方显示出该种类商品的2个新品信息。

    4)其他

    • 通过页面搜索框搜索商品信息。

    3.3 购物车相关

    • 列表页和详情页将商品添加到购物车。
    • 用户登录后,首页,详情页,列表页显示登录用户购物车中商品的数目。
    • 购物车页面:对用户购物车中商品的操作。如选择某件商品,增加或减少购物车中商品的数目。

    3.4 订单相关

    • 提交订单页面:显示用户准备购买的商品信息。
    • 点击提交订单完成订单的创建。
    • 用户中心订单页显示用户的订单信息。
    • 点击支付完成订单的支付。

    4.架构设计

    4.1 页面图

    4.2 功能图

    4.3 部署图

      

    项目架构 

     

     

    5.数据库设计

    用户模块

    • 用户表
      • ID  
      • 用户名
      • 密码
      • 邮箱
      • 激活标识:0,1
      • 权限标识
    • 地址表
      • ID  
      • 收件人
      • 详细信息
      • 联系方式
      • 地址 
      • 是否默认:0,1
      • 用户ID 

    商品模块  

    • 商品表(SKU) 

      • ID
      • 名称
      • 简介
      • 价格
      • 单位
      • 商品库存
      • 商品图片---记录一张,增加效率,以空间换取时间
      • 评论
      • 状态标记:0,1
      • 种类ID
      • SPUID
    • 商品种类表
      • ID
      • 种类名称
      • LOGO
      • 种类图片
    • 商品图片表
      • ID
      • 图片
      • 商品ID(sku)   
    • 商品SPU表
      • ID
      • 名称
      • 详情  
    • 首页轮播商品表
      • ID
      • SKUID
      • 图片
      • index 
    • 首页促销表
      • ID
      • 图片 
      • 活动URL地址
      • index
    • 首页分类商品展示表
      • ID
      • sku ID
      • 种类ID
      • index
      • 展示标识:图片或文字

    购物车模块

    redis实现购物车功能

    redis保存用户历史浏览记录

    订单模块

    • 订单信息表
      • 订单编号
      • 地址ID
      • 支付方式
      • 总金额
      • 总数目
      • 运费
      • 支付状态
      • 创建时间 
    • 订单商品表
      • ID
      • SKU ID
      • 商品数量
      • 商品价格  
      • 评论                     

    SKU与SPU概念

    SPU = Standard Product Unit (标准产品单位)

    SPU 是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述 了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个 SPU。

    例如:iphone7 就是一个 SPU,与商家,与颜色、款式、套餐都无关。

    SKU=stock keeping unit(库存量单位)

    SKU 即库存进出计量的单位, 可以是以件、盒、托盘等为单位。

    SKU 是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。 在服装、鞋类商品中使用最多最普遍。

    例如:纺织品中一个 SKU 通常表示:规格、颜色、款式。

     

    正式开始写。。。

    001.创建项目

    django-admin startproject dailyfresh
    

    002.配置静态文件路径及拷贝静态文件

    配置静态文件(settings.py)

    # 用于隐藏(伪装),配置更改(逻辑显示路径)
    STATIC_URL = '/static/'
    # 静态文件的存放路径(真实路径)
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static'),
    ]  

    创建static文件夹,在这个文件夹下创建应用名的文件夹(类似模板的创建)---与manage.py文件同级

    并将静态文件拷贝进去链接:https://pan.baidu.com/s/1eXFDCimhBdT_HDZFAgZZ6Q   提取码:r0kh

    配置模板文件路径(settings.py)

            'DIRS': [os.path.join(BASE_DIR,"templates")],  

    创建模板文件夹---与manage.py同级  

    配置数据库信息(settings.py)

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'tiantian',
            'USER':'root',
            'PASSWORD':'root',
            'HOST':'localhost',
            'POST':'3306',
        }
    }  

    手动在建立tiantian数据库

    mysql> create database tiantian charset=utf8;
    Query OK, 1 row affected (0.12 sec)
    

    003.创建四个模块 

    与manage.py同级

    用户模块

    python manage.py startapp user  

    商品模块

    python manage.py startapp goods

    购物车模块

    python manage.py startapp  cart  

    订单模块

    python manage.py startapp order

    如果应用较多,新键一个apps包,将所有应用都放在apps包中  

    为apps文件夹加一个搜索路径(settings.py)

    import sys
    # 放在BASE_DIR下方
    sys.path.insert(0,os.path.join(BASE_DIR,'apps'))

    安装,注册应用

    INSTALLED_APPS = [
    	'user',
    	'goods',
    	'order',
    	'cart',
    ]

    配中文时区(settings.py)

    LANGUAGE_CODE = 'zh-hans'
    
    TIME_ZONE = 'Asia/Shanghai'

    配根级url(urls.py)

    from django.conf.urls import url
    from django.contrib import admin
    # 分发url
    from django.conf.urls import include
    
    urlpatterns = [
    	url(r'^admin/', admin.site.urls),
    	url(r'^user/', include('user.urls',namespace='user')),  # 用户模块
    	url(r'^cart/', include('cart.urls',namespace='cart')),  # 购物车模块
    	url(r'^order/', include('order.urls',namespace='order')),  # 订单模块
    	url(r'^', include('goods.urls',namespace='goods')),  # 商品模块
    ] 

    每个应用建立相应的子级urls.py 

    from django.conf.urls import url
    from . import views
    
    
    urlpatterns=[
    
    ] 

    建立模型类继承模板,新键db包,在里面新键base_model.py文件

    from django.db import models
    
    
    class BaseModel(models.Model):
        '''模型抽象基类'''
        create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
        update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
        is_delete = models.BooleanField(default=False, verbose_name='删除标记')
    
        class Meta:
            # 说明是一个抽象模型类
            abstract = True
    

      

    设计用户模型类(models.py) 

    from django.db import models
    from django.contrib.auth.models import AbstractUser
    from db.base_model import BaseModel
    # Create your models here.
    
    
    class User(AbstractUser, BaseModel):
        '''用户模型类'''
    
        class Meta:
            db_table = 'df_user'
            verbose_name = '用户'
            verbose_name_plural = verbose_name
    
    
    class Address(BaseModel):
        '''地址模型类'''
        user = models.ForeignKey('User', verbose_name='所属账户')
        receiver = models.CharField(max_length=20, verbose_name='收件人')
        addr = models.CharField(max_length=256, verbose_name='收件地址')
        zip_code = models.CharField(max_length=6, null=True, verbose_name='邮政编码')
        phone = models.CharField(max_length=11, verbose_name='联系电话')
        is_default = models.BooleanField(default=False, verbose_name='是否默认')
    
        class Meta:
            db_table = 'df_address'
            verbose_name = '地址'
            verbose_name_plural = verbose_name
    

      

    商品模型类(models.py)

    from django.db import models
    from db.base_model import BaseModel
    from tinymce.models import HTMLField
    # Create your models here.
    
    
    class GoodsType(BaseModel):
        '''商品类型模型类'''
        name = models.CharField(max_length=20, verbose_name='种类名称')
        logo = models.CharField(max_length=20, verbose_name='标识')
        image = models.ImageField(upload_to='type', verbose_name='商品类型图片')
    
        class Meta:
            db_table = 'df_goods_type'
            verbose_name = '商品种类'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.name
    
    
    class GoodsSKU(BaseModel):
        '''商品SKU模型类'''
        status_choices = (
            (0, '下线'),
            (1, '上线'),
        )
        type = models.ForeignKey('GoodsType', verbose_name='商品种类')
        goods = models.ForeignKey('Goods', verbose_name='商品SPU')
        name = models.CharField(max_length=20, verbose_name='商品名称')
        desc = models.CharField(max_length=256, verbose_name='商品简介')
        price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='商品价格')
        unite = models.CharField(max_length=20, verbose_name='商品单位')
        image = models.ImageField(upload_to='goods', verbose_name='商品图片')
        stock = models.IntegerField(default=1, verbose_name='商品库存')
        sales = models.IntegerField(default=0, verbose_name='商品销量')
        status = models.SmallIntegerField(default=1, choices=status_choices, verbose_name='商品状态')
    
        class Meta:
            db_table = 'df_goods_sku'
            verbose_name = '商品'
            verbose_name_plural = verbose_name
    
    
    class Goods(BaseModel):
        '''商品SPU模型类'''
        name = models.CharField(max_length=20, verbose_name='商品SPU名称')
        # 富文本类型:带有格式的文本
        detail = HTMLField(blank=True, verbose_name='商品详情')
    
        class Meta:
            db_table = 'df_goods'
            verbose_name = '商品SPU'
            verbose_name_plural = verbose_name
    
    
    class GoodsImage(BaseModel):
        '''商品图片模型类'''
        sku = models.ForeignKey('GoodsSKU', verbose_name='商品')
        image = models.ImageField(upload_to='goods', verbose_name='图片路径')
    
        class Meta:
            db_table = 'df_goods_image'
            verbose_name = '商品图片'
            verbose_name_plural = verbose_name
    
    
    class IndexGoodsBanner(BaseModel):
        '''首页轮播商品展示模型类'''
        sku = models.ForeignKey('GoodsSKU', verbose_name='商品')
        image = models.ImageField(upload_to='banner', verbose_name='图片')
        index = models.SmallIntegerField(default=0, verbose_name='展示顺序')
    
        class Meta:
            db_table = 'df_index_banner'
            verbose_name = '首页轮播商品'
            verbose_name_plural = verbose_name
    
    
    class IndexTypeGoodsBanner(BaseModel):
        '''首页分类商品展示模型类'''
        DISPLAY_TYPE_CHOICES = (
            (0, "标题"),
            (1, "图片")
        )
    
        type = models.ForeignKey('GoodsType', verbose_name='商品类型')
        sku = models.ForeignKey('GoodsSKU', verbose_name='商品SKU')
        display_type = models.SmallIntegerField(default=1, choices=DISPLAY_TYPE_CHOICES, verbose_name='展示类型')
        index = models.SmallIntegerField(default=0, verbose_name='展示顺序')
    
        class Meta:
            db_table = 'df_index_type_goods'
            verbose_name = "主页分类展示商品"
            verbose_name_plural = verbose_name
    
    
    class IndexPromotionBanner(BaseModel):
        '''首页促销活动模型类'''
        name = models.CharField(max_length=20, 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

    其中类型HTMLFiel需要在settings.py中设置 

    INSTALLED_APPS = [
    	'tinymce',  # HttpField--富文本编辑器
    ]
    

      

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

    在根目录urls.py中配置链接

    url(r'^tinyme/', include('tinymce.urls')), # 富文本编辑器

    订单模型类

    from django.db import models
    from db.base_model import BaseModel
    # Create your models here.
    
    
    class OrderInfo(BaseModel):
        '''订单模型类'''
        PAY_METHOD_CHOICES = (
            (1, '货到付款'),
            (2, '微信支付'),
            (3, '支付宝'),
            (4, '银联支付')
        )
    
        ORDER_STATUS_CHOICES = (
            (1, '待支付'),
            (2, '待发货'),
            (3, '待收货'),
            (4, '待评价'),
            (5, '已完成')
        )
    
        order_id = models.CharField(max_length=128, primary_key=True, verbose_name='订单id')
        user = models.ForeignKey('user.User', verbose_name='用户')
        addr = models.ForeignKey('user.Address', verbose_name='地址')
        pay_method = models.SmallIntegerField(choices=PAY_METHOD_CHOICES, default=3, verbose_name='支付方式')
        total_count = models.IntegerField(default=1, verbose_name='商品数量')
        total_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='商品总价')
        transit_price = models.DecimalField(max_digits=10, decimal_places=2,verbose_name='订单运费')
        order_status = models.SmallIntegerField(choices=ORDER_STATUS_CHOICES, default=1, verbose_name='订单状态')
        trade_no = models.CharField(max_length=128, verbose_name='支付编号')
    
        class Meta:
            db_table = 'df_order_info'
            verbose_name = '订单'
            verbose_name_plural = verbose_name
    
    
    class OrderGoods(BaseModel):
        '''订单商品模型类'''
        order = models.ForeignKey('OrderInfo', verbose_name='订单')
        sku = models.ForeignKey('goods.GoodsSKU', verbose_name='商品SKU')
        count = models.IntegerField(default=1, verbose_name='商品数目')
        price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='商品价格')
        comment = models.CharField(max_length=256, verbose_name='评论')
    
        class Meta:
            db_table = 'df_order_goods'
            verbose_name = '订单商品'
            verbose_name_plural = verbose_name
    

      

    替换Django自身认证系统使用的模型类,需要在settings.py中设置

    # django认证系统使用的模型类,应用名.类名
    AUTH_USER_MODEL = 'user.User'

    在settings.py同级的__init__文件中,写入mysql_db对python3的支持  

    import pymysql
    pymysql.install_as_MySQLdb() 

    生成迁移文件

    python manage.py makemigrations
    

    执行迁移  

    python manage.py migrate

    出现错误

    ValueError: Related model 'user.User' cannot be resolved
    

    原因是外键约束阻止的其他表的生成,先生成user表就可以了

    python manage.py makemigrations --empty user

    python manage.py makemigrations

    python manage.py migrate user
    
    
    python manage.py migrate 
    

      

    001.用户模块

    在模板下创建APP同名目录,将用户相关的模板文件拷贝进来

           

    定义视图(views.py)---第一次显示注册页面

    from django.shortcuts import render
    
    # Create your views here.
    def register(request):
    	'''
    	:param request:
    	:return:注册页面
    	'''
    	return render(request,'user/register.html')  

     

    在APP目录下建子级urls.py文件(urls.py-子级)

    from django.conf.urls import url
    from . import views
    
    
    urlpatterns=[
        url(r'^register/$', views.register,name='register'),
    ]

    运行项目查看效果 

    python manage.py runserver  

    页面  http://127.0.0.1:8000/user/register/效果

    静态文件没有引用过来,可能原因1.静态文件路径没配好。2.没有导入静态文件。3.模板导入的静态文件路径不对,这里是第三条

    修改为:

    刷新页面---成功显示

    根据页面的继承情况,创建底部模板文件base_bottom.html---templates文件夹下和df_user同级(base_bottom.html)          

    <!--继承的头部-->
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    <head>
    	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    	<title>{{ title }}天天生鲜</title>
    	<link rel="stylesheet" type="text/css" href="/static/css/reset.css">
    	<link rel="stylesheet" type="text/css" href="/static/css/main.css">
    	<script type="text/javascript" src="/static/js/jquery-1.12.4.min.js"></script>
    	{% block head%}
    	{% endblock head%}
    </head>
    <body>
    
        {% block body%}
    	{% endblock body%}
    
    <!--继承的底部-->
    	<div class="footer no-mp">
    		<div class="foot_link">
    			<a href="#">关于我们</a>
    			<span>|</span>
    			<a href="#">联系我们</a>
    			<span>|</span>
    			<a href="#">招聘人才</a>
    			<span>|</span>
    			<a href="#">友情链接</a>
    		</div>
    		<p>CopyRight © 2016 北京天天生鲜信息技术有限公司 All Rights Reserved</p>
    		<p>电话:010-****888    京ICP备*******8号</p>
    	</div>
    
    </body>
    </html>

    根据继承情况重新编辑register.html文件(register.html)

    <!--继承于base_bottom.html页面-->
    {% extends 'base_bottom.html' %}
    
    <!--head-->
    {% block head%}
    <script type="text/javascript" src="/static/js/register.js"></script>
    {% endblock head%}
    
    <!--body-->
    {% block body%}
    	<div class="register_con">
    		<div class="l_con fl">
    			<a class="reg_logo"><img src="/static/images/logo02.png"></a>
    			<div class="reg_slogan">足不出户  ·  新鲜每一天</div>
    			<div class="reg_banner"></div>
    		</div>
    
    		<div class="r_con fr">
    			<div class="reg_title clearfix">
    				<h1>用户注册</h1>
    				<a href="#">登录</a>
    			</div>
    			<div class="reg_form clearfix">
    				<form>
    				<ul>
    					<li>
    						<label>用户名:</label>
    						<input type="text" name="user_name" id="user_name">
    						<span class="error_tip">提示信息</span>
    					</li>
    					<li>
    						<label>密码:</label>
    						<input type="password" name="pwd" id="pwd">
    						<span class="error_tip">提示信息</span>
    					</li>
    					<li>
    						<label>确认密码:</label>
    						<input type="password" name="cpwd" id="cpwd">
    						<span class="error_tip">提示信息</span>
    					</li>
    					<li>
    						<label>邮箱:</label>
    						<input type="text" name="email" id="email">
    						<span class="error_tip">提示信息</span>
    					</li>
    					<li class="agreement">
    						<input type="checkbox" name="allow" id="allow" checked="checked">
    						<label>同意”天天生鲜用户使用协议“</label>
    						<span class="error_tip2">提示信息</span>
    					</li>
    					<li class="reg_sub">
    						<input type="submit" value="注 册" name="">
    					</li>
    				</ul>
    				</form>
                        {{ errmsg }} </div> </div> </div> {% endblock body%} <!--bottom-->  

    重新查看register页面,如果没有变化就说明模板设置成功

    修改form表单的提交位置及防止跨越请求伪造---表单提交都需要(register.html)

    <form action="/user/register_handle/" method="post">
       {% csrf_token %}  

    定义视图接受注册信息  

    # coding=utf-8
    from django.shortcuts import render, redirect
    # 反向解析地址
    from django.core.urlresolvers import reverse
    
    from .models import User
    from hashlib import sha1
    import re
    # json
    from django.http import JsonResponse
    
    
    # Create your views here.
    # /user/register/
    def register(request):
    	'''
    	:param request:
    	:return:显示注册页面
    	'''
    	return render(request, 'user/register.html')
    
    
    def register_handle(request):
    	'''进行注册处理'''
    	# 1.接受数据
    	post = request.POST
    	# 接收用户输入的值
    	username = post.get('user_name')
    	password = post.get('pwd')
    	password2 = post.get('cpwd')
    	email = post.get('email')
    	allow = post.get('allow')
    	# 2.进行数据校验
    	# 全为true,才返回true
    	if not all([username, password, password2, email]):
    		# 数据不完整
    		return render(request, 'user/register.html', {'errmsg': '数据不完整'})
    	# 校验邮箱
    	if not re.match(r'^[a-z0-9][w.-]*@[a-z0-9-]+(.[a-z]{2,5}){1,2}$',email):
    		return render(request, 'user/register.html', {'errmsg': '邮箱格式不正确'})
    	# 判断2次密码是否一致
    	if password != password2:
    		return render(request, 'user/register.html', {'errmsg': '两次密码不一致'})
    	# 校验是否勾选协议
    	if allow != 'on':
    		return render(request, 'user/register.html', {'errmsg': '请同意协议'})
    
    	# 校验用户名是否重复
    	try:
    		user = User.objects.get(username=username)
    	except User.DoesNotExist:
    		# 用户名不存在
    		user = None
    	if user:
    		# 用户名已存在
    		return render(request, 'user/register.html', {'errmsg': '用户名已存在'})
    	# 3.进行业务处理:进行业务注册
    	# 密码加密
    	s1 = sha1()
    	s1.update(password.encode('utf-8'))
    	password3 = s1.hexdigest()  # 加密后的结果
    	# 创建对象
    	user = User()
    	# 属性赋值
    	user.username = username
    	user.password = password3
    	user.email = email
    	# 默认激活账户,置为0表示没有激活
    	user.is_active = 0
    	user.save()  # 存到数据库里面
    	# 返回应答:注册成功,转到登录页面
    	# return redirect('/user/login/')
    	# 返回首页 反向解析  namespace:name
    	return redirect(reverse('goods:index'))

    添加对应url(urls.py) 

    url(r'^register_handle/$', views.register_handle,name='register_handle'),

    配置goods中的index页面(views.py)

    from django.shortcuts import render
    
    # Create your views here.
    
    
    # 首页
    def index(request):
    	'''首页'''
    	return render(request, 'goods/index.html') 

    配置相应的url(urls.py)

    from django.conf.urls import url
    from . import views
    
    
    urlpatterns=[
    	url(r'^$', views.index,name='index'),
    ]

    在注册表填入正确信息,能跳转到index页面,说明注册页面配置成功

    查看数据库,发现相应注册信息

     注册和处理使用同一个地址,修改表单提交地址---通过请求方式判断

    <form action="/user/register/" method="post">

    修改views.py----register_handle相关函数和url可以删除

    def register(request):
    	'''注册'''
    	if request.method == 'GET':
    		return render(request, 'user/register.html')
    	else:
    		'''进行注册处理'''
    		# 1.接受数据
    		post = request.POST
    		# 接收用户输入的值
    		username = post.get('user_name')
    		password = post.get('pwd')
    		password2 = post.get('cpwd')
    		email = post.get('email')
    		allow = post.get('allow')
    		# 2.进行数据校验
    		# 全为true,才返回true
    		if not all([username, password, password2, email]):
    			# 数据不完整
    			return render(request, 'user/register.html', {'errmsg': '数据不完整'})
    		# 校验邮箱
    		if not re.match(r'^[a-z0-9][w.-]*@[a-z0-9-]+(.[a-z]{2,5}){1,2}$', email):
    			return render(request, 'user/register.html', {'errmsg': '邮箱格式不正确'})
    		# 判断2次密码是否一致
    		if password != password2:
    			return render(request, 'user/register.html', {'errmsg': '两次密码不一致'})
    		# 校验是否勾选协议
    		if allow != 'on':
    			return render(request, 'user/register.html', {'errmsg': '请同意协议'})
    
    		# 校验用户名是否重复
    		try:
    			user = User.objects.get(username=username)
    		except User.DoesNotExist:
    			# 用户名不存在
    			user = None
    		if user:
    			# 用户名已存在
    			return render(request, 'user/register.html', {'errmsg': '用户名已存在'})
    		# 3.进行业务处理:进行业务注册
    		# 密码加密
    		s1 = sha1()
    		s1.update(password.encode('utf-8'))
    		password3 = s1.hexdigest()  # 加密后的结果
    		# 创建对象
    		user = User()
    		# 属性赋值
    		user.username = username
    		user.password = password3
    		user.email = email
    		# 默认激活账户,置为0表示没有激活
    		user.is_active = 0
    		user.save()  # 存到数据库里面
    		# 返回应答:注册成功,转到登录页面
    		# return redirect('/user/login/')
    		# 返回首页 反向解析  namespace:name
    		return redirect(reverse('goods:index'))
    

      

    使用类视图,将请求方式得到的不同结果区分开---更加直观---修改views.py

    访问一个地址的时候,不同的请求方式对应哪个函数

    # coding=utf-8
    from django.shortcuts import render, redirect
    # 反向解析地址
    from django.core.urlresolvers import reverse
    # 类视图
    from django.views.generic import View
    from .models import User
    from hashlib import sha1
    import re
    # json
    from django.http import JsonResponse
    
    
    # Create your views here.
    # /user/register/
    class RegisterView(View):
    	'''注册'''
    	def get(self, request):
    		'''显示注册页面'''
    		return render(request, 'user/register.html')
    
    	def post(self, request):
    		'''进行注册处理'''
    		# 1.接受数据
    		post = request.POST
    		# 接收用户输入的值
    		username = post.get('user_name')
    		password = post.get('pwd')
    		password2 = post.get('cpwd')
    		email = post.get('email')
    		allow = post.get('allow')
    		# 2.进行数据校验
    		# 全为true,才返回true
    		if not all([username, password, password2, email]):
    			# 数据不完整
    			return render(request, 'user/register.html', {'errmsg': '数据不完整'})
    		# 校验邮箱
    		if not re.match(r'^[a-z0-9][w.-]*@[a-z0-9-]+(.[a-z]{2,5}){1,2}$', email):
    			return render(request, 'user/register.html', {'errmsg': '邮箱格式不正确'})
    		# 判断2次密码是否一致
    		if password != password2:
    			return render(request, 'user/register.html', {'errmsg': '两次密码不一致'})
    		# 校验是否勾选协议
    		if allow != 'on':
    			return render(request, 'user/register.html', {'errmsg': '请同意协议'})
    		# 校验用户名是否重复
    		try:
    			user = User.objects.get(username=username)
    		except User.DoesNotExist:
    			# 用户名不存在
    			user = None
    		if user:
    			# 用户名已存在
    			return render(request, 'user/register.html', {'errmsg': '用户名已存在'})
    		# 3.进行业务处理:进行业务注册
    		# 密码加密
    		s1 = sha1()
    		s1.update(password.encode('utf-8'))
    		password3 = s1.hexdigest()  # 加密后的结果
    		# 创建对象
    		user = User()
    		# 属性赋值
    		user.username = username
    		user.password = password3
    		user.email = email
    		# 默认激活账户,置为0表示没有激活
    		user.is_active = 0
    		user.save()  # 存到数据库里面
    		# 返回应答:注册成功,转到登录页面
    		# return redirect('/user/login/')
    		# 返回首页 反向解析  namespace:name
    		return redirect(reverse('goods:index')) 

    修改url

    from django.conf.urls import url
    from . import views
    from .views import RegisterView
    
    
    urlpatterns=[
    	url(r'^register/$', RegisterView.as_view(),name='register'),
    ]
    

      

     增加邮箱验证功能,Django中内置了邮件发送功能,被定义在django.core.mail模块中。发送邮件需要使用SMTP服务器,免费服务器有:163126QQ

    1)注册163邮箱,登录后设置。

    2)在新页面中点击“客户端授权密码”,勾选“开启”,弹出新窗口填写手机验证码。

    3)填写授权码。

    4)提示开启成功。

    settings.py文件写入文件

    # 发送邮件配置
    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    EMAIL_HOST = 'smtp.163.com'
    EMAIL_PORT = 25
    # 发送邮件的邮箱
    EMAIL_HOST_USER = 'hk15827979698@163.com'
    # 在邮箱中设置的客户端授权密码
    EMAIL_HOST_PASSWORD = '****'
    # 收件人看到的发件人
    EMAIL_FROM = '天天生鲜<hk15827979698@163.com>'
    

    修改views.py,可以发送邮件

    # coding=utf-8
    # 过期之后提示
    from django.http import HttpResponse
    from django.shortcuts import render, redirect
    # 反向解析地址
    from django.core.urlresolvers import reverse
    # 类视图
    from django.views.generic import View
    # itsdangerous加密
    from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
    # 过期异常
    from itsdangerous import SignatureExpired
    from django.conf import settings
    # 发送邮件
    from django.core.mail import send_mail
    
    from .models import User
    from hashlib import sha1
    import re
    # json
    from django.http import JsonResponse
    
    
    # Create your views here.
    # /user/register/
    class RegisterView(View):
    	'''注册'''
    
    	def get(self, request):
    		'''显示注册页面'''
    		return render(request, 'user/register.html')
    
    	def post(self, request):
    		'''进行注册处理'''
    		# 1.接受数据
    		post = request.POST
    		# 接收用户输入的值
    		username = post.get('user_name')
    		password = post.get('pwd')
    		password2 = post.get('cpwd')
    		email = post.get('email')
    		allow = post.get('allow')
    		# 2.进行数据校验
    		# 全为true,才返回true
    		if not all([username, password, password2, email]):
    			# 数据不完整
    			return render(request, 'user/register.html', {'errmsg': '数据不完整'})
    		# 校验邮箱
    		if not re.match(r'^[a-z0-9][w.-]*@[a-z0-9-]+(.[a-z]{2,5}){1,2}$', email):
    			return render(request, 'user/register.html', {'errmsg': '邮箱格式不正确'})
    		# 判断2次密码是否一致
    		if password != password2:
    			return render(request, 'user/register.html', {'errmsg': '两次密码不一致'})
    		# 校验是否勾选协议
    		if allow != 'on':
    			return render(request, 'user/register.html', {'errmsg': '请同意协议'})
    		# 校验用户名是否重复
    		try:
    			user = User.objects.get(username=username)
    		except User.DoesNotExist:
    			# 用户名不存在
    			user = None
    		if user:
    			# 用户名已存在
    			return render(request, 'user/register.html', {'errmsg': '用户名已存在'})
    		# 3.进行业务处理:进行业务注册
    		# 密码加密
    		s1 = sha1()
    		s1.update(password.encode('utf-8'))
    		password3 = s1.hexdigest()  # 加密后的结果
    		# 创建对象
    		user = User()
    		# 属性赋值
    		user.username = username
    		user.password = password3
    		user.email = email
    		# 默认激活账户,置为0表示没有激活
    		user.is_active = 0
    		user.save()  # 存到数据库里面
    
    		# 发送激活邮件,包含激活链接
    		# 激活链接需要包含用户的身份信息:http://127.0.0.1:8000/user/active/用户ID
    		# 将用户ID-身份信息加密:itsdangerous   需要先安装
    		serializer = Serializer(settings.SECRET_KEY, 3600)
    		info = {'confirm': user.id}
    		token = serializer.dumps(info)  # token加密信息
    		# 解密
    		# res = serializer.loads(token)
    		# 发邮件
    		subject = '天天生鲜欢迎信息'  # 邮件标题
    		message = '邮件正文'
    		sender = settings.EMAIL_FROM # 发件人
    		receiver = [email] # 收件人列表
    		send_mail(subject,message,sender,receiver)
    		# 返回应答:注册成功,转到登录页面
    		# return redirect('/user/login/')
    		# 返回首页 反向解析  namespace:name
    		return redirect(reverse('goods:index')) 

    可以注册一下试试,可以发送邮件

     

    修改一下发送邮件的信息就可以发送激活邮件

    serializer = Serializer(settings.SECRET_KEY, 3600)
    		info = {'confirm': user.id}
    		token = serializer.dumps(info)  # token加密信息
    		token = token.decode()
    		# 解密
    		# res = serializer.loads(token)
    		# 发邮件
    		subject = '天天生鲜欢迎信息'  # 邮件标题
    		message = ''
    		sender = settings.EMAIL_FROM  # 发件人
    		receiver = [email]  # 收件人列表
    		html_message = '<h1>%s,欢迎您成为天天生鲜注册会员</h1>请点击下面链接激活您的账户<br/>
                        <a href="http://127.0.0.1:8000/user/active/%s">http://127.0.0.1:8000/user/active/%s</a>'%(username,token,token) send_mail(subject, message, sender, receiver,html_message=html_message) # 返回应答:注册成功,转到登录页面

     

    定义类视图,处理激活邮件

    class ActiveView(View):
    	'''用户激活账户'''
    
    	def get(self, request, token):
    		'''用户激活'''
    		# 解密:获取要激活的信息
    		serializer = Serializer(settings.SECRET_KEY, 3600)
    		try:
    			info = serializer.loads(token)
    			# 获取待激活用户ID
    			user_id = info['confirm']
    			# 根据用户ID获取用户信息
    			user = User.objects.get(id=user_id)
    			# 激活标记改为1
    			user.is_active = 1
    			user.save()
    			# 跳转到登录页面
    			return redirect(reverse('user:login'))
    		except SignatureExpired as e:
    			# 激活链接已过期
    			return HttpResponse('激活链接已过期') 

    配置相应的url

    from .views import RegisterView, ActiveView,LoginView
    
    urlpatterns = [
    	url(r'^register/$', RegisterView.as_view(), name='register'),  # 注册
    	url(r'^active/(?P<token>.*)$', ActiveView.as_view(), name='active'),  # 用户激活
    
    	url(r'^login/$', LoginView.as_view(),name='login'),
    ]

    在配置登录view

    # /user/login/
    class LoginView(View):
    	'''登录'''
    
    	def get(self, request):
    		'''显示登录页面'''
    		return render(request, 'user/login.html') 

    激活文件可以跳转到登录页面-修改登录页错误的static文件路径----同注册

     

     

     

     celery异步发送邮件,避免邮件堵塞,造成网址等待时间过长

    用户需要在我们的网站填写注册信息,我们发给用户一封注册激活邮件到用户邮箱,如果由于各种原因,这封邮件发送所需时间较长,那么客户端将会等待很久,造成不好的用户体验.

    我们将耗时任务放到后台异步执行。不会影响用户其他操作。除了注册功能,例如上传,图形处理等等耗时的任务,都可以按照这种思路来解决。

    处理者所在电脑必须联网

    在项目目录下方建立celery_tasks包,并建立tasks.py文件(tasks.py)

    # 使用celery
    from django.core.mail import send_mail
    from django.conf import settings
    from celery import Celery
    import time
    
    # 创建celery类实例对象 8号数据库
    app = Celery('celery_tasks.tasks', broker='redis://127.0.0.1:6379/8')
    
    
    # 定义任务函数
    @app.task
    def send_register_active_email(to_email, username, token):
    	'''发送激活邮件'''
    	# 发邮件
    	subject = '天天生鲜欢迎信息'  # 邮件标题
    	message = ''
    	sender = settings.EMAIL_FROM  # 发件人
    	receiver = [to_email]  # 收件人列表
    	html_message = '<h1>%s,欢迎您成为天天生鲜注册会员</h1>请点击下面链接激活您的账户<br/>' 
    				   '<a href="http://127.0.0.1:8000/user/active/%s">http://127.0.0.1:8000/user/active/%s</a>' % (
    					   username, token, token)
    	send_mail(subject, message, sender, receiver, html_message=html_message)
    

     

    修改view中发送邮件部分

    # 导入celery发送邮件函数
    from celery_tasks.tasks import send_register_active_email
    

      

      # 发邮件,使用celery去发
      send_register_active_email.delay(email,username,token) 

    启动redis服务器

    启动客户端

    redis-cli
    

    拷贝一份代码到另外一个地方,可以一台电脑,也可以不是同一台电脑

    启动任务处理者

    E:celery_workerdailyfresh>celery -A celery_tasks.tasks worker -l info
    

      

    启动项目,重新注册一次,看看邮件的发送情况

    任务处理者报错,redis版本过高

    AttributeError: 'str' object has no attribute 'items'
    

     

    重新启动一个低版本的redis

    启动项目,并注册,提示收到了任务,报错,没有对项目的配置文件进行初始化

     Received task: celery_tasks.tasks.send_register_active_email[758c686c-acba-42ed-9bec-00027f7c4bea]
    

      

    在处理者一端的tasks.py代码中加入,Django环境的初始化

    import os
    import django
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dailyfresh.settings")
    django.setup()
    

      

    在次启动项目,启动处理者,进行注册操作,发送成功

    用户登录

    修改login.html中的静态文件及from提交地址

        <form method="post" action="">
                       {% csrf_token %}
    

      

    修改类视图

    # django的认证函数和session
    from django.contrib.auth import authenticate, login
    
    class LoginView(View):
    	'''登录'''
    
    	def get(self, request):
    		'''显示登录页面'''
    		return render(request, 'user/login.html')
    
    	def post(self, request):
    		'''登录校验'''
    		# 1.接受数据
    		post = request.POST
    		username = post.get('username')
    		password = post.get('pwd')
    		s1 = sha1()
    		s1.update(password.encode('utf-8'))
    		password2 = s1.hexdigest()
    		# 2.校验数据
    		if not all([username, password]):
    			render(request, 'user/login.html', {'errmsg': '数据不完整'})
    		# 3.业务处理:登录校验
    		# 不知道为啥校验不了sh1加密的密码,还是直接查询
    		# user = authenticate(username=username, password=password2)
    		user = User.objects.get(username=username, password=password2)
    		if user is not None:
    			if user.is_active:
    				# 用户已激活,记录用户的登录状态
    				login(request, user)
    				# 跳转到首页
    				return redirect(reverse('goods:index'))
    			else:
    				return render(request, 'user/login.html', {'errmsg': '账户未激活'})
    		else:
    			# 用户名或密码错误
    			return render(request, 'user/login.html', {'errmsg': '用户名或密码错误'})
    	 

    登录成功可以跳转到i首页

    为了降低用户对数据库的读写压力,将session放在redis中,

    安装----安装这个,将django自动升级到3.0很多东西不兼容

    pip install django-redis 

    卸载django

    pip uninstall django

    安装指定版本

    pip install django==1.11.26 -i https://pypi.tuna.tsinghua.edu.cn/simple
    

     

    配置settings.py

    # Django的缓存配置项
    CACHES = {
    	"default": {
    		"BACKEND": "django_redis.cache.RedisCache",
    		"LOCATION": "redis://127.0.0.1:6379/9",
    		"OPTIONS": {
    			"CLIENT_CLASS": "django_redis.client.DefaultClient",
    		}
    	}
    }
    # 配置session的存储
    SESSION_ENGINE = "django.contrib.sessions.backends.cache"
    SESSION_CACHE_ALIAS = "default"
    

      

    启动项目,登录

    redis数据库有数据

    session已经存储到redis数据库中

    记住用户名

    		if user is not None:
    			if user.is_active:
    				# 用户已激活,记录用户的登录状态
    				login(request, user)
    				# 首页
    				response =  redirect(reverse('goods:index'))
    
    				# 判断是否需要记住用户名
    				remember = post.get('remember')
    				if remember == 'on':
    					# 记住用户名
    					response.set_cookie('username',username,max_age=7*24*3600)
    				else:
    					response.delete_cookie('username')
    				# 跳转到首页
    				return response
    			else:
    				return render(request, 'user/login.html', {'errmsg': '账户未激活'})
    		else:
    			# 用户名或密码错误
    			return render(request, 'user/login.html', {'errmsg': '用户名或密码错误'})
    

      

     

    修改登录时,判断cookie中是否有username

    class LoginView(View):
    	'''登录'''
    
    	def get(self, request):
    		'''显示登录页面'''
    		# 判断是否记住了用户名
    		if 'username' in request.COOKIES:
    			username = request.COOKIES.get('username')
    			# 勾选
    			checked = 'checked'
    		else:
    			username = ''
    			checked = ''
    		# 使用模板
    		return render(request, 'user/login.html',{'username':username,'checked':checked})
    

      

    修改前端中用户名的value,和check,接受提交的内容

    <input type="text" name="username" class="name_input" value="{{ username }}" placeholder="请输入用户名">
    

     

    <input type="checkbox" name="remember" {{ checkeds }}>
    

      

    清除cookie,重新登录,成功记住用户名

    登录和注册页面完成

  • 相关阅读:
    系统架构
    创造HTTPS的是个神
    为Chrome开发插件提高工作效率
    Javascript 控制style 小结
    svcutil 生成代理类时的问题
    xeam Build Definition Extension uninstall 卸载
    看看 Delphi XE2 为 VCL 提供的 14 种样式
    FireMonkey 绘图(1)
    终于, Delphi XE2 携带 GDI+ 库了
    关于禁止程序重复启动的另一种需要与实现
  • 原文地址:https://www.cnblogs.com/shuimohei/p/13113618.html
Copyright © 2020-2023  润新知