1.認識電商
-
B2B---企業對企業:阿里巴巴
- C2C---個人對個人:淘寶,瓜子二手車
- B2C---企業對個人:唯品會
- C2B---個人對企業:海爾商城(定制)----需要做的項目
- O2O---線上到線下:美團,餓了么
- F2C---工廠到個人:戴爾的一部分
- B2B2C---企業-企業-個人:京東商城,天貓商城
2.Web項目開發流程
- 項目立項:領導決定做這個項目
- 需求分析:需求人提出自己的需求--客戶,老板等,分析到底實現什么功能
- 分析功能能不能實現,
- 功能技術實現難度
- 原型設計:產品經理根據需求畫產品的原型圖(Axure)
- 后端進行架構設計---架構師
- 模塊划分
- 功能架構
- 開發環境的選擇
- 項目中用到的其他技術
- 項目部署架構
- 網址開發模式
- 前后端分離:Flask
- 前后端不分離:Django
- 數據庫設計
- 分析數據表和表字段
- 表之間的關系
- 模塊代碼實現和單元測試----我們需要做的
- 根據分工,實現模塊代碼
- 對你寫的代碼進行單元測試
- 代碼整合
- 前端UI設計,根據UI形成前端頁面,
- 整合前后端
- 集成測試:代碼合起來,檢測整個流程有沒有問題
- 網址發布:項目部署上線
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服務器,免費服務器有:163、126、QQ
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,重新登錄,成功記住用戶名
登錄和注冊頁面完成