天天生鮮項目(一)


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,重新登錄,成功記住用戶名

 

登錄和注冊頁面完成

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM