Django实践1——电子商城项目

简书文章目录生成脚本: 左侧目录
不要错过,在简书,你一定需要的
喜欢本文,请点个👍Thanks!

参考资料: 《精通Django 3 Web开发》源码
编程环境: Anaconda 2.4 + Python 3.8 + PyCharm 2021.2 + Django 3.2.7

一、设计与配置(原文网址

1.1 设计

  • 网站首页——5个功能区
      • 商品搜索功能、网站导航、广告轮播、商品分类热销、网站尾部
  • 商品列表页——4个功能区
      • 商品搜索功能、网站导航、商品分类、商品列表信息
  • 商品详细页——5个功能区
      • 商品搜索功能、网站导航、商品基本信息、商品详细介绍、热销推荐
  • 购物车页面——3个功能区
      • 商品搜索功能、网站导航、商品的购买费用核算
  • 个人中心页面——4个功能区
      • 商品搜索功能、网站导航、用户基本信息、订单信息
  • 用户登录注册页面——3个功能区
      • 商品搜索功能、网站导航、登录注册表单

1.2 数据结构图

数据结构图

1.3 搭建项目功能配置

  • 创建项目myproject
cd 项目存储目录
mkdir myproject & cd myproject 
django-admin startproject config .
  • 创建项目应用(Apps): indexcommodityshopper
python manage.py startapp index
python manage.py startapp commodity
python manage.py startapp shopper
  • 创建文件夹mediapstatictemplates
mkdir media
mkdir pstatic
mkdir templates
  • 文件夹及文件说明

congfig文件夹: 含有文件__init__.pyasgi.pysettings.pyurls.pywsgi.py

commodity文件夹: Django创建的项目应用(App),文件夹含有__init__.pyadmin.pyapps.pymodels.pytests.pyviews.py文件,它主要实现网站的商品列表页商品详细页

index文件夹: Django创建的项目应用(App),文件夹含有的文件与项目应用(App)commodity相同,它主要实现网站首页

shopper文件夹: Django创建的项目应用(App),它主要实现网站的购物车页面个人中心页面用户登录注册页面在线支付功能等。

media文件夹:网站的媒体资源,用于存放商品的主图和详细介绍图。

pstatic件夹:网站的静态资源,用于存放网站的静态资源文件,如CSSJavaScript网站界面图片。**

templates件夹:存放HTML模板文件,即网站的网页文件

manage.py 是项目的命令行工具,内置多种方法与项目进行交互。manage.py help,可以查看该工具的指令信息。

1.5 项目setting.py设置

  • 设置访问许可
    ALLOWED_HOSTS = ['*'] #允许任意主机访问
  • 添加/激活项目应用(点式路径)
INSTALLED_APPS = [
...
'commodity.apps.CommodityConfig',
'index.apps.IndexConfig',
'shopper.apps.ShopperConfig',
]
  • 添加中间件
MIDDLEWARE = [
...
'django.middleware.locale.LocaleMiddleware',  # 添加的中间件: 本地媒体资源
]
  • 配置静态资源
# 设置静态资源的路由地址,其作用是使浏览器能成功访问Django的静态资源。
STATIC_URL = '/static/'

# 若想在网页上正常访问静态资源文件,可以将文件夹static写入资源集合STATICFILES_DIRS
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'pstatic'),
]

# 实现服务器和项目之间的映射。
# STATIC_ROOT主要收集整个项目的静态资源并存放在一个新的文件夹,
# 然后由该文件夹与服务器之间构建映射关系。
STATIC_ROOT = os.path.join(BASE_DIR, 'AllStatic')
  • 配置媒体资源
    STATIC_URL是设置静态文件的路由地址,如CSS样式文件、JavaScript文件以及常用图片等。
    对于一些经常变动的资源,通常将其存放在媒体资源文件夹,如用户头像、商品主图、商品详细介绍图等。
MEDIA_URL='/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  • 配置模板资源
TEMPLATES = [
    {
       ...
        # 配置模板资源
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
...},
]
  • 设置语言和时区——本地化后台界面语言和时区
LANGUAGE_CODE = 'zh-hans'  # 中文简体
TIME_ZONE = 'Asia/Shanghai'  # 中国上海时区
  • 配置数据库
    本文采用自带的Sqlite数据库
    创建默认数据表和超级管理员
# 创建默认数据表
python manage.py migrate
# 创建超级管理员
python manage.py createsuperuser
  • 账号: adminemai:xxx@xx.xx, 密码: **************
  • 启动服务器
    python manage.py runserver 0.0.0.0:8000
  • **浏览器: ** localhost:8000
    image.png
  • 浏览器: localhost:8000/admin
    image.png
  • 登录成功跳转的页面
    image.png

二、商城网址的规划(原文网址)

2.1 设置路由分发规则

  • 一个完整的路由包含:路由地址视图函数(或者视图类)路由变量路由命名
  • settings.py 中有这样得到一条语句: ROOT_URLCONF = 'config.urls' 是定义项目所有路由地址的总入口
  • 为方便管理,分别为每个项目应用(App)定义一条路由入口
  • 分别在项目应用indexshoppercommodity新建urls.py,然后添加到config文件夹的urls.py
  • 设置项目主路由
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 设置商城的路由地址

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

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

(1)第一个参数为空字符串,这是设置具体的路由地址
(2)第二个参数为indexView,这是指向项目应用index的views.py的某个视图函数或视图类
(3)第三个参数为name=‘index’,这是函数path的可选参数,该参数是命名路由地址。

  • 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设置了路由变量id,变量id对应商品信息表的主键id,通过改变变量id的数值可以查看不同商品的详细介绍。
(2) 路由地址的末端设置了“.html”,这是一种伪静态URL技术,可将网址设置为静态网址,用于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代表个人中心页
(2) 路由login代表用户登录注册页
(3) 路由logout实现个人中心的用户注销功能
(4) 路由shopcart代表购物车信息页

2.3 视图函数的简单设置

此处仅对视图函数作简单配置,以便进行数据模型的创建,后续将会进行修改

  • 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')

三、数据模型搭建(原文网址

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('一级类型', max_length=100)
    seconds = models.CharField('二级类型', 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('颜色规格', max_length=100)
    types = models.CharField('商品类型', max_length=100)
    price = models.FloatField('商品价格')
    discount = models.FloatField('折后价格')
    stock = models.IntegerField('存货数量')
    sold = models.IntegerField('已售数量')
    likes = models.IntegerField('收藏数量')
    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('购买数量')
    commodityInfos_id = models.IntegerField('商品ID')
    user_id = models.IntegerField('用户ID')

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

    class Meta:
        verbose_name = '购物车'
        verbose_name_plural = '购物车'


class OrderInfos(models.Model):
    id = models.AutoField(primary_key=True)
    price = models.FloatField('订单总价')
    created = models.DateField('创建时间', auto_now_add=True)
    user_id = models.IntegerField('用户ID')
    state = models.CharField('订单状态', max_length=20, choices=(
        ('待支付', '待支付'),
        ('已支付', '已支付'),
        ('发货中', '发货中'),
        ('已签收', '已签收'),
        ('退货中', '退货中'),
    ))

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

    class Meta:
        verbose_name = '订单信息'
        verbose_name_plural = '订单信息'

3.4 数据迁移创建数据表

python manage.py makemigrations
python manage.py migrate

四、数据渲染与展示(原文网址

4.1 基础模板设计

在项目babys的templates文件夹新建文件base.html
该文件用于存放每个网页顶部的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="请输入需要的商品">
            <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 %}>首页</a>
            <a href="{% url 'commodity:commodity' %}" {% if classContent == 'commoditys'%}class="active"{% endif %}>所有商品</a>
            <a href="{% url 'shopper:shopcart' %}" {% if classContent == 'shopcarts'%}class="active"{% endif %}>购物车</a>
            <a href="{% url 'shopper:shopper' %}" {% if classContent == 'informations'%}class="active"{% endif %}>个人中心</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 首页模板设计

模板文件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天无理由退换货</a>
          <a class="icon2" href="javascript:;">满99元全场免邮</a>
          <a class="icon3" style="margin-right: 0" href="javascript:;">100%品质保证</a>
        </p>
      </div>
    </div>
    <div class="mod_help w1200">
      <p>
        <a href="javascript:;">关于我们</a>
        <span>|</span>
        <a href="javascript:;">帮助中心</a>
        <span>|</span>
        <a href="javascript:;">售后服务</a>
        <span>|</span>
        <a href="javascript:;">母婴资讯</a>
        <span>|</span>
        <a href="javascript:;">关于货源</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 %}读取静态资源
  • 重写base.html定义的接口content
  • 实现“今日必抢”的商品热销功能,该功能共分为2页,每页自动进行轮播展示
  • {% url ‘commodity:detail’ c.id %}是使用商品的主键字段id生成对应的商品详细页的路由地址,当单击商品即可查看商品详细页
  • {{ c.img.url }}代表当前遍历对象的字段img
  • {{ c.name }}获取当前遍历对象的字段name
  • {{ c.discount|floatformat:‘2’ }}获取当前遍历对象的字段discount
  • {{ c.price|floatformat:‘2’ }}获取当前遍历对象的字段price
  • 实现某分类的商品热销功能,分别为“宝宝服饰”、“奶粉辅食”和“宝宝用品”
  • 重写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'),
    # 配置媒体资源的路由信息
]

五、商品信息模块(原文网址

5.1 商品列表页逻辑代码(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()

    # 获取请求参数
    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是商品搜索功能的关键字,它与模型CommodityInfos的字段name进行模糊匹配,因此查询条件为name__contains=n。
(2)变量t是查询某个分页的商品信息,它以整型格式表示,代表模型Type的主键id
(3)变量s是设置商品的排序方式,如果请求参数s为空,则默认变量s等于字符串sold
(4)变量p是设置商品信息的页数,默认变量p等于1

5.2 商品列表页的数据渲染

  • 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 }}">价格</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">上一页</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">下一页</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 商品详细页

  • commodity的views.py定义视图函数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' %}">首页</a>
        <span>></span>
        <a href="{% url 'commodity:commodity' %}">所有商品</a>
        <span>></span>
        <a href="javascript:;">产品详情</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>参考价</span> <del>¥{{ commoditys.price|floatformat:'2' }}</del></p>
              <p class="activity"><span>活动价</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;天河区</strong></p>
            </div>
            <div class="choose-attrs">
              <div class="color layui-clear"><span class="title">规&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">数&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>加入购物车</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实现商品收藏

commodity的views.py添加视图函数collectView

def collectView(request):
    id = request.GET.get('id', '')
    result = {"result": "已收藏"}
    likes = request.session.get('likes', [])
    if id and not int(id) in likes:
        # 对商品的收藏数量执行自增加1
        CommodityInfos.objects.filter(id=id).update(likes=F('likes')+1)
        result['result'] = "收藏成功"
        request.session['likes'] = likes + [int(id)]
    return JsonResponse(result)

六、用户信息模块(原文网址

6.1 内置User实现注册登录

  • shopper的views.py编写视图函数loginView的业务逻辑
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 = '注册成功'
            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>手机号注册登录</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="请输入手机号" 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="请输入密码" 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">注册/登录</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天无理由退换货</a>
          <a class="icon2" href="javascript:;">满99元全场免邮</a>
          <a class="icon3" style="margin-right: 0" href="javascript:;">100%品质保证</a>
        </p>
      </div>
    </div>
    <div class="mod_help w1200">
      <p>
        <a href="javascript:;">关于我们</a>
        <span>|</span>
        <a href="javascript:;">帮助中心</a>
        <span>|</span>
        <a href="javascript:;">售后服务</a>
        <span>|</span>
        <a href="javascript:;">母婴资讯</a>
        <span>|</span>
        <a href="javascript:;">关于货源</a>
      </p>
      <p class="coty">母婴商城版权所有 &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("请输入正确的手机号");
              return false;
            }
        })
    })
{% endblock script %}

需要在表单中加入{% csrf_token %}设置CSRF防护
avaScript脚本代码是验证用户输入的用户名是否为手机号码,验证方式使用正则表达式,比如if(!/^1\d{10}/.test(("#username").val())),其中("#username").val()定位id=username的input控件,然后获取该控件的属性value的值,最后使用if(!/^1\d{10}/.test()对属性value的值进行正则判断,验证该值是否为长度等于11的手机号码。

6.2 个人中心页

  • shopper的views.py定义视图函数shopperView

使用Django的内置装饰器login_required设置用户登录访问权限,如果当前用户在尚未登录的状态下访问个人中心页,程序就会自动跳转到指定的路由地址

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 = '个人中心'
    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 = '注册成功'
            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">登录时间:{{user.last_login}}</p>
              <div class="left-nav">
                  <div class="title">
                    <a href="{% url 'shopper:shopcart' %}">我的购物车</a>
                  </div>
                  <div class="title">
                    <a href="{% url 'shopper:logout' %}">我的优惠卷</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">
            订单编号
          </div>
        </div>
        <div class="th th-price">
          <div class="th-inner">
            订单价格
          </div>
        </div>
        <div class="th th-amount">
          <div class="th-inner">
            购买时间
          </div>
        </div>
        <div class="th th-sum">
          <div class="th-inner">
            订单状态
          </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">上一页</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">下一页</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 %}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容