shuimohei

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_worker\dailyfresh>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,重新登录,成功记住用户名

 

登录和注册页面完成

 

分类:

技术点:

相关文章: