摘抄Django項目(一)

果生鮮項目介紹

  • 1.商業模式說明
  • 2.開發流程介紹
  • 3.項目需求分析
  • 4.項目架構分析
  • 5.數據庫設計
  • 6.模型類說明
  • 7.建立dailyfresh項目
  • 8.展現註冊頁面
  • 9.視圖函數的get和post請求處理
  • 10.類視圖

 

商業模式介紹

目的:知道每天生鮮項目屬於那種商業模式css

1.B2B--企業對企業

  • B2B (Business to Business)是指進行電子商務交易的供需雙方都是商家(或企業、公司),她(他)們使用了互聯網的技術或各類商務網絡平臺,完成商務交易的過程。電子商務是現代 B2B marketing 的一種具體主要的表現形式。

  • 案例:阿里巴巴、慧聰網

2.C2C--我的對我的

  • C2C 即 Customer to Customer,意思就是消費者我的間的電子商務行爲。好比一個消費者有一臺電腦,經過網絡進行交易,把它出售給另一個消費者,此種交易類型就稱爲 C2C 電子商務。```

  • 案例:淘寶、易趣、瓜子二手車

3.B2C--企業對我的

  • B2C 是 Business to Customer 的縮寫,而其中文簡稱爲「商對客」。「商對客」是電子商務 的一種模式,也就是一般說的直接面向消費者銷售產品和服務商業零售模式。這種形式的電子商務通常以網絡零售業爲主,主要藉助於互聯網開展在線銷售活動。B2C 即企業經過互 聯網爲消費者提供一個新型的購物環境——網上商店,消費者經過網絡在網上購物、網上支付等消費行爲。

  • 案例:惟品會、樂蜂網

4.C2B--我的對企業

  • C2B(Consumer to Business,即消費者到企業),是互聯網經濟時代新的商業模式。這一模式改變了原有生產者(企業和機構)和消費者的關係,是一種消費者貢獻價值(Create Value),企業和機構消費價值(Consume Value)。C2B 模式和咱們熟知的供需模式(DSM, Demand Supply Model)偏偏相反,真正的 C2B 應該先有消費者需求產生然後有企業生產,即先有消費者提出需求,後有生產企業按 需求組織生產。一般狀況爲消費者根據自身需求定製產品和價格,或主動參與產品設計、生產和訂價,產品、價格等彰顯消費者的個性化需求,生產企業進行定製化生產。

  • 案例:海爾商城、 尚品宅配

5.O2O--線上到線下

  • O2O 即 Online To Offline(在線離線/線上到線下),是指將線下的商務機會與互聯網結合,讓互聯網成爲線下交易的平臺,這個概念最先來源於美國。O2O 的概念很是普遍,既可涉及到線上,又可涉及到線下,能夠通稱爲 O2O。主流商業管理課程均對 O2O 這種新型的商業模式有所介紹及關注。```

  • 案例:美團、餓了嗎

6.F2C--工廠到我的

  • F2C 指的是 Factory to customer,即從廠商到消費者的電子商務模式

  • 案例:戴爾

7.B2B2C--企業--企業--我的

  • B2B2C 是一種電子商務類型的網絡購物商業模式,B 是 BUSINESS 的簡稱,C 是 CUSTOMER 的簡稱,第一個 B 指的是商品或服務的供應商,第二個 B 指的是從事電子商務的企業,C 則是表示消費者。第一個 BUSINESS,並不只僅侷限於品牌供應商、影視製做公司和圖書出版商,任何的商品供應商或服務供應商都能能夠成爲第一個 BUSINESS;第二 B 是 B2B2C 模式的電子商務企業,經過統一的經營管理對商品和服務、消費者終端同時進行整合,是廣大供應商和消費 者之間的橋樑,爲供應商和消費者提供優質的服務,是互聯網電子商務服務供應商。C 表示 消費者,在第二個 B 構建的統一電子商務平臺購物的消費者。B2B2C 的來源於目前的 B2B、B2C 模式的演變和完善,把 B2C 和 C2C 完美地結合起來,經過 B2B2C 模式的電子商務企業構建本身的物流供應鏈系統,提供統一的服務。html

  • 案例:京東商城、天貓商城前端


 

開發流程介紹

 

提示:

  • 1.架構設計
    • 分析可能用到的技術點
    • 先後端是否分離
    • 前端使用哪些框架
    • 後端使用哪些框架,Django、Flask、......
    • 選擇什麼數據庫
    • 如何實現緩存
    • 如何搭建分佈式服務
    • 如何管理源代碼
    • ......
  • 2.數據庫設計
    • 數據庫表的設計相當重要
    • 根據項目需求,設計合適的數據庫表
    • 數據庫表在前期若是設計不合理,後期隨需求增長將很難維護
    • ......
  • 3.集成測試
    • 在測試階段要留意測試系統發送的BUG郵件
  • 4.先後端是否分離
    • Django框架,通常是先後端不分離,後端須要處理前端一些業務邏輯
      • 後端使用模板動態渲染好html頁面,響應給客戶端
      • 後端維護html頁面時,壓力大,可是是必須承受的
      • 也能夠實現先後端分離
        • 不使用模板,直接響應JSON等一些數據給客戶端
    • Flask框架,通常是先後端分離

 


 

項目需求分析

靜態文件預覽

 

頁面需求分析

  • register.html
    • 註冊頁面,已加入了初步的表單驗證效果,此效果在課程中已講述如何製做
  • login.html
    • 登陸頁面
  • user_center_info.html
    • 用戶中心-用戶信息頁 用戶中心功能一,查看用戶的基本信息
  • user_center_order.html
    • 用戶中心-用戶訂單頁 用戶中心功能二,查看用戶的所有訂單
  • user_center_site.html
    • 用戶中心-用戶收貨地址頁 用戶中心功能三,查看和設置用戶的收貨地址
  • index.html
    • 網站首頁,頂部「註冊|登陸」和用戶信息是切換顯示的,商品分類菜單點擊直接連接滾動到本頁面商品模塊。首頁已加入幻燈片效果。此效果在課程中已講述如何製做
  • list.html
    • 商品列表頁,商品分類菜單鼠標懸停時切換顯示和隱藏,點擊菜單後連接到對應商品的列表頁
  • detail.html
    • 商品詳情頁,某一件商品的詳細信息
  • cart.html
    • 個人購物車頁,列出已放入購物車上的商品
  • place_order.html
    • 提交訂單頁

 


 

項目架構分析

項目架構

項目功能模塊

功能模塊說明

1.用戶模塊

  • register.html login.html user_center_info.html user_center_order.htmluser_center_site.html
  • 註冊頁
    • 顯示註冊頁面
    • 實現用戶的註冊邏輯
  • 登陸頁
    • 顯示登陸頁面
    • 實現用戶的登陸邏輯
  • 用戶中心
    • 用戶中心信息頁:
      • 顯示用戶的信息,包括用戶名、電話和地址,同時頁面下方顯示出用戶最近瀏覽的商品信息
    • 用戶中心地址頁:
      • 顯示用戶的當前收件地址,頁面下方的表單能夠新增用戶的收貨地址
    • 用戶中心訂單頁:
      • 顯示出當前用戶的訂單信息

2.商品模塊

  • index.html list.html detail.html
  • 首頁
    • 點擊頁面上相應的商品種類連接,跳轉到相應的商品列表頁
    • 每一個種類的商品顯示4個,按照默認排序方式進行顯示
    • 每一個種類商品後面顯示出3個最新的商品標題
    • 點擊某一個商品時跳轉到商品的詳情頁面
    • 若是用戶已經登陸,頁面頂部顯示登陸用戶的信息
  • 商品詳情頁
    • 顯示出某一個商品的詳細信息
    • 頁面的左下方顯示出該種類商品的2個最新商品信息
  • 商品列表頁
    • 顯示出某一個種類商品的列表數據
    • 頁面的左下方顯示出該種類商品的2個最新商品信息
  • 其餘
    • 經過頁面搜索框搜索商品信息

3.購物車模塊

  • cart.html
  • 列表頁和詳情頁將商品添加到購物車
  • 用戶登陸後,除註冊頁和登陸頁以外,其餘頁面上顯示登陸用戶購物車中商品的數目
  • 購物車頁面:對用戶購物車中商品的操做。如選擇某件商品,增長或減小購物車中商品的數目

4.訂單模塊

  • place_order.html
  • 提交訂單頁面:顯示用戶準備購買的商品信息
  • 用戶中心信息頁顯示用戶的訂單信息

項目總體架構

 


 

數據庫設計

提示

  • 項目開發須要數據來驅動,因此咱們須要先思考數據庫表該如何設計
  • 只有先設計好了數據庫表,後臺運營人員才能經過後臺管理平臺向數據庫中發佈數據
  • Django內嵌ORM框架,咱們是經過模型類以面向對象思想操做數據庫
  • 一個模型類映射數據庫中一張表

須要設計的數據庫表

  • 1.用戶模塊
    • 用戶表
    • 用戶地址表
  • 2.商品模塊
    • 商品類別表
    • 商品SPU表
    • 商品SKU表
    • 商品圖片表
    • 主頁輪播商品展現表
    • 主頁分類商品展現表
    • 主頁促銷活動展現表
  • 3.訂單模塊
    • 訂單信息表
    • 訂單商品表
  • 4.購物車模塊數據存儲在redis中

數據庫表詳情

基類:BaseModel

  • 模型類補充字段,做爲基類使用
字段名 字段類型 字段選項 字段說明
create_time DateTimeField auto_now_add=True 建立時間
update_time DateTimeField auto_now=True 更新時間

用戶表

  • 模型類名:User;表名:df_users
  • 使用Django自帶的用戶認證系統管理
  • 繼承自:AbstractUser,導包 from django.contrib.auth.models import AbstractUser
  • 遷移前,須要在settings.py文件中設置:AUTH_USER_MODEL = '應用.用戶模型類'python

 

用戶地址表

  • 模型類名:Address;表名:df_address
  • 使用User模型類做爲外鍵約束地址信息屬於哪一個用戶
字段名 字段類型 字段選項 字段說明
id IntegerField primary_key=True 主鍵字段
user ForeignKey 外鍵爲User模型 約束地址屬於哪一個用戶
receiver_name CharField max_length=20 收件人
receiver_mobile CharField max_length=11 聯繫電話
detail_addr CharField max_length=256 詳細地址
zip_code CharField max_length=6 郵政編碼

商品類別表

  • 模型類名:GoodsCategory;表名:df_goods_category
字段名 字段類型 字段選項 字段說明
id IntegerField primary_key=True 主鍵字段
name CharField max_length=20 商品類別名稱
logo CharField max_length=100 商品類別標識
image ImageField upload_to="category" 商品類別圖片

商品SPU表

  • 模型類名:Goods;表名:df_goods
字段名 字段類型 字段選項 字段說明
id IntegerField primary_key=True 主鍵字段
name CharField max_length=100 商品名稱
desc HTMLField blank=True 商品詳細介紹

商品SKU表

  • 模型類名:GoodsSKU;表名:df_goods_sku
字段名 字段類型 字段選項 字段說明
id IntegerField primary_key=True 主鍵字段
category ForeignKey 外鍵爲GoodsCategory模型 約束該商品的類別
goods ForeignKey 外鍵爲Goods模型 約束該商品的SPU
name CharField max_length=100 商品名稱
title CharField max_length=200 商品簡介
unit CharField max_length=10 銷售單位
price DecimalField max_digits=10, decimal_places=2 商品價格
stock IntegerField default=0 商品庫存
sales IntegerField default=0 商品銷量
default_image ImageField upload_to="goods" 商品默認圖片
status BooleanField default=True 是否上線

商品圖片表

  • 模型類名:GoodsImage;表名:df_goods_image
字段名 字段類型 字段選項 字段說明
id IntegerField primary_key=True 主鍵字段
sku ForeignKey 外鍵爲GoodsSKU模型 約束圖片屬於哪一個商品的
image ImageField upload_to="goods" 商品圖片

主頁輪播商品展現表

  • 模型類名:IndexGoodsBanner;表名:df_index_goods
字段名 字段類型 字段選項 字段說明
id IntegerField primary_key=True 主鍵字段
sku ForeignKey 外鍵爲GoodsSKU模型 約束該商品的sku
image ImageField upload_to="banner" 商品圖片
index SmallIntegerField default=0 輪播順序

主頁分類商品展現表

  • 模型類名:IndexCategoryGoodsBanner;表名:df_index_category_goods
字段名 字段類型 字段選項 字段說明
id IntegerField primary_key=True 主鍵字段
category ForeignKey 外鍵爲GoodsCategory模型 約束該商品類型
sku ForeignKey 外鍵爲GoodsSKU模型 約束該商品的sku
display_type SmallIntegerField choices=DISPLAY_TYPE_CHOICES 展現類型:圖片或標題
index SmallIntegerField default=0 展現順序

主頁促銷活動展現表

  • 模型類名:IndexCategoryGoodsBanner;表名:df_index_category_goods
字段名 字段類型 字段選項 字段說明
id IntegerField primary_key=True 主鍵字段
name CharField max_length=50 活動名稱
url URLField   活動連接
image ImageField upload_to="banner" 活動商品圖片
index SmallIntegerField default=0 活動商品順序

訂單信息表

  • 模型類名:OrderInfo;表名:df_order_info
字段名 字段類型 字段選項 字段說明
order_id IntegerField primary_key=True 主鍵字段
user ForeignKey 外鍵爲User模型 下單用戶
address ForeignKey 外鍵爲Address模型 下單地址
total_count IntegerField default=1 商品總數
total_amount DecimalField max_digits=10, decimal_places=2 商品總金額
trans_cost DecimalField max_digits=10, decimal_places=2 運費
pay_method SmallIntegerField choices=PAY_METHOD_CHOICES 支付方式,定義支付選項
status SmallIntegerField choices=ORDER_STATUS_CHOICES 訂單狀態,自定義狀態
trade_id CharField max_length=100, unique=True 訂單編號

訂單商品表

  • 模型類名:OrderGoods;表名:df_order_goods
字段名 字段類型 字段選項 字段說明
id IntegerField primary_key=True 主鍵字段
order ForeignKey 外鍵爲OrderInfo模型 約束是哪一個商品訂單
sku ForeignKey 外鍵爲GoodsSKU模型 約束訂單商品的sku
count IntegerField default=1 訂單商品數量
price DecimalField max_digits=10, decimal_places=2 商品單價
comment TextField default="" 評價信息

補充:SKU與SPU概念

  • SPU = Standard Product Unit (標準產品單位)
    • SPU 是商品信息聚合的最小單位,是一組可複用、易檢索的標準化信息的集合,該集合描述了一個產品的特性。
    • 通俗點講,屬性值、特性相同的商品就能夠稱爲一個SPU。
    • 例如:iphone7 就是一個SPU,與商家,與顏色、款式、套餐都無關。
  • SKU=stock keeping unit(庫存量單位)
    • SKU 即庫存進出計量的單位,能夠是以件、盒、托盤等爲單位。 SKU 是物理上不可分割的最小存貨單元。
    • 在使用時要根據不一樣業態,不一樣管理模式來處理。在服裝、鞋類商品中使用最多最廣泛。
    • 例如:紡織品中一個SKU,一般表示:規格、顏色、款式。

 


 

模型類說明

用戶模塊

  • 模型類名:User;表名:df_users
  • 使用Django自帶的用戶認證系統管理
  • 繼承自:AbstractUser,導包 from django.contrib.auth.models import AbstractUser
  • 遷移前,須要在settings.py文件中設置:AUTH_USER_MODEL = '應用.用戶模型類'
from django.db import models
from django.contrib.auth.models import AbstractUser
from utils.models import BaseModel
from django.conf import settings
from goods.models import GoodsSKU
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

class User(AbstractUser, BaseModel):
    """用戶"""
    class Meta:
        db_table = "df_users"

    def generate_active_token(self):
        """生成激活令牌"""
        serializer = Serializer(settings.SECRET_KEY, 3600)
        token = serializer.dumps({"confirm": self.id})  # 返回bytes類型
        return token.decode()


class Address(BaseModel):
    """地址"""
    user = models.ForeignKey(User, verbose_name="所屬用戶")
    receiver_name = models.CharField(max_length=20, verbose_name="收件人")
    receiver_mobile = models.CharField(max_length=11, verbose_name="聯繫電話")
    detail_addr = models.CharField(max_length=256, verbose_name="詳細地址")
    zip_code = models.CharField(max_length=6, verbose_name="郵政編碼")

    class Meta:
        db_table = "df_address"

  


 

商品模塊

from django.db import models
from utils.models import BaseModel
from tinymce.models import HTMLField

class GoodsCategory(BaseModel):
    """商品類別表"""
    name = models.CharField(max_length=20, verbose_name="名稱")
    logo = models.CharField(max_length=100, verbose_name="標識")
    image = models.ImageField(upload_to="category", verbose_name="圖片")

    class Meta:
        db_table = "df_goods_category"
        verbose_name = "商品類別"  # admin站點使用
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Goods(BaseModel):
    """商品SPU表"""
    name = models.CharField(max_length=100, verbose_name="名稱")
    desc = HTMLField(verbose_name="詳細介紹", default="", blank=True)

    class Meta:
        db_table = "df_goods"
        verbose_name = "商品"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class GoodsSKU(BaseModel):
    """商品SKU表"""
    category = models.ForeignKey(GoodsCategory, verbose_name="類別")
    goods = models.ForeignKey(Goods, verbose_name="商品")
    name = models.CharField(max_length=100, verbose_name="名稱")
    title = models.CharField(max_length=200, verbose_name="簡介")
    unit = models.CharField(max_length=10, verbose_name="銷售單位")
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="價格")
    stock = models.IntegerField(default=0, verbose_name="庫存")
    sales = models.IntegerField(default=0, verbose_name="銷量")
    default_image = models.ImageField(upload_to="goods", verbose_name="圖片")
    status = models.BooleanField(default=True, verbose_name="是否上線")

    class Meta:
        db_table = "df_goods_sku"
        verbose_name = "商品SKU"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class GoodsImage(BaseModel):
    """商品圖片"""
    sku = models.ForeignKey(GoodsSKU, verbose_name="商品SKU")
    image = models.ImageField(upload_to="goods", verbose_name="圖片")

    class Meta:
        db_table = "df_goods_image"
        verbose_name = "商品圖片"
        verbose_name_plural = verbose_name

    def __str__(self):
        return str(self.sku)


class IndexGoodsBanner(BaseModel):
    """主頁輪播商品展現"""
    sku = models.ForeignKey(GoodsSKU, verbose_name="商品SKU")
    image = models.ImageField(upload_to="banner", verbose_name="圖片")
    index = models.SmallIntegerField(default=0, verbose_name="順序")

    class Meta:
        db_table = "df_index_goods"
        verbose_name = "主頁輪播商品"
        verbose_name_plural = verbose_name

    def __str__(self):
        return str(self.sku)


class IndexCategoryGoodsBanner(BaseModel):
    """主頁分類商品展現"""
    DISPLAY_TYPE_CHOICES = (
        (0, "標題"),
        (1, "圖片")
    )
    category = models.ForeignKey(GoodsCategory, verbose_name="商品類別")
    sku = models.ForeignKey(GoodsSKU, verbose_name="商品SKU")
    display_type = models.SmallIntegerField(choices=DISPLAY_TYPE_CHOICES, verbose_name="展現類型")
    index = models.SmallIntegerField(default=0, verbose_name="順序")

    class Meta:
        db_table = "df_index_category_goods"
        verbose_name = "主頁分類展現商品"
        verbose_name_plural = verbose_name

    def __str__(self):
        return str(self.sku)


class IndexPromotionBanner(BaseModel):
    """主頁促銷活動展現"""
    name = models.CharField(max_length=50, verbose_name="活動名稱")
    url = models.URLField(verbose_name="活動鏈接")
    image = models.ImageField(upload_to="banner", verbose_name="圖片")
    index = models.SmallIntegerField(default=0, verbose_name="順序")

    class Meta:
        db_table = "df_index_promotion"
        verbose_name = "主頁促銷活動"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

 


 

訂單模塊

from django.db import models
from utils.models import BaseModel
from users.models import User, Address
from goods.models import GoodsSKU

class OrderInfo(BaseModel):
    """訂單信息"""
    PAY_METHODS = {
        1: "貨到付款",
        2: "支付寶",
    }

    PAY_METHODS_ENUM = {
        "CASH": 1,
        "ALIPAY": 2
    }

    PAY_METHOD_CHOICES = (
        (1, "貨到付款"),
        (2, "支付寶"),
    )

    ORDER_STATUS = {
        1: "待支付",
        2: "待發貨",
        3: "待收貨",
        4: "待評價",
        5: "已完成",
    }

    ORDER_STATUS_ENUM = {
        "UNPAID": 1,
        "UNSEND": 2,
        "UNRECEIVED": 3,
        "UNCOMMENT": 4,
        "FINISHED": 5
    }

    ORDER_STATUS_CHOICES = (
        (1, "待支付"),
        (2, "待發貨"),
        (3, "待收貨"),
        (4, "待評價"),
        (5, "已完成"),
    )

    order_id = models.CharField(max_length=64, primary_key=True, verbose_name="訂單號")
    user = models.ForeignKey(User, verbose_name="下單用戶")
    address = models.ForeignKey(Address, verbose_name="收穫地址")
    total_count = models.IntegerField(default=1, verbose_name="商品總數")
    total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="商品總金額")
    trans_cost = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="運費")
    pay_method = models.SmallIntegerField(choices=PAY_METHOD_CHOICES, default=1, verbose_name="支付方式")
    status = models.SmallIntegerField(choices=ORDER_STATUS_CHOICES, default=1, verbose_name="訂單狀態")
    trade_id = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name="支付編號")

    class Meta:
        db_table = "df_order_info"


class OrderGoods(BaseModel):
    """訂單商品"""
    order = models.ForeignKey(OrderInfo, verbose_name="訂單")
    sku = models.ForeignKey(GoodsSKU, verbose_name="訂單商品")
    count = models.IntegerField(default=1, verbose_name="數量")
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="單價")
    comment = models.TextField(default="", verbose_name="評價信息")

    class Meta:
        db_table = "df_order_goods"

 

 


 

建立yiguofresh項目

基礎項目目錄

基礎項目目錄說明

  • apps:應用目錄,包含用戶、商品、訂單、購物車四個應用
  • dailyfresh:項目同名目錄
  • static:靜態文件目錄,包含images、css、js、html ...
  • templates:模板文件目錄
  • utils:實用用工具類,包含模型基類 ...

建立應用

  • 注意:該項目的應用在apps文件目錄下,不是在項目根目錄下mysql

 

  # 以用戶模塊爲例
  cd Desktop/dailyfresh/apps/    
  python ../manage.py startapp users

MySQL數據庫

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'dailyfresh',
        'HOST': '192.168.24.136', # MySQL數據庫地址
        'PORT': '3306',
        'USER': 'root',
        'PASSWORD': 'mysql',
    }
}

 

 

定義模型

  • 分別在usersgoodsorders應用中定義好對應的模型類
    • cart應用中暫時不定義模型類,其中的數據是使用redis數據庫維護的

User模型提示

  • users應用中的模型類User是使用Django自帶的用戶認證系統維護的nginx

遷移前,須要在settings.py文件中設置:AUTH_USER_MODEL = '應用.用戶模型類'git

 

增長導包路徑

  • 緣由:在settings.py中設置AUTH_USER_MODEL時,編碼規則爲'應用.用戶模型類'
  • 可是,應用在apps/文件目錄下,爲了保證正確的編碼,咱們須要增長導包路徑
  • 同時,爲了配合AUTH_USER_MODEL的配置,應用的安裝直接使用users,不要使用apps.usersweb

  import sys
  sys.path.insert(1, os.path.join(BASE_DIR, 'apps'))

完成模型遷移

展現註冊頁面

準備靜態文件

 

  • 配置靜態文件加載路徑

 

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
  • 測試靜態頁面展現效果

 

 

展現註冊頁面

  • 提示:在用戶模塊users

1.準備註冊頁面視圖

 

def register(request):
    """返回註冊頁面"""
    return render(request, 'register.html')

2.準備模板

 

3.匹配URL

  • 項目中的urls.pyredis

 

  urlpatterns = [
      url(r'^admin/', include(admin.site.urls)),
      # 訪問用戶模塊的路由配置
      url(r'^users/', include('apps.users.urls',namespace='users')),
  ]

 

 users應用中的urls.pysql

  urlpatterns = [from django.conf.urls import url
  from apps.users import views

  urlpatterns = [
      # 註冊
      url(r'^register$', views.register, name='register'),
  ]

 

 

測試註冊頁面展現效果

提示

  • URL正則匹配中,增長了命名空間,方便後續的反解析
  • URL正則匹配中,register後面是否加/,根據公司需求而定

 

 

視圖函數的get和post請求處理

思考:一個register視圖,是否能夠處理兩種邏輯?好比get和post請求邏輯。

如何在一個視圖中處理get和post請求

註冊視圖處理get和post請求

 

 def register(request):
    """處理註冊"""

    # 獲取請求方法,判斷是GET/POST請求
    if request.method == 'GET':
        # 處理GET請求,返回註冊頁面
        return render(request, 'register.html')
    else:
        # 處理POST請求,實現註冊邏輯
        return HttpResponse('這裏實現註冊邏輯')

 

 


 

類視圖

類視圖介紹

  • 將視圖以類的形式定義
  • 須要繼承自:通用類視圖基類 View
  • 須要導包:from django.views.generic import View 或 from django.views.generic.base import View
  • 應用/urls.py中配置路由時,使用類視圖的as_view()方法 ,將類視圖轉成視圖函數
  • dispatch()方法將具體的request分發至對應請求方式的處理方法中,好比get、post ...
  • 類視圖和視圖函數根據具體的需求而選擇使用,可是目前更加傾向於類視圖,若是類視圖很差實現的就能夠選擇視圖函數,以保證明現邏輯爲主
  • 類視圖文檔

類視圖使用

1.定義類視圖處理註冊邏輯

class RegisterView(View):
    """類視圖:處理註冊"""

    def get(self, request):
        """處理GET請求,返回註冊頁面"""
        return render(request, 'register.html')

    def post(self, request):
        """處理POST請求,實現註冊邏輯"""
        return HttpResponse('這裏實現註冊邏輯')

 

 

2.匹配URL

  • users應用中的urls.py
urlpatterns = [
    # 視圖函數:註冊
    # url(r'^register$', views.register, name='register'),
    # 類視圖:註冊
    url(r'^register$', views.RegisterView.as_view(), name='register'),
]

 

 

提示

  • 類視圖相對於函數視圖有更高的複用性
  • 若是其餘地方須要用到某個類視圖的邏輯,直接繼承該類視圖便可

 

 

模板加載靜態文件

若是發現模板中,靜態文件是之前端的方式處理的,Django程序猿在使用時,須要修改爲Django的處理方式

前端的方式處理靜態文件

Django的方式處理靜態文件

 

提示

  • Django擅長處理動態的業務邏輯,靜態的業務邏輯交給nginx來處理
  • 關於static標籤瞭解用法便可
  • 項目後期咱們會使用nginx來處理靜態文件

 

 

註冊登錄

  • 1.須要實現用戶註冊邏輯
  • 2.須要實現用戶激活邏輯
  • 3.須要實現用戶登錄邏輯

 

 

註冊邏輯介紹

  • 提示:用戶註冊邏輯中,包含了用戶郵件激活邏輯


 

註冊邏輯實現

準備處理註冊邏輯類視圖

class RegisterView(View):
    """類視圖:處理註冊"""

    def get(self, request):
        """處理GET請求,返回註冊頁面"""
        return render(request, 'register.html')

    def post(self, request):
        """處理POST請求,實現註冊邏輯"""
        return HttpResponse('這裏實現註冊邏輯')

獲取註冊請求參數

class RegisterView(View):
    """類視圖:處理註冊"""

    def get(self, request):
        """處理GET請求,返回註冊頁面"""
        return render(request, 'register.html')

    def post(self, request):
        """處理POST請求,實現註冊邏輯"""

        # 獲取註冊請求參數
        user_name = request.POST.get('user_name')
        password = request.POST.get('pwd')
        email = request.POST.get('email')
        allow = request.POST.get('allow')

        return HttpResponse('這裏實現註冊邏輯')

校驗註冊請求參數

  • 先後端的校驗須要分離:前端檢驗完,數據到服務器後繼續校驗,避免黑客繞過客戶端發請求
  • 提示:出現異常的處理方式,根據公司具體需求來實現
class RegisterView(View):
    """類視圖:處理註冊"""

    def get(self, request):
        """處理GET請求,返回註冊頁面"""
        return render(request, 'register.html')

    def post(self, request):
        """處理POST請求,實現註冊邏輯"""

        # 獲取註冊請求參數
        user_name = request.POST.get('user_name')
        password = request.POST.get('pwd')
        email = request.POST.get('email')
        allow = request.POST.get('allow')

        # 參數校驗:缺乏任意一個參數,就不要在繼續執行
        if not all([user_name, password, email]):
            return redirect(reverse('users:register'))
        # 判斷郵箱
        if not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$", email):
            return render(request, 'register.html', {'errmsg':'郵箱格式不正確'})
        # 判斷是否勾選協
        if allow != 'on':
            return render(request, 'register.html', {'errmsg': '沒有勾選用戶協議'})

        return HttpResponse('這裏實現註冊邏輯')

保存用戶註冊信息

  • 提示:隱私信息須要加密,能夠直接使用django提供的用戶認證系統完成
  • 用戶認證系統文檔
  • 調用create_user(user_name, email, password)實現用戶保存和加密隱私信息
    • 參數順序不能錯
  • IntegrityError異經常使用於判斷用戶是否重名、已註冊,這樣能夠減小訪問數據庫頻率
  • 保存完用戶註冊信息後,須要重置用戶激活狀態,由於Django用戶認證系統默認激活狀態爲True
class RegisterView(View):
    """類視圖:處理註冊"""

    def get(self, request):
        """處理GET請求,返回註冊頁面"""
        return render(request, 'register.html')

    def post(self, request):
        """處理POST請求,實現註冊邏輯"""

        # 獲取註冊請求參數
        user_name = request.POST.get('user_name')
        password = request.POST.get('pwd')
        email = request.POST.get('email')
        allow = request.POST.get('allow')

        # 參數校驗:缺乏任意一個參數,就不要在繼續執行
        if not all([user_name, password, email]):
            return redirect(reverse('users:register'))
        # 判斷郵箱
        if not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$", email):
            return render(request, 'register.html', {'errmsg':'郵箱格式不正確'})
        # 判斷是否勾選協
        if allow != 'on':
            return render(request, 'register.html', {'errmsg': '沒有勾選用戶協議'})

        # 保存數據到數據庫
        try:
            # 隱私信息須要加密,能夠直接使用django提供的用戶認證系統完成
            user = User.objects.create_user(user_name, email, password)
        except db.IntegrityError:
            return render(request, 'register.html', {'errmsg': '用戶已註冊'})

        # 手動的將用戶認證系統默認的激活狀態is_active設置成False,默認是True
        user.is_active = False
        # 保存數據到數據庫
        user.save()

        return HttpResponse('這裏實現註冊邏輯')

查看保存用戶註冊信息結果

# 查詢出數據,並以列表形式展現
select * from df_users \G

郵件激活用戶

思考:

  • 服務器激活郵件如何發送?
  • 服務器如何才能知道是誰要激活?

提示

  • 1.服務器激活郵件如何發送?

    • 激活郵件的發送不能阻塞註冊結果的響應
    • 激活郵件須要異步發送,集成Celery模塊能夠實現異步任務

 

2.服務器如何才能知道是誰要激活?

  • 激活郵件連接:http://127.0.0.1:8000/users/active/user_id
  • 注意:user_id須要以密文的形式發送到用戶郵箱,避免被黑客獲取破解
  • user_id加密後的結果稱之爲token、口令、令牌
  • token用於客戶端向服務器發送激活請求時,服務器對用戶身份的識別

 

準備工做

class ActiveView(View):
    """郵件激活"""

    def get(self, request, token):
        """處理激活請求"""
        pass

 

 

# 郵件激活
url(r'^active/(?P<token>.+)$', views.ActiveView.as_view(), name='active'),

 

 

實現步驟

  • 第一步:生成激活token
  • 第二步:Celery異步發送激活郵件

 

 

itsdangerous中文文檔

  • 1.安裝 itsdangerous 模塊

pip install itsdangerous

 

 

2.生成用戶激活token的方法封裝在User模型類中

  • Serializer()生成序列化器,傳入混淆字符串和過時時間
  • dumps()生成user_id加密後的token,傳入封裝user_id的字典
  • 返回token字符串
  • loads()解出token字符串,獲得用戶id明文

 

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings

class User(AbstractUser, BaseModel):
  """用戶"""
  class Meta:
      db_table = "df_users"

  def generate_active_token(self):
      """生成激活令牌"""
      serializer = Serializer(settings.SECRET_KEY, 3600)
      token = serializer.dumps({"confirm": self.id})  # 返回bytes類型
      return token.decode()

 

 3.生成激活token方法的調用

token = user.generate_active_token()

提示: SignatureExpired 異常

簽名過時的異常


 

Django發送郵件

  • Django中內置了郵件發送功能
  • 被定義在django.core.mail模塊中,調用方法 send_mail()
  • 發送郵件須要使用SMTP服務器,經常使用的免費服務器有:16三、12六、QQ
  • 提示:Django須要知道是誰在幫它發郵件,因此須要提早配置郵箱服務器

 

Django發送郵件步驟

  • 1.肯定郵件服務器和發件人sender
  • 2.settings.py中配置郵件服務器參數
  • 3.調用send_email()

1. 配置郵件服務器和發件人sender

  • 示例:此處演示網易郵箱服務器做爲發件方的郵件服務器
  • 發件人sender:dailyfreshzxc@yeah.net
  • 發件人郵箱受權

 

 

2. settings.py中配置郵件服務器參數

 

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 導入郵件模塊
EMAIL_HOST = 'smtp.yeah.net' # 發郵件主機
EMAIL_PORT = 25 # 發郵件端口
EMAIL_HOST_USER = 'dailyfreshzxc@yeah.net' # 受權的郵箱
EMAIL_HOST_PASSWORD = 'dailyfresh123' # 郵箱受權時得到的密碼,非註冊登陸密碼
EMAIL_FROM = '每天生鮮<dailyfreshzxc@yeah.net>' # 發件人擡頭

 

 

3. 調用send_email()

from django.core.mail import send_mail
from django.conf import settings

def send_active_email(to_email, user_name, token):
    """封裝發送郵件方法"""
    subject = "每天生鮮用戶激活"  # 標題
    body = ""  # 文本郵件體
    sender = settings.EMAIL_FROM  # 發件人
    receiver = [to_email]  # 接收人
    html_body = '<h1>尊敬的用戶 %s, 感謝您註冊每天生鮮!</h1>' \
                '<br/><p>請點擊此連接激活您的賬號<a href="http://127.0.0.1:8000/users/active/%s">' \
                'http://127.0.0.1:8000/users/active/%s</a></p>' % (user_name, token, token)
    send_mail(subject, body, sender, receiver, html_message=html_body)

4.使用


 

Celery異步發送激活郵件

Celery介紹

  • 1.Celery介紹
    • 點擊查看Celery參考文檔
    • Celery是一個功能完備即插即用的任務隊列
    • Celery適用異步處理問題,好比發送郵件、文件上傳,圖像處理等等比較耗時的操做,咱們可將其異步執行,這樣用戶不須要等待好久,提升用戶體驗
  • 2.Celery特色:
    • 簡單,易於使用和維護,有豐富的文檔
    • 高效,單個Celery進程每分鐘能夠處理數百萬個任務
    • 靈活,Celery中幾乎每一個部分均可以自定義擴展
    • Celery很是易於集成到一些web開發框架中
  • 3.安裝Celery

    # 進入虛擬環境
    pip install celery
  • 4.Celery組成結構

    • 任務隊列是一種跨線程、跨機器工做的一種機制
    • 任務隊列中包含任務的工做單元。有專門的工做進程持續不斷的監視任務隊列,並從中得到新的任務並處理
    • Celery經過消息進行通訊,一般使用一個叫broker(中間人)來協client(任務的發出者)和worker(任務的處理者)
    • client發出消息到隊列中,broker將隊列中的信息派發給worker來處理
    • 一個Celery系統能夠包含不少的worker和broker,可加強橫向擴展性和高可用性能。
    • Celery組成結構是生產者消費者模型的一種體現

 

 

 

Celery使用

1.建立Celery異步任務文件

2.建立應用對象/客戶端/client

  • 應用對象內部封裝要異步執行的任務
  • Celery():
    • 參數1是異步任務路徑
    • 參數2是指定的broker
      • redis://密碼@redis的ip:端口/數據庫
      • redis://192.168.243.191:6379/4
    • 返回客戶端應用對象app
  • send_active_email():內部封裝激活郵件內容,並用裝飾器@app.task註冊
  • 調用python的send_mail()將激活郵件發送出去

 

  from celery import Celery
  from django.core.mail import send_mail
  from django.conf import settings

  # 建立celery應用對象
  app = Celery('celery_tasks.tasks', broker='redis://192.168.243.191:6379/4')

  @app.task
  def send_active_email(to_email, user_name, token):
      """發送激活郵件"""

      subject = "每天生鮮用戶激活"  # 標題
      body = ""  # 文本郵件體
      sender = settings.EMAIL_FROM  # 發件人
      receiver = [to_email]  # 接收人
      html_body = '<h1>尊敬的用戶 %s, 感謝您註冊每天生鮮!</h1>' \
                  '<br/><p>請點擊此連接激活您的賬號<a href="http://127.0.0.1:8000/users/active/%s">' \
                  'http://127.0.0.1:8000/users/active/%s</a></p>' %(user_name, token, token)
      send_mail(subject, body, sender, receiver, html_message=html_body)

 

 

3.中間人broker

  • 示例:此處演示Redis數據庫做爲中間人broker
  • Celery須要一種解決消息的發送和接受的方式,咱們把這種用來存儲消息的的中間裝置叫作message broker, 也可叫作消息中間人。
  • 做爲中間人,咱們有幾種方案可選擇:

4.建立worker

  • 示例:此處演示把worker建立到ubuntu虛擬機中,ubuntu做爲Celery服務器
  • Celery服務器建立worker步驟

    • 1.把項目代碼拷貝一份到ubuntu虛擬機中

      • 並在celery_tasks/tasks.py文件頂部添加如下代碼
      • 做用:讓Celery的worker可以加載Django配置環境

import os 
os.environ["DJANGO_SETTINGS_MODULE"] = "dailyfresh.settings"
# 放到Celery服務器上時添加的代碼
import django
django.setup()

 

 

2.終端建立worker

celery -A celery_tasks.tasks worker -l info

3.開啓redis-server,查看broker

 

4.測試發郵件

5.查看worker收到的異步任務消息

完整註冊邏輯實現代碼

 

class RegisterView(View):
    """類視圖:處理註冊"""

    def get(self, request):
        """處理GET請求,返回註冊頁面"""
        return render(request, 'register.html')

    def post(self, request):
        """處理POST請求,實現註冊邏輯"""

        # 獲取註冊請求參數
        user_name = request.POST.get('user_name')
        password = request.POST.get('pwd')
        email = request.POST.get('email')
        allow = request.POST.get('allow')

        # 參數校驗:缺乏任意一個參數,就不要在繼續執行
        if not all([user_name, password, email]):
            return redirect(reverse('users:register'))
        # 判斷郵箱
        if not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$", email):
            return render(request, 'register.html', {'errmsg':'郵箱格式不正確'})
        # 判斷是否勾選協
        if allow != 'on':
            return render(request, 'register.html', {'errmsg': '沒有勾選用戶協議'})

        # 保存數據到數據庫
        try:
            # 隱私信息須要加密,能夠直接使用django提供的用戶認證系統完成
            user = User.objects.create_user(user_name, email, password)
        except db.IntegrityError:
            return render(request, 'register.html', {'errmsg': '用戶已註冊'})

        # 手動的將用戶認證系統默認的激活狀態is_active設置成False,默認是True
        user.is_active = False
        # 保存數據到數據庫
        user.save()

        # 生成激活token
        token = user.generate_active_token()

        # celery發送激活郵件:異步完成,發送郵件不會阻塞結果的返回
        send_active_email.delay(email, user_name, token)

        # 返回結果:好比重定向到首頁
        return redirect(reverse('goods:index'))

註冊一個用戶測試激活郵件是否正確發送


 

 

激活邏輯實現

  • 當點擊激活郵件中的激活連接時,訪問如下視圖
  • 實現說明:
    • Serializer():建立序列化器
    • loads(token):獲取token明文信息、字典
    • SignatureExpired異常:判斷token是否過時
    • DoesNotExist異常:判斷激活用戶是否存在,防止激活黑客帳戶
    • 查詢出要激活的用戶後,設置is_active=True,完成用戶激活

 

class ActiveView(View):
    """用戶激活"""

    def get(self, request, token):
        # 建立序列化器
        serializer = Serializer(settings.SECRET_KEY, 3600)

        try:
            # 使用序列化器,獲取token明文信息,須要判斷簽名是否過時
            result = serializer.loads(token)
        except SignatureExpired:
            # 提示激活連接已過時
            return HttpResponse('激活連接已過時')

        # 獲取用戶id
        user_id = result.get('confirm')

        try:
            # 查詢須要激活的用戶,須要判斷查詢的用戶是否存在
            user = User.objects.get(id=user_id)
        except User.DoesNotExist:
            # 提示用戶不存在
            return HttpResponse('用戶不存在')

        # 設置激活用戶的is_active爲Ture
        user.is_active = True
        # 保存數據到數據庫
        user.save()

        # 響應信息給客戶端
        return redirect(reverse('users:login'))
  • 郵件激活前

 

  • 郵件激活後

 

提示:DoesNotExist 異常

 


登錄邏輯介紹

 

登錄邏輯實現

如何登入一個用戶

  • 訪問登錄頁面和登錄請求都是如下類視圖完成:
  • Django自帶的用戶認證系統完成登錄驗證
    • authenticate():
      • 參數1:用戶名
      • 參數2:密碼
      • 返回用戶對象,若是用戶對象不存在,表示登錄驗證失敗
  • 登錄驗證成功後,須要繼續判斷是不是激活用戶
  • 若是用戶驗證成功+是激活用戶:下一步就是登入用戶
    • Django用戶認證系統提供登入方法:login(request, user)
    • 登入時,服務器須要記錄登錄狀態,即須要記錄session數據
      • 默認是存儲在MySQL的django_session數據庫表中
  • 若是用戶session數據須要存儲到Redis數據庫中
    • 須要安裝django-redis-sessions來輔助完成
    • 或者安裝django-redis來輔助完成(功能更加豐富,推薦使用)

 

class LoginView(View):
    """登錄"""

    def get(self, request):
        """響應登錄頁面"""
        return render(request, 'login.html')

    def post(self, request):
        """處理登錄邏輯"""

        # 獲取用戶名和密碼
        user_name = request.POST.get('username')
        password = request.POST.get('pwd')

        # 參數校驗
        if not all([user_name, password]):
            return redirect(reverse('users:login'))

        # django用戶認證系統判斷是否登錄成功
        user = authenticate(username=user_name, password=password)

        # 驗證登錄失敗
        if user is None:
            # 響應登陸頁面,提示用戶名或密碼錯誤
            return render(request, 'login.html', {'errmsg':'用戶名或密碼錯誤'})

        # 驗證登錄成功,並判斷是不是激活用戶
        if user.is_active is False:
            # 若是不是激活用戶
            return render(request, 'login.html', {'errmsg':'用戶未激活'})

        # 使用django的用戶認證系統,在session中保存用戶的登錄狀態
        login(request, user)

        # 登錄成功,重定向到主頁
        return redirect(reverse('goods:index'))

 

 


 

 

狀態保持

  • 用戶登錄成功後,須要將用戶的登錄狀態記錄下來,即保存由服務器生成的session數據
  • 瀏覽器和服務器中都要保存用戶登錄狀態
    • 服務器存儲session數據
    • 瀏覽器存儲sessionid

如下配置是配合Django用戶認證系統login()方法,完成session信息存儲

Redis數據庫緩存session信息

django-redis 中文文檔

  • 1.安裝django-redis

  • pip install django-redis

     2.settings.py文件配置django-redis

  • # 緩存
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://192.168.243.193:6379/5",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            }
        }
    }
    
    
    # Session
    # http://django-redis-chs.readthedocs.io/zh_CN/latest/#session-backend
    
    SESSION_ENGINE = "django.contrib.sessions.backends.cache"
    SESSION_CACHE_ALIAS = "default"

     

    查看服務器存儲session效果

    session中文文檔

    • session信息存儲在服務器

 

  • session信息被寫入到客戶端Cookie

 

登錄記住用戶

需求分析

爲何要記住用戶?

  • 登錄-->沒有記住用戶名-->退出瀏覽器-->再打開網頁-->狀態沒有保持-->須要再登錄

  • 登錄-->記住用戶名-->退出瀏覽器-->再打開網頁-->狀態依然保持-->不用再登錄直接訪問網站

記住用戶技術點分析

  • 已知:
    • 服務器的redis數據庫中,存儲了用戶session信息
    • 瀏覽器的cookie中,存儲了用戶sessionid信息
    • 每次請求瀏覽器會帶上cookie信息
  • 結論:記住用戶就是設置session有效期

 request.session.set_expiry(value)
  • 若是value是一個整數,那麼會話將在value秒沒有活動後過時
  • 若是value爲0,那麼會話的Cookie將在用戶的瀏覽會話結束時過時
  • 若是value爲None,那麼會話則兩個星期後過時

記住用戶實現

 

# 獲取是否勾選'記住用戶名'
remembered = request.POST.get('remembered')

# 登入用戶
login(request, user)

# 判斷是不是否勾選'記住用戶名'
if remembered != 'on':
    # 沒有勾選,不須要記住cookie信息,瀏覽會話結束後過時
    request.session.set_expiry(0)
else:
    # 已勾選,須要記住cookie信息,兩週後過時
    request.session.set_expiry(None)

# 響應結果: 重定向到主頁
return redirect(reverse('goods:index'))

 

 

退出登陸

如何登出一個用戶文檔

需求分析

退出登陸邏輯分析

  • 退出登陸對應的是get請求方法,不須要向服務器傳參數
  • 須要清理服務器的session數據
  • 須要清理瀏覽器的cookie數據
  • 以上兩步操做,都由Django用戶認證系統來完成

退出登陸實現

  • Django用戶認證系統提供logout()函數
  • 參數:request
  • 說明:若是用戶已登陸,Django用戶認證系統會把user對象加入到request當中
  • 結論:從request中能夠獲取到user信息,request.user

 

  class LogoutView(View):
      """退出登陸"""

      def get(self, request):
          """處理退出登陸邏輯"""

          # 由Django用戶認證系統完成:須要清理cookie和session,request參數中有user對象
          logout(request)
          # 退出後跳轉:由產品經理設計
          return redirect(reverse('goods:index'))

 

 配置URL正則

url(r'^logout$', views.LogoutView.as_view(), name='logout'),
相關文章
相關標籤/搜索