Django實(shí)踐1——電子商城項(xiàng)目

簡(jiǎn)書(shū)文章目錄生成腳本: 左側(cè)目錄
不要錯(cuò)過(guò),在簡(jiǎn)書(shū),你一定需要的
喜歡本文,請(qǐng)點(diǎn)個(gè)??,Thanks!

參考資料: 《精通Django 3 Web開(kāi)發(fā)》源碼
編程環(huán)境: Anaconda 2.4 + Python 3.8 + PyCharm 2021.2 + Django 3.2.7

一、設(shè)計(jì)與配置(原文網(wǎng)址

1.1 設(shè)計(jì)

  • 網(wǎng)站首頁(yè)——5個(gè)功能區(qū)
      • 商品搜索功能、網(wǎng)站導(dǎo)航、廣告輪播、商品分類熱銷、網(wǎng)站尾部
  • 商品列表頁(yè)——4個(gè)功能區(qū)
      • 商品搜索功能、網(wǎng)站導(dǎo)航、商品分類、商品列表信息
  • 商品詳細(xì)頁(yè)——5個(gè)功能區(qū)
      • 商品搜索功能、網(wǎng)站導(dǎo)航、商品基本信息、商品詳細(xì)介紹、熱銷推薦
  • 購(gòu)物車頁(yè)面——3個(gè)功能區(qū)
      • 商品搜索功能、網(wǎng)站導(dǎo)航、商品的購(gòu)買費(fèi)用核算
  • 個(gè)人中心頁(yè)面——4個(gè)功能區(qū)
      • 商品搜索功能、網(wǎng)站導(dǎo)航、用戶基本信息、訂單信息
  • 用戶登錄注冊(cè)頁(yè)面——3個(gè)功能區(qū)
      • 商品搜索功能、網(wǎng)站導(dǎo)航、登錄注冊(cè)表單

1.2 數(shù)據(jù)結(jié)構(gòu)圖

數(shù)據(jù)結(jié)構(gòu)圖

1.3 搭建項(xiàng)目功能配置

  • 創(chuàng)建項(xiàng)目myproject
cd 項(xiàng)目存儲(chǔ)目錄
mkdir myproject & cd myproject 
django-admin startproject config .
  • 創(chuàng)建項(xiàng)目應(yīng)用(Apps): index、commodity、shopper
python manage.py startapp index
python manage.py startapp commodity
python manage.py startapp shopper
  • 創(chuàng)建文件夾media、pstatictemplates
mkdir media
mkdir pstatic
mkdir templates
  • 文件夾及文件說(shuō)明

congfig文件夾: 含有文件__init__.py、asgi.pysettings.py、urls.pywsgi.py

commodity文件夾: Django創(chuàng)建的項(xiàng)目應(yīng)用(App),文件夾含有__init__.py、admin.py、apps.pymodels.py、tests.pyviews.py文件,它主要實(shí)現(xiàn)網(wǎng)站的商品列表頁(yè)商品詳細(xì)頁(yè)

index文件夾: Django創(chuàng)建的項(xiàng)目應(yīng)用(App),文件夾含有的文件與項(xiàng)目應(yīng)用(App)commodity相同,它主要實(shí)現(xiàn)網(wǎng)站首頁(yè)。

shopper文件夾: Django創(chuàng)建的項(xiàng)目應(yīng)用(App),它主要實(shí)現(xiàn)網(wǎng)站的購(gòu)物車頁(yè)面個(gè)人中心頁(yè)面、用戶登錄注冊(cè)頁(yè)面在線支付功能等。

media文件夾:網(wǎng)站的媒體資源,用于存放商品的主圖和詳細(xì)介紹圖。

pstatic件夾:網(wǎng)站的靜態(tài)資源,用于存放網(wǎng)站的靜態(tài)資源文件,如CSS、JavaScript網(wǎng)站界面圖片。**

templates件夾:存放HTML模板文件,即網(wǎng)站的網(wǎng)頁(yè)文件。

manage.py 是項(xiàng)目的命令行工具,內(nèi)置多種方法與項(xiàng)目進(jìn)行交互。manage.py help,可以查看該工具的指令信息。

1.5 項(xiàng)目setting.py設(shè)置

  • 設(shè)置訪問(wèn)許可
    ALLOWED_HOSTS = ['*'] #允許任意主機(jī)訪問(wèn)
  • 添加/激活項(xiàng)目應(yīng)用(點(diǎn)式路徑)
INSTALLED_APPS = [
...
'commodity.apps.CommodityConfig',
'index.apps.IndexConfig',
'shopper.apps.ShopperConfig',
]
  • 添加中間件
MIDDLEWARE = [
...
'django.middleware.locale.LocaleMiddleware',  # 添加的中間件: 本地媒體資源
]
  • 配置靜態(tài)資源
# 設(shè)置靜態(tài)資源的路由地址,其作用是使瀏覽器能成功訪問(wèn)Django的靜態(tài)資源。
STATIC_URL = '/static/'

# 若想在網(wǎng)頁(yè)上正常訪問(wèn)靜態(tài)資源文件,可以將文件夾static寫(xiě)入資源集合STATICFILES_DIRS
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'pstatic'),
]

# 實(shí)現(xiàn)服務(wù)器和項(xiàng)目之間的映射。
# STATIC_ROOT主要收集整個(gè)項(xiàng)目的靜態(tài)資源并存放在一個(gè)新的文件夾,
# 然后由該文件夾與服務(wù)器之間構(gòu)建映射關(guān)系。
STATIC_ROOT = os.path.join(BASE_DIR, 'AllStatic')
  • 配置媒體資源
    STATIC_URL是設(shè)置靜態(tài)文件的路由地址,如CSS樣式文件、JavaScript文件以及常用圖片等。
    對(duì)于一些經(jīng)常變動(dòng)的資源,通常將其存放在媒體資源文件夾,如用戶頭像、商品主圖、商品詳細(xì)介紹圖等。
MEDIA_URL='/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  • 配置模板資源
TEMPLATES = [
    {
       ...
        # 配置模板資源
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
...},
]
  • 設(shè)置語(yǔ)言和時(shí)區(qū)——本地化后臺(tái)界面語(yǔ)言和時(shí)區(qū)
LANGUAGE_CODE = 'zh-hans'  # 中文簡(jiǎn)體
TIME_ZONE = 'Asia/Shanghai'  # 中國(guó)上海時(shí)區(qū)
  • 配置數(shù)據(jù)庫(kù)
    本文采用自帶的Sqlite數(shù)據(jù)庫(kù)
    創(chuàng)建默認(rèn)數(shù)據(jù)表和超級(jí)管理員
# 創(chuàng)建默認(rèn)數(shù)據(jù)表
python manage.py migrate
# 創(chuàng)建超級(jí)管理員
python manage.py createsuperuser
  • 賬號(hào): admin,emai:xxx@xx.xx, 密碼: **************
  • 啟動(dòng)服務(wù)器
    python manage.py runserver 0.0.0.0:8000
  • **瀏覽器: ** localhost:8000
    image.png
  • 瀏覽器: localhost:8000/admin
    image.png
  • 登錄成功跳轉(zhuǎn)的頁(yè)面
    image.png

二、商城網(wǎng)址的規(guī)劃(原文網(wǎng)址)

2.1 設(shè)置路由分發(fā)規(guī)則

  • 一個(gè)完整的路由包含:路由地址、視圖函數(shù)(或者視圖類)、路由變量路由命名。
  • settings.py 中有這樣得到一條語(yǔ)句: ROOT_URLCONF = 'config.urls' 是定義項(xiàng)目所有路由地址的總?cè)肟?/code>
  • 為方便管理,分別為每個(gè)項(xiàng)目應(yīng)用(App)定義一條路由入口
  • 分別在項(xiàng)目應(yīng)用indexshopper、commodity新建urls.py,然后添加到config文件夾的urls.py
  • 設(shè)置項(xiàng)目主路由
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(('index.urls', 'index'), namespace='index')),
    path('commodity', include(('commodity.urls', 'commodity'), namespace='commodity')),
    path('shopper', include(('shopper.urls', 'shopper'), namespace='shopper')),
]

2.2 設(shè)置商城的路由地址

  • index App的urls.py
from django.urls import path
from .views import *

urlpatterns = [
    path('', indexView, name='index'),
]

(1)第一個(gè)參數(shù)為空字符串,這是設(shè)置具體的路由地址
(2)第二個(gè)參數(shù)為indexView,這是指向項(xiàng)目應(yīng)用index的views.py的某個(gè)視圖函數(shù)或視圖類
(3)第三個(gè)參數(shù)為name=‘index’,這是函數(shù)path的可選參數(shù),該參數(shù)是命名路由地址。

  • commodity App的urls.py
from django.urls import path
from .views import *

urlpatterns = [
   path('.html',commodityView,name='commodity'),
   path('/detail.<int:id>.html',detailView,name='detail'),
   path('/collect.html', collectView, name='collect')
]

(1) 路由detail設(shè)置了路由變量id,變量id對(duì)應(yīng)商品信息表的主鍵id,通過(guò)改變變量id的數(shù)值可以查看不同商品的詳細(xì)介紹。
(2) 路由地址的末端設(shè)置了“.html”,這是一種偽靜態(tài)URL技術(shù),可將網(wǎng)址設(shè)置為靜態(tài)網(wǎng)址,用于SEO搜索引擎的爬取,如百度、谷歌等。

  • shopper App的urls.py
from django.urls import path
from .views import *

urlpatterns = [
     path('.html', shopperView, name='shopper'),
     path('/login.html', loginView, name='login'),
     path('/logout.html', logoutView, name='logout'),
     path('/shopcart.html', shopcartView, name='shopcart'),
]

(1) 路由shopper代表個(gè)人中心頁(yè)
(2) 路由login代表用戶登錄注冊(cè)頁(yè)
(3) 路由logout實(shí)現(xiàn)個(gè)人中心的用戶注銷功能
(4) 路由shopcart代表購(gòu)物車信息頁(yè)

2.3 視圖函數(shù)的簡(jiǎn)單設(shè)置

此處僅對(duì)視圖函數(shù)作簡(jiǎn)單配置,以便進(jìn)行數(shù)據(jù)模型的創(chuàng)建,后續(xù)將會(huì)進(jìn)行修改

  • index App的views.py
from django.http import HttpResponse

def indexView(request):
    return HttpResponse('Hello World')
  • commodity App的views.py文件
from django.http import HttpResponse

def commodityView(request):
    return HttpResponse('Hello World')

def detailView(request, id):
    return HttpResponse('Hello World')
  • shopper App的views.py文件
from django.http import HttpResponse

def shopperView(request):
    return HttpResponse('Hello World')

def loginView(request):
    return HttpResponse('Hello World')

def logoutView(request):
    return HttpResponse('Hello World')

def shopcartView(request):
    return HttpResponse('Hello World')

三、數(shù)據(jù)模型搭建(原文網(wǎng)址

3.1 commodity的models.py文件

from django.db import models

# Create your models here.


class Types(models.Model):
    id = models.AutoField(primary_key=True)
    firsts = models.CharField('一級(jí)類型', max_length=100)
    seconds = models.CharField('二級(jí)類型', max_length=100)

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

    class Meta:
        verbose_name = '商品類型'
        verbose_name_plural = '商品類型'

class CommodityInfos(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField('商品名稱', max_length=100)
    sezes = models.CharField('顏色規(guī)格', max_length=100)
    types = models.CharField('商品類型', max_length=100)
    price = models.FloatField('商品價(jià)格')
    discount = models.FloatField('折后價(jià)格')
    stock = models.IntegerField('存貨數(shù)量')
    sold = models.IntegerField('已售數(shù)量')
    likes = models.IntegerField('收藏?cái)?shù)量')
    created = models.DateField('上架日期', auto_now_add=True)
    img = models.FileField('商品住圖', upload_to=r'imgs')
    details = models.FileField('商品介紹', upload_to=r'details')

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

    class Meta:
        verbose_name = '商品信息'
        verbose_name_plural = '商品信息'

3.2 shopper的models.py文件

from django.db import models

# Create your models here.


class CartInfos(models.Model):
    id = models.AutoField(primary_key=True)
    quantity = models.IntegerField('購(gòu)買數(shù)量')
    commodityInfos_id = models.IntegerField('商品ID')
    user_id = models.IntegerField('用戶ID')

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

    class Meta:
        verbose_name = '購(gòu)物車'
        verbose_name_plural = '購(gòu)物車'


class OrderInfos(models.Model):
    id = models.AutoField(primary_key=True)
    price = models.FloatField('訂單總價(jià)')
    created = models.DateField('創(chuàng)建時(shí)間', auto_now_add=True)
    user_id = models.IntegerField('用戶ID')
    state = models.CharField('訂單狀態(tài)', max_length=20, choices=(
        ('待支付', '待支付'),
        ('已支付', '已支付'),
        ('發(fā)貨中', '發(fā)貨中'),
        ('已簽收', '已簽收'),
        ('退貨中', '退貨中'),
    ))

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

    class Meta:
        verbose_name = '訂單信息'
        verbose_name_plural = '訂單信息'

3.4 數(shù)據(jù)遷移創(chuàng)建數(shù)據(jù)表

python manage.py makemigrations
python manage.py migrate

四、數(shù)據(jù)渲染與展示(原文網(wǎng)址

4.1 基礎(chǔ)模板設(shè)計(jì)

在項(xiàng)目babys的templates文件夾新建文件base.html
該文件用于存放每個(gè)網(wǎng)頁(yè)頂部的HTML代碼

<!DOCTYPE html>
<html lang="en">
<head>
    {% load static %}
    <title>{{title}}</title>
    <link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
    <link rel="stylesheet" type="text/css" href="{% static 'layui/css/layui.css' %}">
    <script type="text/javascript" src="{% static 'layui/layui.js' %}"></script>
</head>
<body>
  <div class="header">
    <div class="headerLayout w1200">
      <div class="headerCon">
        <h1 class="mallLogo">
          <a href="{% url 'index:index' %}" title="母嬰商城">
            <img src="{% static 'img/logo.png' %}">
          </a>
        </h1>
        <div class="mallSearch">
          <form action="{% url 'commodity:commodity' %}" method="get" class="layui-form" novalidate>
            <input type="text" name="n" required  lay-verify="required" autocomplete="off" class="layui-input" placeholder="請(qǐng)輸入需要的商品">
            <button class="layui-btn" lay-submit lay-filter="formDemo">
                <i class="layui-icon layui-icon-search"></i>
            </button>
          </form>
        </div>
      </div>
    </div>
  </div>

  <div class="content content-nav-base {{classContent}}">
    <div class="main-nav">
      <div class="inner-cont0">
        <div class="inner-cont1 w1200">
          <div class="inner-cont2">
            <a href="{% url 'index:index' %}" {% if classContent == ''%}class="active"{% endif %}>首頁(yè)</a>
            <a href="{% url 'commodity:commodity' %}" {% if classContent == 'commoditys'%}class="active"{% endif %}>所有商品</a>
            <a href="{% url 'shopper:shopcart' %}" {% if classContent == 'shopcarts'%}class="active"{% endif %}>購(gòu)物車</a>
            <a href="{% url 'shopper:shopper' %}" {% if classContent == 'informations'%}class="active"{% endif %}>個(gè)人中心</a>
          </div>
        </div>
      </div>
    </div>
    {% block content %}{% endblock content %}
  </div>

{% block footer %}{% endblock footer %}
<script type="text/javascript">{% block script %}{% endblock script %}</script>
</body>
</html>

4.2 首頁(yè)模板設(shè)計(jì)

模板文件index.html的代碼

{% extends 'base.html' %}
{% load static %}
{% block content %}
    <div class="category-con">
      <div class="category-banner">
        <div class="w1200">
          <img src="{% static 'img/banner1.jpg' %}">
        </div>
      </div>
    </div>
    <div class="floors">
      <div class="sk">
        <div class="sk_inner w1200">
          <div class="sk_hd">
            <a href="javascript:;">
              <img src="{% static 'img/s_img1.jpg' %}">
            </a>
          </div>
          <div class="sk_bd">
            <div class="layui-carousel" id="test1">
              <div carousel-item>
              <div class="item-box">
              {% for c in commodityInfos %}
                  {% if forloop.counter < 5 %}
                    <div class="item">
                    <a href="{% url 'commodity:detail' c.id %}">
                        <img src="{{ c.img.url }}"></a>
                    <div class="title">{{ c.name }}</div>
                    <div class="price">
                      <span>¥{{ c.discount|floatformat:'2' }}</span>
                      <del>¥{{ c.price|floatformat:'2' }}</del>
                    </div>
                    </div>
                  {% endif %}
                {% endfor %}
                </div>

              <div class="item-box">
              {% for c in commodityInfos %}
                  {% if forloop.counter > 4 %}
                    <div class="item">
                    <a href="{% url 'commodity:detail' c.id %}">
                        <img src="{{ c.img.url }}"></a>
                    <div class="title">{{ c.name }}</div>
                    <div class="price">
                      <span>¥{{ c.discount|floatformat:'2' }}</span>
                      <del>¥{{ c.price|floatformat:'2' }}</del>
                    </div>
                    </div>
                  {% endif %}
                {% endfor %}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="product-cont w1200" id="product-cont">
      <div class="product-item product-item1 layui-clear">
        <div class="left-title">
          <h4><i>1F</i></h4>
          <img src="{% static 'img/icon_gou.png' %}">
          <h5>寶寶服飾</h5>
        </div>
        <div class="right-cont">
          <a href="javascript:;" class="top-img"><img src="{% static 'img/img12.jpg' %}" alt=""></a>
          <div class="img-box">
            {% for c in clothes %}
              <a href="{% url 'commodity:detail' c.id %}"><img src="{{ c.img.url }}"></a>
            {% endfor %}
          </div>
        </div>
      </div>
      <div class="product-item product-item2 layui-clear">
        <div class="left-title">
          <h4><i>2F</i></h4>
          <img src="{% static 'img/icon_gou.png' %}">
          <h5>奶粉輔食</h5>
        </div>
        <div class="right-cont">
          <a href="javascript:;" class="top-img"><img src="{% static 'img/img12.jpg' %}" alt=""></a>
          <div class="img-box">
            {% for f in food %}
            <a href="{% url 'commodity:detail' f.id %}"><img src="{{ f.img.url }}"></a>
            {% endfor %}
          </div>
        </div>
      </div>
      <div class="product-item product-item3 layui-clear">
        <div class="left-title">
          <h4><i>3F</i></h4>
          <img src="{% static 'img/icon_gou.png' %}">
          <h5>寶寶用品</h5>
        </div>
        <div class="right-cont">
          <a href="javascript:;" class="top-img"><img src="{% static 'img/img12.jpg' %}"></a>
          <div class="img-box">
            {% for g in goods %}
            <a href="{% url 'commodity:detail' g.id %}"><img src="{{ g.img.url }}"></a>
            {% endfor %}
          </div>
        </div>
      </div>
    </div>
{% endblock content %}

{% block footer %}
  <div class="footer">
    <div class="ng-promise-box">
      <div class="ng-promise w1200">
        <p class="text">
          <a class="icon1" href="javascript:;">7天無(wú)理由退換貨</a>
          <a class="icon2" href="javascript:;">滿99元全場(chǎng)免郵</a>
          <a class="icon3" style="margin-right: 0" href="javascript:;">100%品質(zhì)保證</a>
        </p>
      </div>
    </div>
    <div class="mod_help w1200">
      <p>
        <a href="javascript:;">關(guān)于我們</a>
        <span>|</span>
        <a href="javascript:;">幫助中心</a>
        <span>|</span>
        <a href="javascript:;">售后服務(wù)</a>
        <span>|</span>
        <a href="javascript:;">母嬰資訊</a>
        <span>|</span>
        <a href="javascript:;">關(guān)于貨源</a>
      </p>
    </div>
  </div>
{% endblock footer %}

{% block script %}
layui.config({
    base: '{% static 'js/' %}'
  }).use(['mm','carousel'],function(){
      var carousel = layui.carousel,
     mm = layui.mm;
     var option = {
        elem: '#test1'
        ,width: '100%'
        ,arrow: 'always'
        ,height:'298'
        ,indicator:'none'
      }
      carousel.render(option);
});
{% endblock script %}
  • 使用{%load static %}讀取靜態(tài)資源
  • 重寫(xiě)base.html定義的接口content
  • 實(shí)現(xiàn)“今日必?fù)尅钡纳唐窡徜N功能,該功能共分為2頁(yè),每頁(yè)自動(dòng)進(jìn)行輪播展示
  • {% url ‘commodity:detail’ c.id %}是使用商品的主鍵字段id生成對(duì)應(yīng)的商品詳細(xì)頁(yè)的路由地址,當(dāng)單擊商品即可查看商品詳細(xì)頁(yè)
  • {{ c.img.url }}代表當(dāng)前遍歷對(duì)象的字段img
  • {{ c.name }}獲取當(dāng)前遍歷對(duì)象的字段name
  • {{ c.discount|floatformat:‘2’ }}獲取當(dāng)前遍歷對(duì)象的字段discount
  • {{ c.price|floatformat:‘2’ }}獲取當(dāng)前遍歷對(duì)象的字段price
  • 實(shí)現(xiàn)某分類的商品熱銷功能,分別為“寶寶服飾”、“奶粉輔食”和“寶寶用品”
  • 重寫(xiě)base.html定義的接口footer和script

4.3 配置媒體資源的路由信息

from django.contrib import admin
from django.urls import path, include, re_path
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(('index.urls','index'),namespace='index')),
    path('commodity', include(('commodity.urls','commodity'),namespace='commodity')),
    path('shopper', include(('shopper.urls','shopper'),namespace='shopper')),
    re_path('media/(?P<path>.*)', serve, {'document_root':settings.MEDIA_ROOT}, name='media'),
    # 配置媒體資源的路由信息
]

五、商品信息模塊(原文網(wǎng)址

5.1 商品列表頁(yè)邏輯代碼(commodity App的view.py)

from django.core.paginator import Paginator, PageNotAnInteger,EmptyPage
from django.http import HttpResponse
from django.shortcuts import render

from .models import *


def commodityView(request):
    title = '商品列表'
    classContent = 'commoditys'
    firsts = Types.objects.values('firsts').distinct()
    typesList = Types.objects.all()

    # 獲取請(qǐng)求參數(shù)
    t = request.GET.get('t','')
    s = request.GET.get('s','sold')
    p = request.GET.get('p',1)
    n = request.GET.get('n','')

    commodityInfos = CommodityInfos.objects.all()
    if t:
        types = Types.objects.filter(id=t).first()
        commodityInfos = commodityInfos.filter(types=types.seconds)
    if s:
        commodityInfos = commodityInfos.order_by('-'+ s)
    if n:
        commodityInfos = commodityInfos.filter(name_contains=n)

    pagintor = Paginator(commodityInfos,6)
    try:
        pages = pagintor.page(p)
    except PageNotAnInteger:
        pages = pagintor.page(1)
    except EmptyPage:
        pages = pagintor.page(pagintor.num_pages)

    return render(request,'commodity.html',locals())

(1)變量n是商品搜索功能的關(guān)鍵字,它與模型CommodityInfos的字段name進(jìn)行模糊匹配,因此查詢條件為name__contains=n。
(2)變量t是查詢某個(gè)分頁(yè)的商品信息,它以整型格式表示,代表模型Type的主鍵id
(3)變量s是設(shè)置商品的排序方式,如果請(qǐng)求參數(shù)s為空,則默認(rèn)變量s等于字符串sold
(4)變量p是設(shè)置商品信息的頁(yè)數(shù),默認(rèn)變量p等于1

5.2 商品列表頁(yè)的數(shù)據(jù)渲染

  • commodity App的templates文件夾中新建commodity.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
    <div class="commod-cont-wrap">
      <div class="commod-cont w1200 layui-clear">
        <div class="left-nav">
          <div class="title">所有分類</div>
          <div class="list-box">
            {% for f in firsts %}
            <dl>
             <dt>{{ f.firsts }}</dt>
             {% for t in typesList %}
                 {% if t.firsts == f.firsts %}
                    <dd><a href="{% url 'commodity:commodity' %}?t={{ t.id }}&n={{ n }}">{{ t.seconds }}</a></dd>
                 {% endif %}
             {% endfor %}
            </dl>
            {% endfor %}

          </div>
        </div>
        <div class="right-cont-wrap">
          <div class="right-cont">
            <div class="sort layui-clear">

              <a {% if not s or s == 'sold' %}class="active" {% endif %} href="{% url 'commodity:commodity' %}?t={{ t }}&s=sold&n={{ n }}">銷量</a>
              <a {% if s == 'price' %}class="active" {% endif %} href="{% url 'commodity:commodity' %}?t={{ t }}&s=price&n={{ n }}">價(jià)格</a>
              <a {% if s == 'created' %}class="active" {% endif %} href="{% url 'commodity:commodity' %}?t={{ t }}&s=created&n={{ n }}">新品</a>
              <a {% if s == 'likes' %}class="active" {% endif %} href="{% url 'commodity:commodity' %}?t={{ t }}&s=likes&n={{ n }}">收藏</a>

            </div>
            <div class="prod-number">
                <a href="javascript:;">商品列表</a>
                <span>></span>
                <a href="javascript:;">共{{ commodityInfos|length }}件商品</a>
            </div>
            <div class="cont-list layui-clear" id="list-cont">
              {% for p in pages.object_list %}
              <div class="item">
                <div class="img">
                  <a href="{% url 'commodity:detail' p.id %}">
                  <img height="280" width="280" src="{{ p.img.url }}"></a>
                </div>
                <div class="text">
                  <p class="title">{{ p.name }}</p>
                  <p class="price">
                    <span class="pri">¥{{ p.price|floatformat:'2' }}</span>
                    <span class="nub">{{ p.sold }}付款</span>
                  </p>
                </div>
              </div>
              {% endfor %}
            </div>
          </div>
        </div>
      <div id="demo0" style="text-align: center;">
            <div class="layui-box layui-laypage layui-laypage-default" id="layui-laypage-1">
                {% if pages.has_previous %}
                    <a href="{% url 'commodity:commodity' %}?t={{ t }}&s={{ s }}&n={{ n }}&p={{ pages.previous_page_number }}" class="layui-laypage-prev">上一頁(yè)</a>
                {% endif %}

                {% for page in pages.paginator.page_range %}
                    {% if pages.number == page %}
                        <span class="layui-laypage-curr"><em class="layui-laypage-em"></em><em>{{ page }}</em></span>
                    {% elif pages.number|add:'-1' == page or pages.number|add:'1' == page %}
                        <a href="{% url 'commodity:commodity' %}?t={{ t }}&s={{ s }}&n={{ n }}&p={{ page }}">{{ page }}</a>
                    {% endif %}
                {% endfor %}

                {% if pages.has_next %}
                    <a href="{% url 'commodity:commodity' %}?t={{ t }}&s={{ s }}&n={{ n }}&p={{ pages.pages.next_page_number }}" class="layui-laypage-next">下一頁(yè)</a>
                {% endif %}
            </div>
      </div>
      </div>
    </div>
{% endblock content %}
  
{% block script %}
  layui.config({
    base: '{% static 'js/' %}'
  }).use(['mm','laypage','jquery'],function(){
      var laypage = layui.laypage,$ = layui.$,
      mm = layui.mm;
    $('.list-box dt').on('click',function(){
      if($(this).attr('off')){
        $(this).removeClass('active').siblings('dd').show()
        $(this).attr('off','')
      }else{
        $(this).addClass('active').siblings('dd').hide()
        $(this).attr('off',true)
      }
    })

});
{% endblock script %}

5.3 商品詳細(xì)頁(yè)

  • commodity的views.py定義視圖函數(shù)detailView
def detailView(request, id):
    title = '商品介紹'
    classContent = 'datails'
    commoditys = CommodityInfos.objects.filter(id=id).first()
    items = CommodityInfos.objects.exclude(id=id).order_by('-sold')[:5]
    likesList = request.session.get('likes', [])
    likes = True if id in likesList else False
    return render(request, 'details.html', locals())
  • commodity的templates文件夾中新建details.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
    <div class="data-cont-wrap w1200">
      <div class="crumb">
        <a href="{% url 'index:index' %}">首頁(yè)</a>
        <span>></span>
        <a href="{% url 'commodity:commodity' %}">所有商品</a>
        <span>></span>
        <a href="javascript:;">產(chǎn)品詳情</a>
      </div>
      <div class="product-intro layui-clear">
        <div class="preview-wrap">
          <img height="300" width="300" src="{{ commoditys.img.url }}">
        </div>
        <div class="itemInfo-wrap">
          <div class="itemInfo">
            <div class="title">
              <h4>{{ commoditys.name }}</h4>
                {% if likes %}
              <span id="collect"><i class="layui-icon layui-icon-rate-solid"></i>收藏</span>
                {% else %}
              <span id="collect"><i class="layui-icon layui-icon-rate"></i>收藏</span>
                {% endif %}
            </div>
            <div class="summary">
              <p class="reference"><span>參考價(jià)</span> <del>¥{{ commoditys.price|floatformat:'2' }}</del></p>
              <p class="activity"><span>活動(dòng)價(jià)</span><strong class="price"><i>¥</i>{{ commoditys.discount|floatformat:'2' }}</strong></p>
              <p class="address-box"><span>送&nbsp;&nbsp;&nbsp;&nbsp;至</span><strong class="address">廣東&nbsp;&nbsp;廣州&nbsp;&nbsp;天河區(qū)</strong></p>
            </div>
            <div class="choose-attrs">
              <div class="color layui-clear"><span class="title">規(guī)&nbsp;&nbsp;&nbsp;&nbsp;格</span> <div class="color-cont"><span class="btn active">{{ commoditys.sezes }}</span></div></div>
              <div class="number layui-clear"><span class="title">數(shù)&nbsp;&nbsp;&nbsp;&nbsp;量</span><div class="number-cont">
                  <span class="cut btn">-</span>
                  <input onkeyup="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.replace(/\D/g,'')}" onafterpaste="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.replace(/\D/g,'')}" maxlength="4" type="" id="quantity" value="1">
                  <span class="add btn">+</span></div></div>
            </div>
            <div class="choose-btns">
              <a class="layui-btn  layui-btn-danger car-btn">
                  <i class="layui-icon layui-icon-cart-simple"></i>加入購(gòu)物車</a>
            </div>
          </div>
        </div>
      </div>
      <div class="layui-clear">
        <div class="aside">
          <h4>熱銷推薦</h4>
          <div class="item-list">
            {% for item in items %}
            <div class="item">
              <a href="{% url 'commodity:detail' item.id %}">
              <img height="280" width="280" src="{{ item.img.url }}">
              </a>
              <p>
                  <span title="{{ item.name }}">
                      {% if item.name|length > 15 %}
                        {{ item.name|slice:":15" }}...
                      {% else %}
                        {{ item.name|slice:":15" }}
                      {% endif %}
                  </span>
                  <span class="pric">¥{{ item.discount|floatformat:'2' }}</span>
              </p>
            </div>
            {% endfor %}
          </div>
        </div>
        <div class="detail">
          <h4>詳情</h4>
          <div class="item">
            <img width="800" src="{{ commoditys.details.url }}">
          </div>
        </div>
      </div>
    </div>
{% endblock content %}

{% block script %}
  layui.config({
    base: '{% static 'js/' %}'
  }).use(['mm','jquery'],function(){
      var mm = layui.mm,$ = layui.$;
      var cur = $('.number-cont input').val();
      $('.number-cont .btn').on('click',function(){
        if($(this).hasClass('add')){
          cur++;
        }else{
          if(cur > 1){
            cur--;
          }
        }
        $('.number-cont input').val(cur)
      })

    $('.layui-btn.layui-btn-danger.car-btn').on('click',function(){
        var quantity = $("#quantity").val();
        window.location = "{% url 'shopper:shopcart' %}?id={{ commoditys.id }}&quantity=" + quantity
    });

    $('#collect').on('click',function(){
        var url = "{% url 'commodity:collect' %}?id={{ commoditys.id }}"
        $.get(url ,function(data,status){
            if (data.result=="收藏成功"){
                $('#collect').find("i").removeClass("layui-icon-rate")
                $('#collect').find("i").addClass("layui-icon-rate-solid")
            }
            alert(data.result);
        });
    });
  });

{% endblock script %}

5.4 Ajax實(shí)現(xiàn)商品收藏

commodity的views.py添加視圖函數(shù)collectView

def collectView(request):
    id = request.GET.get('id', '')
    result = {"result": "已收藏"}
    likes = request.session.get('likes', [])
    if id and not int(id) in likes:
        # 對(duì)商品的收藏?cái)?shù)量執(zhí)行自增加1
        CommodityInfos.objects.filter(id=id).update(likes=F('likes')+1)
        result['result'] = "收藏成功"
        request.session['likes'] = likes + [int(id)]
    return JsonResponse(result)

六、用戶信息模塊(原文網(wǎng)址

6.1 內(nèi)置User實(shí)現(xiàn)注冊(cè)登錄

  • shopper的views.py編寫(xiě)視圖函數(shù)loginView的業(yè)務(wù)邏輯
from django.contrib.auth import authenticate, login
from django.contrib.auth.models import User
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.urls import reverse

def loginView(request):
    title = '用戶登錄'
    classContent = 'logins'
    if request.method == 'POST':
        username = request.POST.get('username','')
        password = request.POST.get('password','')
        if User.objects.filter(username=username):
            user = authenticate(username=username,password=password)
            if user:
                login(request,user)
                return redirect(reverse('shopper:shopper'))
        else:
            state = '注冊(cè)成功'
            d=dict(username=username,password=password,
                   is_staff=1,is_active=1)
            user = User.objects.create_user(**d)
            user.save()
    return render(request,'login.html',locals())
  • 在shopper App的templates文件夾中新建login.html模板文件
{% extends 'base.html' %}
{% load static %}
{% block content %}
    <div class="login-bg">
      <div class="login-cont w1200">
        <div class="form-box">
          <form class="layui-form" action="" method="post">
            {% csrf_token %}
            <legend>手機(jī)號(hào)注冊(cè)登錄</legend>
            <div class="layui-form-item">
              <div class="layui-inline iphone">
                <div class="layui-input-inline">
                  <i class="layui-icon layui-icon-cellphone iphone-icon"></i>
                  <input name="username" id="username" lay-verify="required|phone" placeholder="請(qǐng)輸入手機(jī)號(hào)" class="layui-input">
                {{ infos.username }}
                </div>
              </div>
              <div class="layui-inline iphone">
                <div class="layui-input-inline">
                  <i class="layui-icon layui-icon-password iphone-icon"></i>
                  <input id="password" type="password" name="password" lay-verify="required|password" placeholder="請(qǐng)輸入密碼" class="layui-input">
                {{ infos.password }}
                </div>
              </div>
            </div>
            <p>{{ state }}</p>
            <div class="layui-form-item login-btn">
              <div class="layui-input-block">
                <button class="layui-btn" lay-submit="" lay-filter="demo1">注冊(cè)/登錄</button>
              </div>
            </div>
          </form>
        </div>
      </div>
    </div>
{% endblock content %}

{% block footer %}
  <div class="footer">
    <div class="ng-promise-box">
      <div class="ng-promise w1200">
        <p class="text">
          <a class="icon1" href="javascript:;">7天無(wú)理由退換貨</a>
          <a class="icon2" href="javascript:;">滿99元全場(chǎng)免郵</a>
          <a class="icon3" style="margin-right: 0" href="javascript:;">100%品質(zhì)保證</a>
        </p>
      </div>
    </div>
    <div class="mod_help w1200">
      <p>
        <a href="javascript:;">關(guān)于我們</a>
        <span>|</span>
        <a href="javascript:;">幫助中心</a>
        <span>|</span>
        <a href="javascript:;">售后服務(wù)</a>
        <span>|</span>
        <a href="javascript:;">母嬰資訊</a>
        <span>|</span>
        <a href="javascript:;">關(guān)于貨源</a>
      </p>
      <p class="coty">母嬰商城版權(quán)所有 &copy; 2012-2020</p>
    </div>
  </div>
{% endblock footer %}

{% block script %}
   layui.config({
      base: '{% static 'js/' %}'
    }).use(['jquery','form'],function(){
          var $ = layui.$,form = layui.form;
        $("#find").click(function() {
            if(!/^1\d{10}$/.test($("#username").val())){
              layer.msg("請(qǐng)輸入正確的手機(jī)號(hào)");
              return false;
            }
        })
    })
{% endblock script %}

需要在表單中加入{% csrf_token %}設(shè)置CSRF防護(hù)
avaScript腳本代碼是驗(yàn)證用戶輸入的用戶名是否為手機(jī)號(hào)碼,驗(yàn)證方式使用正則表達(dá)式,比如if(!/^1\d{10}/.test(("#username").val())),其中("#username").val()定位id=username的input控件,然后獲取該控件的屬性value的值,最后使用if(!/^1\d{10}/.test()對(duì)屬性value的值進(jìn)行正則判斷,驗(yàn)證該值是否為長(zhǎng)度等于11的手機(jī)號(hào)碼。

6.2 個(gè)人中心頁(yè)

  • shopper的views.py定義視圖函數(shù)shopperView

使用Django的內(nèi)置裝飾器login_required設(shè)置用戶登錄訪問(wèn)權(quán)限,如果當(dāng)前用戶在尚未登錄的狀態(tài)下訪問(wèn)個(gè)人中心頁(yè),程序就會(huì)自動(dòng)跳轉(zhuǎn)到指定的路由地址

from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.urls import reverse
from shopper.models import *


@login_required(login_url='/shopper/login.html')
def shopperView(request):
    title = '個(gè)人中心'
    classContenr = 'informations'
    p = request.GET.get('p',1)
    t = request.GET.get('t','')
    payTime = request.session.get('payTime','')
    if t and payTime and t == payTime:
        payInfo = request.session.get('payInfo','')
        OrderInfos.objects.create(**payInfo)
        del request.session['payTime']
        del request.session['payInfo']
    orderInfos = OrderInfos.objects.filter(user_id=request.user.id).order_by('-created')
    paginator = Paginator(orderInfos,7)
    try:
        pages = paginator.page(p)
    except PageNotAnInteger:
        pages = paginator.page(1)
    except EmptyPage:
        pages = paginator.page(paginator.num_pages)
    return render(request,'shopper.html',locals())

def loginView(request):
    title = '用戶登錄'
    classContent = 'logins'
    if request.method == 'POST':
        username = request.POST.get('username','')
        password = request.POST.get('password','')
        if User.objects.Filter(username=username):
            user = authenticate(username=username,password=password)
            if user:
                login(request,user)
                return redirect(reverse('shopper:shopper'))
        else:
            state = '注冊(cè)成功'
            d=dict(username=username,password=password,
                   is_staff=1,is_active=1)
            user = User.objects.create_user(**d)
            user.save()
    return render(request,'login.html',locals())

def logoutView(request):
    return HttpResponse('Hello World')

def shopcartView(request):
    return HttpResponse('Hello World')
  • 模板文件shopper.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
    <div class="info-list-box">
      <div class="info-list">
        <div class="item-box layui-clear" id="list-cont">
          <div class="item">
            <div class="img">
              <img src="{% static 'img/portrait' %}" alt="">
            </div>
            <div class="text">
              <h4>{{user.suername}}</h4>
              <p class="data">登錄時(shí)間:{{user.last_login}}</p>
              <div class="left-nav">
                  <div class="title">
                    <a href="{% url 'shopper:shopcart' %}">我的購(gòu)物車</a>
                  </div>
                  <div class="title">
                    <a href="{% url 'shopper:logout' %}">我的優(yōu)惠卷</a>
                  </div>
              </div>
            </div>
          </div>
<div class="item1">
        
        <div class="cart">
      <div class="cart-table-th">

        <div class="th th-item">
          <div class="th-inner">
            訂單編號(hào)
          </div>
        </div>
        <div class="th th-price">
          <div class="th-inner">
            訂單價(jià)格
          </div>
        </div>
        <div class="th th-amount">
          <div class="th-inner">
            購(gòu)買時(shí)間
          </div>
        </div>
        <div class="th th-sum">
          <div class="th-inner">
            訂單狀態(tài)
          </div>
        </div>
      </div>
       <div class="OrderList">
        <div class="order-content" id="list-cont">
          {% for p in pages.object_list %}
          <ul class="item-content layui-clear">
            <li class="th th-item">{{ p.id }}</li>
            <li class="th th-price">¥{{ p.price|floatformat:'2' }}</li>
            <li class="th th-amount">{{ p.created }}</li>
            <li class="th th-sum">{{ p.state }}</li>
          </ul>
          {% endfor %}
        </div>
      </div>

        </div>
        </div>
    </div>
    </div>
      <div id="demo0" style="text-align: center;">
            <div class="layui-box layui-laypage layui-laypage-default" id="layui-laypage-1">
                {% if pages.has_previous %}
                    <a href="{% url 'shopper:shopper' %}?p={{ pages.previous_page_number }}" class="layui-laypage-prev">上一頁(yè)</a>
                {% endif %}

                {% for page in pages.paginator.page_range %}
                    {% if pages.number == page %}
                        <span class="layui-laypage-curr"><em class="layui-laypage-em"></em><em>{{ page }}</em></span>
                    {% elif pages.number|add:'-1' == page or pages.number|add:'1' == page %}
                        <a href="{% url 'shopper:shopper' %}?p={{ page }}">{{ page }}</a>
                    {% endif %}
                {% endfor %}

                {% if pages.has_next %}
                    <a href="{% url 'shopper:shopper' %}?p={{ pages.pages.next_page_number }}" class="layui-laypage-next">下一頁(yè)</a>
                {% endif %}
            </div>
      </div>
  </div>
{% endblock content %}

{% block script %}
  layui.config({
    base: '{% static 'js/' %}'
  }).use(['mm','laypage'],function(){
      var mm = layui.mm,laypage = layui.laypage;
});
{% endblock script %}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容