文章目录生成脚本: 左侧目录
不要错过,在,你一定需要的
喜欢本文,请点个,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):
index
、commodity
、shopper
python manage.py startapp index
python manage.py startapp commodity
python manage.py startapp shopper
- 创建文件夹
media
、pstatic
、templates
mkdir media
mkdir pstatic
mkdir templates
- 文件夹及文件说明
congfig文件夹: 含有文件
__init__.py
、asgi.py
、settings.py
、urls.py
和wsgi.py
commodity文件夹: Django创建的项目应用(App),文件夹含有
__init__.py
、admin.py
、apps.py
、models.py
、tests.py
和views.py
文件,它主要实现网站的商品列表页
和商品详细页
index文件夹: Django创建的项目应用(App),文件夹含有的文件与项目应用(App)commodity相同,它主要实现
网站首页
。
shopper文件夹: Django创建的项目应用(App),它主要实现网站的
购物车页面
、个人中心页面
、用户登录注册页面
、在线支付功能
等。
media文件夹:网站的
媒体资源
,用于存放商品的主图和详细介绍图。
pstatic件夹:网站的
静态资源
,用于存放网站的静态资源文件,如CSS
、JavaScript
和网站界面图片
。**
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
-
账号: admin
,emai:[email protected]
,密码: **************
- 启动服务器
python manage.py runserver 0.0.0.0:8000
- **浏览器: **
localhost:8000
- 浏览器:
localhost:8000/admin
- 登录成功跳转的页面
二、商城网址的规划(原文网址)
2.1 设置路由分发规则
- 一个完整的路由包含:
路由地址
、视图函数(或者视图类)
、路由变量
和路由命名
。 - settings.py 中有这样得到一条语句:
ROOT_URLCONF = 'config.urls'
是定义项目所有路由地址的总入口
- 为方便管理,分别为每个项目应用(App)定义一条路由入口
- 分别在项目应用
index
、shopper
、commodity
新建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..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代码
{% load static %}
{{title}}
{% block footer %}{% endblock footer %}
4.2 首页模板设计
模板文件index.html的代码
{% extends 'base.html' %}
{% load static %}
{% block content %}
{% for c in commodityInfos %}
{% if forloop.counter < 5 %}
{% endif %}
{% endfor %}
{% endblock content %}
{% block footer %}
{% 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.*)', 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 %}
所有分类
{% for f in firsts %}
- {{ f.firsts }}
{% for t in typesList %}
{% if t.firsts == f.firsts %}
- {{ t.seconds }}
{% endif %}
{% endfor %}
{% endfor %}
{% if pages.has_previous %}
上一页
{% endif %}
{% for page in pages.paginator.page_range %}
{% if pages.number == page %}
{{ page }}
{% elif pages.number|add:'-1' == page or pages.number|add:'1' == page %}
{{ page }}
{% endif %}
{% endfor %}
{% if pages.has_next %}
下一页
{% endif %}
{% 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 %}
{{ commoditys.name }}
{% if likes %}
收藏
{% else %}
收藏
{% endif %}
参考价 ¥{{ commoditys.price|floatformat:'2' }}
活动价¥{{ commoditys.discount|floatformat:'2' }}
送 至广东 广州 天河区
规 格 {{ commoditys.sezes }}
数 量
-
+
{% 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 %}
{% endblock content %}
{% block footer %}
{% 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}("#username").val())),其中/.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 %}
{% if pages.has_previous %}
上一页
{% endif %}
{% for page in pages.paginator.page_range %}
{% if pages.number == page %}
{{ page }}
{% elif pages.number|add:'-1' == page or pages.number|add:'1' == page %}
{{ page }}
{% endif %}
{% endfor %}
{% if pages.has_next %}
下一页
{% endif %}
{% endblock content %}
{% block script %}
layui.config({
base: '{% static 'js/' %}'
}).use(['mm','laypage'],function(){
var mm = layui.mm,laypage = layui.laypage;
});
{% endblock script %}