当前位置: 首页 > news >正文

Django异步请求和后台管理实战

项目概述

  1. 项目实现Ajax异步请求局部刷新
  2. 使用XAdmin后台模板
  3. 提供图片上传接口
  4. 在明细页应用了富文本编辑器
  5. 在加载图书信息的时候使用LazyLoad(图片懒加载)
# 环境
asgiref==3.7.2
crispy-bootstrap3==2024.1
defusedxml==0.7.1
diff-match-patch==20230430
Django==3.2.25
django-ckeditor==5.9.0
django-crispy-forms==2.0
django-formtools==2.1
django-import-export==3.2.0
django-js-asset==2.0.0
django-reversion==5.0.12
django-stdimage==3.2.0
et-xmlfile==1.1.0
future==0.15.2
httplib2==0.9.2
MarkupPy==1.14
odfpy==1.4.1
openpyxl==3.1.3
Pillow==6.2.0
progressbar2==4.2.0
PyMySQL==1.1.1
python-utils==3.5.2
pytz==2024.1
PyYAML==6.0.1
six==1.10.0
sqlparse==0.4.4
tablib==3.4.0
typing_extensions==4.7.1
xlrd==2.0.1
xlwt==1.3.0

 

项目初始化并添加xadmin

  • 创建项目 python manage.py startapp bookshop,并在配置文件中完成注册
  • 根目录新建apps文件夹存放刚创建出来的项目
  • 根目录新建extra_apps文件夹存放xadmin源码
# 添加修改settings.py import os,syssys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
sys.path.insert(0, os.path.join(BASE_DIR, 'extra_apps'))INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','bookshop','xadmin','crispy_forms','crispy_bootstrap3',
]
  • 配置文件中设置项目根目录并将文件夹设置为Sources Root格式

  • 新建数据库连接并完成连接配置

# settings.py 添加mysql连接信息DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'django_bookshops','USER': '用户名','PASSWORD': '密码','HOST': 'ip','PORT': '端口'}
}# 根目录下与settings.py同文件夹下__init__.py 添加如下信息
import pymysql
pymysql.install_as_MySQLdb()
  • 前端文件添加

apps文件夹下新建Python Package文件命名static,在static文件夹下新建css、images、js文件夹

# settings.py 下添加访问静态文件路径STATIC_URL = '/static/'STATICFILES_DIRS = [os.path.join(BASE_DIR,'apps','static')
]

将准备好的前端文件放在指定文件夹中,目录结构如下

# 修改 HTML 文件
# 顶部添加
{% load static %}
# 引入静态文件
<link href="{%  static 'css/book.css' %}" rel="stylesheet">
  • url路由配置

apps文件夹下的bookshop项目中新建urls.py 文件,并在根目录下与settings.py文件同目录下的urls.py文件中添加引入外部 url配置

# settings.py 同目录下 urls.py配置from django.contrib import admin
from django.urls import path
# 调用其他app下定义的 url
from django.urls import includeurlpatterns = [path('admin/', admin.site.urls),path('home/', include('bookshop.urls'))
]# ==============apps目录下urls.py================from django.urls import path
from bookshop import views as bookshop_viewsurlpatterns = [# =========前端 url请求:返回页面============path('', bookshop_views.book, name='book'),
]
  • 准备数据库models类并插入数据
# models.py 
from django.db import models
# 引入stdimage的模块
from stdimage.models import StdImageField  # 字段类型
from stdimage.utils import UploadToUUID   # 生成文件名
from ckeditor_uploader.fields import RichTextUploadingField# Create your models here.class BookType(models.Model):  # 图书类别 -- 计算机,法律,外语,。。。。name = models.CharField(max_length=100, null=False, verbose_name='名称')  # 类别编码class Meta:verbose_name = "图书类别"verbose_name_plural = "图书类别"def __str__(self):return "%s" % self.nameclass Press(models.Model):  # 出版社id = models.CharField(primary_key=True, max_length=100, null=False, verbose_name='编号')  # 编号name = models.CharField(max_length=100, null=False, verbose_name='名称')  # 名称city = models.CharField(max_length=100,  null=False, verbose_name='城市')  # 城市tel = models.CharField(max_length=100, null=False, verbose_name='电话')  # 电话email = models.CharField(max_length=100,  null=False, verbose_name='邮箱')  # 邮箱address = models.CharField(max_length=100,  null=False, verbose_name='地址')  # 地址class Meta:verbose_name = "出版社"verbose_name_plural = "出版社"def __str__(self):return ("出版社编号:%s\t出版社名称:%s\n" % (self.id,self.name))# return "%s" % self.nameclass Author(models.Model):  # 作者gender_choices = (("男", '男'), ('女', '女'))id = models.CharField(primary_key=True, max_length=100,  null=False, verbose_name='编号')  # 编号name = models.CharField(max_length=100,  null=False, verbose_name='姓名')  # 姓名gender = models.CharField(max_length=100, choices=gender_choices, verbose_name='性别')    # 性别city = models.CharField(max_length=100, null=False, verbose_name='城市')  # 城市tel = models.CharField(max_length=100,  null=False, verbose_name='电话')  # 电话email = models.CharField(max_length=100,  null=False, verbose_name='邮箱')  # 邮箱address = models.CharField(max_length=100,  null=False, verbose_name='地址')  # 地址class Meta:verbose_name = "作者"verbose_name_plural = "作者"def __str__(self):return "%s\n" % self.nameclass Book(models.Model):   # 图书id = models.CharField(primary_key=True, max_length=100, null=False, verbose_name='编号')  # 编号,主键name = models.CharField(max_length=100,  null=False, verbose_name='名称')  # 名称barcode = models.CharField(max_length=100,  null=False, verbose_name='条形码')  # 条形码type = models.ForeignKey("BookType", on_delete=models.CASCADE, verbose_name='类别')  # 类别press = models.ForeignKey("Press", on_delete=models.CASCADE, verbose_name='出版社')  # 出版社author = models.ForeignKey("Author", on_delete=models.CASCADE, verbose_name='作者')  # 作者price = models.FloatField(null=False, verbose_name='单价')  # 价格difficulty = models.CharField(max_length=100,null=False, verbose_name='难易程度')  # 难易程度: 0--入门  1--中级  2--高级publish_date = models.DateField(null=False, verbose_name='发行时间')  # 发行时间storage_in_num = models.IntegerField(null=False, verbose_name='入库量')  # 入库量storage_in_date = models.DateField(null=False, verbose_name='入库时间') # 入库时间inventory_num = models.IntegerField(null=False, verbose_name='库存量')  # 库存量class Meta:verbose_name = "图书"verbose_name_plural = "图书"def __str__(self):return "%s" % self.name
# 同步数据库信息
python manage.py makemigrations
python manage.py migrate

Ajax 异步请求 

Ajax 即“Asynchronous JavaScript And XML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容

  • 准备图书类别的JSON数据 
# ************定义views视图********
from django.shortcuts import render
from django.http import HttpResponse,JsonResponse
from apps.bookshop.models import *# Create your views here.
# =============前端代码:返回页面===========
def book(request):return render(request,'book.html')# =============后端代码:从数据库中提取数据封装成json返回给前端===========# 获取所有的图书类别
def get_booktype(request):obj_booktypes = BookType.objects.all().values()# Queryset无法直接转换成 Json,只有python基础的数据类型才可以,最外层的集合转换成列表booktypes = list(obj_booktypes)# 插入全部选项booktypes.insert(0,{"id":0,"name":"全部"})# 返回前端 json格式return JsonResponse({'type':booktypes})# **********定义url路由************from django.urls import path
from bookshop import views as bookshop_viewsurlpatterns = [# =========前端 url请求:返回页面============path('', bookshop_views.book, name='book'),# =========后端 url请求:返回json数据========path('booktype/get/', bookshop_views.get_booktype, name='booktype'),]

  • 将后端返回的数据展示在页面上
# book.html
{% load static %}
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>图书页面</title><!-- 新 Bootstrap 核心 CSS 文件 --><link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><!--index.css--><link href="{%  static 'css/book.css' %}" rel="stylesheet"><!-- jQuery文件。务必在bootstrap.min.js 之前引入 --><script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script><!-- 最新的 Bootstrap 核心 JavaScript 文件 --><script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script><!-- jQuery中的Transtion  --><script src="https://cdn.bootcss.com/jquery.transit/0.9.12/jquery.transit.min.js"></script><!--引入LazyLoad--><script src="{% static 'js/jquery.lazyload.min.js' %}"></script>
</head><body><div class="container-fiuled header"><div class="container"><img src="{% static 'images/book_logo.png' %}"></div></div><div class="container typefilter"><div class="row"><div class="col-lg-1">类别:</div><div class="col-lg-11 booktypes"><!-- Ajax动态加载 --></div></div></div><div class="container otherfilter"><form class="form-inline"><div class="col-lg-2 col-lg-offset-1"><div class="form-group-sm"><label>价格: &nbsp;&nbsp;</label><select class="form-control price"><option value="0" selected>所有</option><option value="1">0-20元</option><option value="2">20-30元</option><option value="3">30-40元</option><option value="4">40-50元</option><option value="5">50-60元</option><option value="6">60-70元</option><option value="7">70元以上</option></select></div></div><div class="col-lg-2"><div class="form-group-sm"><label>难易程度: &nbsp;&nbsp;</label><select class="form-control difficulty"><option value="0" selected>所有</option><option value="1">入门</option><option value="2">中级</option><option value="3">高级</option></select></div></div><div class="col-lg-2"><div class="form-group-sm"><span style="font-weight: bold">价格排序: </span><span class="glyphicon glyphicon-sort-by-attributes asc" style="margin-left: 10px;"></span><span class="glyphicon glyphicon-sort-by-attributes-alt desc" style="margin-left: 10px;"></span></div></div><div class="col-lg-5" style="text-align: right"><div class="form-group-sm"><input type="text" class="form-control querystr" placeholder="输入图书关键字" style="width:360px;"><button class="btn btn-primary btn-sm btnquery" type="button">查找</button></div></div></form></div><div class="container books" style="width:1220px;height: 400px;margin:20px auto;"><!-- Ajax动态加载 --></div>
</body>
</html>
#  JavaScript代码
<script>// 入口函数$(document).ready(function (){// 代码自动执行(页面加载时自动执行)$.ajax({url: 'booktype/get/',method: 'GET',success: function (res){console.log(res)// 调用函数addTypes(res.type)}});});// DOM 操作// 将图书类别加载到指定区域function addTypes(types){// 清空指定区域内容$('.typefilter .booktypes').html("");// 循环加载图书类别types.forEach(function (element,index,array){// 新建一个 spanlet oneType = $('<span>').html(element.name).attr('value',element.id).css('font-size',"14px").css('font-weight','normal').css('margin',"10px");;//添加一个Hover操作oneType.on('mouseenter',()=>{oneType.css('font-weight','bold');}).on('mouseleave',()=>{oneType.css('font-weight','normal');});// 判断是不是全部if (index===0){oneType.css({'color':'red'});}// 添加到页面类别中oneType.appendTo($('.typefilter .booktypes'));});}       </script>
  • 准备图书列表数据
# ************定义views视图********
from django.shortcuts import render
from django.http import HttpResponse,JsonResponse
from apps.bookshop.models import *# Create your views here.
# =============前端代码:返回页面===========
def book(request):return render(request,'book.html')# =============后端代码:从数据库中提取数据封装成json返回给前端===========# 获取所有的图书类别
def get_booktype(request):obj_booktypes = BookType.objects.all().values()# Queryset无法直接转换成 Json,只有python基础的数据类型才可以,最外层的集合转换成列表booktypes = list(obj_booktypes)# 插入全部选项booktypes.insert(0,{"id":0,"name":"全部"})# 返回前端 json格式return JsonResponse({'type':booktypes})# 获取所有的图书列表
def get_books(request):obj_books = Book.objects.all().values()# Queryset无法直接转换成 Json,只有python基础的数据类型才可以,最外层的集合转换成列表books = list(obj_books)# 遍历图书 获取类别名称for index,item in enumerate(books):# 根据类别编号获取图书类别的对象obj_type = BookType.objects.get(id=item['type_id'])# 附加typename到当前成员books[index]['typename'] = obj_type.name# 返回前端 json格式# return JsonResponse({'book': books})# 'ensure_ascii': False 返回的数据显示中文return JsonResponse({'book': books},json_dumps_params={'ensure_ascii': False})# **********定义url路由************from django.urls import path
from bookshop import views as bookshop_viewsurlpatterns = [# =========前端 url请求:返回页面============path('', bookshop_views.book, name='book'),# =========后端 url请求:返回json数据========path('booktype/get/', bookshop_views.get_booktype, name='booktype'),path('books/get/', bookshop_views.get_books, name='books'),]

  • 将后端返回的图书展示在页面上
#  JavaScript代码
<script>// 入口函数$(document).ready(function (){// 代码自动执行(页面加载时自动执行)$.ajax({url: 'booktype/get/',method: 'GET',success: function (res){console.log(res)// 调用函数addTypes(res.type)}});// *******自动加载图书列表(页面加载时自动执行)*******$.ajax({url: 'books/get/',method: 'GET',success: function (res){console.log(res)// 调用函数addBooks(res.book)}});});// DOM 操作// 将图书类别加载到指定区域function addTypes(types){// 清空指定区域内容$('.typefilter .booktypes').html("");// 循环加载图书类别types.forEach(function (element,index,array){// 新建一个 spanlet oneType = $('<span>').html(element.name).attr('value',element.id).css('font-size',"14px").css('font-weight','normal').css('margin',"10px");;//添加一个Hover操作oneType.on('mouseenter',()=>{oneType.css('font-weight','bold');}).on('mouseleave',()=>{oneType.css('font-weight','normal');});// 判断是不是全部if (index===0){oneType.css({'color':'red'});}// 添加到页面类别中oneType.appendTo($('.typefilter .booktypes'));});} //**********把图书列表加载到制定区域*********function addBooks(books) {//清空区域内容$('.books').html("");//遍历books.forEach(function (element, index, array) {//新建一个div-最外层的容器let outer = $('<div>').attr( "class", "col-lg-3");//新建一个div,设置类为thumbnaillet thumbnail = $('<div>').attr( "class", "thumbnail").css('overflow','hidden').appendTo(outer);//新建一个img标签,放置图片let img = $('<img>').attr('scr',baseURL+element.image) //******.appendTo(thumbnail)//设置图的hover过渡特效 --- 放大1.4倍img.on('mouseenter',function () {img.transition({ scale: 1.4 }, 300);}).on('mouseleave',function () {img.transition({ scale: 1 }, 300);});//新建一个div--放置价格let price = $('<div>').html("价格:" + element.price.toFixed(2) + "元").css('text-align', 'left').css('color','red').css('margin-top','20px').css('font-size','16px').css('line-height', '30px').appendTo(thumbnail);{#$("<span>").html("类别:" + element.typename).css('text-align', 'left').css('font-size','16px')#}{#   .css('line-height', '30px').appendTo(thumbnail);#}//新建一个div--放置图书名称let bookname =$('<div>').html(element.name).css('text-align', 'left').css('color','navy').css('font-size','16px').appendTo(thumbnail);//设置图书名称的 Hoverbookname.on('mouseenter',function () {bookname.css('font-weight','bold');}).on('mouseleave',function () {bookname.css('font-weight','normal');});//把当前容器放置在页面上outer.appendTo($('.books'))});}      </script>
  • 效果如下 

xadmin 部署及配置

  • 引入源码到项目中
# 安装xadmin的依赖包进入源码的目录
pip install -r requirements.txt
  • 安装核心的包
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','bookshop','xadmin','crispy_forms','crispy_bootstrap3',
]
  • 修改URL
from django.contrib import admin
from django.urls import path
# 调用其他app下定义的 url
from django.urls import include
# 引入 xadmin
import xadminurlpatterns = [# path('admin/', admin.site.urls),path('xadmin/', xadmin.site.urls),path('home/', include('bookshop.urls'))
]
  • 同步数据库
python manage.py makemigrations
python manage.py migrate
  • 设置管理员账号密码
python manage.py createsuperuser
  • 报错问题解决
解决报错 render() missing 1 required positional argument: 'form_style' 
1.utils.py 29行render_field()中添加form_style=None,
2.删除 detail.py 34、35行form_style解决报错 render() missing 1 required positional argument: context
1.utils.py  66行context变为context=context解决报错 OperationalError: (1271, "Illegal mix of collations for operation 'like'")
1.找到django包下面的\site-packages\django\db\backends\mysql\base.py文件,编辑
将icontains的值 'icontains': 'LIKE %s',改为'icontains': 'LIKE BINARY %s',解决报错 django.template.exceptions.TemplateDoesNotExist: bootstrap3/errors.html
1.安装 pip install crispy-bootstrap3 -i https://mirrors.aliyun.com/pypi/simple/
2.注册 'crispy_bootstrap3'
  • Models类注册到xadmin
# 1.后台界面中文显示,更改 settings.py# 语言
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-Hans'# 时区
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'# 2.bookshop目录下新建 adminx.py 文件from apps.bookshop import models
import xadmin# 定义相应对象的xadmin类
class PressAdmin(object):# 配置表格显示字段list_display = ['id','name','city','tel','email','address']# 分页list_per_page = 10# 将类注册到xadmin
xadmin.site.register(models.Press,PressAdmin)# 定义相应对象的xadmin类
class BookTypeAdmin(object):# 配置表格显示字段list_display = ['name']# 分页list_per_page = 10# 将类注册到xadmin
xadmin.site.register(models.BookType,BookTypeAdmin)# 定义相应对象的xadmin类
class AuthorAdmin(object):# 配置表格显示字段list_display = ['id','name','city','gender','tel','email','address']# 分页list_per_page = 10# 将类注册到xadmin
xadmin.site.register(models.Author,AuthorAdmin)# 定义相应对象的xadmin类
class BookAdmin(object):# 配置表格显示字段list_display = ['id','name','barcode','type','press','author','price','difficulty','publish_date','storage_in_num','storage_in_date','inventory_num']# 分页list_per_page = 10# 排序ordering = ['id']# 查看详情show_detail_fields = ['id']# 图标  https://v3.bootcss.com/components/# https://fontawesome.com/v4/icons/model_icon = "fa fa-signal"# 搜索字段search_fields = ['id','name','barcode']# 筛选字段list_filter = ['id','name','barcode','type','press','author','price']# 配置自动刷新的时间refresh_times = (3,5)# 将类注册到xadmin
xadmin.site.register(models.Book,BookAdmin)
  • xadmin中全局配置
from apps.bookshop import models
import xadmin# 定义相应对象的xadmin类
class PressAdmin(object):# 配置表格显示字段list_display = ['id','name','city','tel','email','address']# 分页list_per_page = 10# 将类注册到xadmin
xadmin.site.register(models.Press,PressAdmin)# 定义相应对象的xadmin类
class BookTypeAdmin(object):# 配置表格显示字段list_display = ['name']# 分页list_per_page = 10# 将类注册到xadmin
xadmin.site.register(models.BookType,BookTypeAdmin)# 定义相应对象的xadmin类
class AuthorAdmin(object):# 配置表格显示字段list_display = ['id','name','city','gender','tel','email','address']# 分页list_per_page = 10# 将类注册到xadmin
xadmin.site.register(models.Author,AuthorAdmin)# 定义相应对象的xadmin类
class BookAdmin(object):# 配置表格显示字段list_display = ['id','name','barcode','type','press','author','price','difficulty','publish_date','storage_in_num','storage_in_date','inventory_num']# 分页list_per_page = 10# 排序ordering = ['id']# 查看详情show_detail_fields = ['id']# 图标  https://v3.bootcss.com/components/# https://fontawesome.com/v4/icons/model_icon = "fa fa-signal"# 搜索字段search_fields = ['id','name','barcode']# 筛选字段list_filter = ['id','name','barcode','type','press','author','price']# 配置自动刷新的时间refresh_times = (3,5)# 将类注册到xadmin
xadmin.site.register(models.Book,BookAdmin)"""========修改全局基础配置=========="""
from xadmin import views
class BaseSetting(object):# 是否启用主题enable_themes = Trueuse_bootswatch = Truedef has_add_permission(self,request):return Falseclass GlobalSetting(object):site_title = '后台管理系统'site_footer = '版权所有 @lv'# 侧边栏收起menu_style = 'accordion'# *****将全局配置配置到xadmin中*****
xadmin.site.register(views.BaseAdminView,BaseSetting)
xadmin.site.register(views.CommAdminView,GlobalSetting)

图片上传功能实现

  • 准备存储图片的文件夹media
  • 设置MEDIA ROOT 和 MEDIA URL
# MEDIA ROOT:本地处理的路径,MEDIA URL: 互联网访问的路径
# settings.py 
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = ''
  • 配置URL
from django.urls import path
# 调用其他app下定义的 url
from django.urls import include
# 引入 xadmin
import xadmin
from django.conf import settings
# 匹配静态文件
from django.conf.urls.static import staticurlpatterns = [# path('admin/', admin.site.urls),path('xadmin/', xadmin.site.urls),path('home/', include('bookshop.urls'))
]# 允许所有的图片被访问
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  • 安装模块
pip install django-stdimage==3.2.0 -i https://mirrors.aliyun.com/pypi/simple/
  • 配置models.py模版文件
# 引入stdimage的模块
from stdimage.models import StdImageField
# 生成唯一文件名
from stdimage.utils import UploadToUUID
# 添加图书字段
# path="" 默认存储到media下 variations缩略图配置
image = StdImageField(max_length=200, upload_to=UploadToUUID(path=""),
verbose_name='图片', variations={'thumbnail': {'width': 70, 'height': 100}})
  •  更新连接表
python manage.py makemigrations
  • 实现上传的图片在前台显示
#  JavaScript代码
<script>// 入口函数$(document).ready(function (){// 代码自动执行(页面加载时自动执行)$.ajax({url: 'booktype/get/',method: 'GET',success: function (res){console.log(res)// 调用函数addTypes(res.type)}});// *******自动加载图书列表(页面加载时自动执行)*******$.ajax({url: 'books/get/',method: 'GET',success: function (res){console.log(res)// 调用函数addBooks(res.book)}});});// DOM 操作// 将图书类别加载到指定区域function addTypes(types){// 清空指定区域内容$('.typefilter .booktypes').html("");// 循环加载图书类别types.forEach(function (element,index,array){// 新建一个 spanlet oneType = $('<span>').html(element.name).attr('value',element.id).css('font-size',"14px").css('font-weight','normal').css('margin',"10px");;//添加一个Hover操作oneType.on('mouseenter',()=>{oneType.css('font-weight','bold');}).on('mouseleave',()=>{oneType.css('font-weight','normal');});// 判断是不是全部if (index===0){oneType.css({'color':'red'});}// 添加到页面类别中oneType.appendTo($('.typefilter .booktypes'));});} //**********把图书列表加载到制定区域*********function addBooks(books) {//清空区域内容$('.books').html("");//*********定义一个基础的URL********let baseURL = "http://127.0.0.1:8000/";//遍历books.forEach(function (element, index, array) {//新建一个div-最外层的容器let outer = $('<div>').attr( "class", "col-lg-3");//新建一个div,设置类为thumbnaillet thumbnail = $('<div>').attr( "class", "thumbnail").css('overflow','hidden').appendTo(outer);//新建一个img标签,放置图片let img = $('<img>').attr('scr',baseURL+element.image) //******.appendTo(thumbnail)//设置图的hover过渡特效 --- 放大1.4倍img.on('mouseenter',function () {img.transition({ scale: 1.4 }, 300);}).on('mouseleave',function () {img.transition({ scale: 1 }, 300);});//新建一个div--放置价格let price = $('<div>').html("价格:" + element.price.toFixed(2) + "元").css('text-align', 'left').css('color','red').css('margin-top','20px').css('font-size','16px').css('line-height', '30px').appendTo(thumbnail);{#$("<span>").html("类别:" + element.typename).css('text-align', 'left').css('font-size','16px')#}{#   .css('line-height', '30px').appendTo(thumbnail);#}//新建一个div--放置图书名称let bookname =$('<div>').html(element.name).css('text-align', 'left').css('color','navy').css('font-size','16px').appendTo(thumbnail);//设置图书名称的 Hoverbookname.on('mouseenter',function () {bookname.css('font-weight','bold');}).on('mouseleave',function () {bookname.css('font-weight','normal');});//把当前容器放置在页面上outer.appendTo($('.books'))});}      </script>

 

图书筛选功能实现

  • 图书类别筛选
#  前端JavaScript代码
<script>// *******queryItems = {type: 0,  //类别pricemin: 0, //价格区间的最小值pricemax: 10000,//价格区间的最大值difficulty: "0", //难易程度order: 0,  //排序: 0-不排序  1-升序  2-降序inputstr: "", //输入的查询字符串};// 入口函数$(document).ready(function (){// 代码自动执行(页面加载时自动执行)$.ajax({url: 'booktype/get/',method: 'GET',success: function (res){console.log(res)// 调用函数展示筛选后的图书addTypes(res.type)}});// *******自动加载图书列表(页面加载时自动执行)*******$.ajax({url: 'books/get/',method: 'GET',success: function (res){console.log(res)// 调用函数addBooks(res.book)}});});// DOM 操作// 提交图书查询的ajax请求function queryBooks(){// 进行ajax请求$.ajax({url: "books/query/",method: "post",data: queryItems,success:function (res){console.log(res);// 展示筛选后的图书addBooks(res.book)}})}// 将图书类别加载到指定区域function addTypes(types){// 清空指定区域内容$('.typefilter .booktypes').html("");// 循环加载图书类别types.forEach(function (element,index,array){// 新建一个 spanlet oneType = $('<span>').html(element.name).attr('value',element.id).css('font-size',"14px").css('font-weight','normal').css('margin',"10px");;//添加一个Hover操作oneType.on('mouseenter',()=>{oneType.css('font-weight','bold');}).on('mouseleave',()=>{oneType.css('font-weight','normal');});// 判断是不是全部if (index===0){oneType.css({'color':'red'});}// *****响应事件*****// 类别点击事件oneType.click(function (){//清楚所有的选择$(".booktypes span").css('color','#000');//把点击的选择$(this).css('color','red');// 把typeid传递给 queryitme.typequeryItems.type = $(this).attr('value');{#console.log(queryItems);#}//提交到后台筛选queryBooks()// 添加到页面类别中oneType.appendTo($('.typefilter .booktypes'));});} //**********把图书列表加载到制定区域*********function addBooks(books) {//清空区域内容$('.books').html("");//*********定义一个基础的URL********let baseURL = "http://127.0.0.1:8000/";//遍历books.forEach(function (element, index, array) {//新建一个div-最外层的容器let outer = $('<div>').attr( "class", "col-lg-3");//新建一个div,设置类为thumbnaillet thumbnail = $('<div>').attr( "class", "thumbnail").css('overflow','hidden').appendTo(outer);//新建一个img标签,放置图片let img = $('<img>').attr('scr',baseURL+element.image) //******.appendTo(thumbnail)//设置图的hover过渡特效 --- 放大1.4倍img.on('mouseenter',function () {img.transition({ scale: 1.4 }, 300);}).on('mouseleave',function () {img.transition({ scale: 1 }, 300);});//新建一个div--放置价格let price = $('<div>').html("价格:" + element.price.toFixed(2) + "元").css('text-align', 'left').css('color','red').css('margin-top','20px').css('font-size','16px').css('line-height', '30px').appendTo(thumbnail);{#$("<span>").html("类别:" + element.typename).css('text-align', 'left').css('font-size','16px')#}{#   .css('line-height', '30px').appendTo(thumbnail);#}//新建一个div--放置图书名称let bookname =$('<div>').html(element.name).css('text-align', 'left').css('color','navy').css('font-size','16px').appendTo(thumbnail);//设置图书名称的 Hoverbookname.on('mouseenter',function () {bookname.css('font-weight','bold');}).on('mouseleave',function () {bookname.css('font-weight','normal');});//把当前容器放置在页面上outer.appendTo($('.books'))});}      </script>
# 后端代码 view.py
from django.shortcuts import render
from django.http import HttpResponse,JsonResponse
from apps.bookshop.models import *# 实现图书查询
def query_books(request):# 获取传递来的条件type = request.POST.get('type')if type == '0':obj_books = Book.objects.all().values()else:# 通过编号筛选出图书obj_books = Book.objects.filter(type_id=type).values()books = list(obj_books.values())# 遍历图书 获取类别名称for index, item in enumerate(books):# 根据类别编号获取图书类别的对象obj_type = BookType.objects.get(id=item['type_id'])# 附加typename到当前成员books[index]['typename'] = obj_type.namereturn JsonResponse({'book': books}, json_dumps_params={'ensure_ascii': False})# 设置路由 urls.py
from django.urls import path
from bookshop import views as bookshop_viewsurlpatterns = [# =========前端 url请求:返回页面============path('', bookshop_views.book, name='book'),# =========后端 url请求:返回json数据========path('booktype/get/', bookshop_views.get_booktype, name='booktype'),path('books/get/', bookshop_views.get_books, name='books'),path('books/query/', bookshop_views.query_books, name='books_query'),
]
  • 图书价格区间筛选
#  前端JavaScript代码
<script>// *******PRICE_REGION_VALUE = [[0,10000],[0,20],[20,30],[30,40],[40,50],[50,60],[60,70],[70,10000]];DIFF_VALUE =['0', '入门', '中级', '高级'];queryItems = {type: 0,  //类别pricemin: 0, //价格区间的最小值pricemax: 10000,//价格区间的最大值difficulty: "0", //难易程度order: 0,  //排序: 0-不排序  1-升序  2-降序inputstr: "", //输入的查询字符串};// 入口函数$(document).ready(function (){// 代码自动执行(页面加载时自动执行)$.ajax({url: 'booktype/get/',method: 'GET',success: function (res){console.log(res)// 调用函数展示筛选后的图书addTypes(res.type)}});// *******自动加载图书列表(页面加载时自动执行)*******$.ajax({url: 'books/get/',method: 'GET',success: function (res){console.log(res)// 调用函数addBooks(res.book)}});// *******价格的下拉框改变事件*******$('.price').change(function (){let priceArr = PRICE_REGION_VALUE[parseInt($('.price').val())];// 范围值赋值给pricemin、pricemaxqueryItems.pricemin = priceArr[0];queryItems.pricemax = priceArr[1];// 传递到后端queryBooks()});});// DOM 操作// 提交图书查询的ajax请求function queryBooks(){// 进行ajax请求$.ajax({url: "books/query/",method: "post",data: queryItems,success:function (res){console.log(res);// 展示筛选后的图书addBooks(res.book)}})}// 将图书类别加载到指定区域function addTypes(types){// 清空指定区域内容$('.typefilter .booktypes').html("");// 循环加载图书类别types.forEach(function (element,index,array){// 新建一个 spanlet oneType = $('<span>').html(element.name).attr('value',element.id).css('font-size',"14px").css('font-weight','normal').css('margin',"10px");;//添加一个Hover操作oneType.on('mouseenter',()=>{oneType.css('font-weight','bold');}).on('mouseleave',()=>{oneType.css('font-weight','normal');});// 判断是不是全部if (index===0){oneType.css({'color':'red'});}// *****响应事件*****// 类别点击事件oneType.click(function (){//清楚所有的选择$(".booktypes span").css('color','#000');//把点击的选择$(this).css('color','red');// 把typeid传递给 queryitme.typequeryItems.type = $(this).attr('value');{#console.log(queryItems);#}//提交到后台筛选queryBooks()// 添加到页面类别中oneType.appendTo($('.typefilter .booktypes'));});} //**********把图书列表加载到制定区域*********function addBooks(books) {//清空区域内容$('.books').html("");//*********定义一个基础的URL********let baseURL = "http://127.0.0.1:8000/";//遍历books.forEach(function (element, index, array) {//新建一个div-最外层的容器let outer = $('<div>').attr( "class", "col-lg-3");//新建一个div,设置类为thumbnaillet thumbnail = $('<div>').attr( "class", "thumbnail").css('overflow','hidden').appendTo(outer);//新建一个img标签,放置图片let img = $('<img>').attr('scr',baseURL+element.image) //******.appendTo(thumbnail)//设置图的hover过渡特效 --- 放大1.4倍img.on('mouseenter',function () {img.transition({ scale: 1.4 }, 300);}).on('mouseleave',function () {img.transition({ scale: 1 }, 300);});//新建一个div--放置价格let price = $('<div>').html("价格:" + element.price.toFixed(2) + "元").css('text-align', 'left').css('color','red').css('margin-top','20px').css('font-size','16px').css('line-height', '30px').appendTo(thumbnail);{#$("<span>").html("类别:" + element.typename).css('text-align', 'left').css('font-size','16px')#}{#   .css('line-height', '30px').appendTo(thumbnail);#}//新建一个div--放置图书名称let bookname =$('<div>').html(element.name).css('text-align', 'left').css('color','navy').css('font-size','16px').appendTo(thumbnail);//设置图书名称的 Hoverbookname.on('mouseenter',function () {bookname.css('font-weight','bold');}).on('mouseleave',function () {bookname.css('font-weight','normal');});//把当前容器放置在页面上outer.appendTo($('.books'))});}      </script>
# 后端代码 view.py
from django.shortcuts import render
from django.http import HttpResponse,JsonResponse
from apps.bookshop.models import *# 实现图书查询
def query_books(request):# 获取传递来的条件type = request.POST.get('type')# 价格price_min = request.POST.get('pricemin')price_max = request.POST.get('pricemax')if type == '0':obj_books = Book.objects.all().values()else:# 通过编号筛选出图书obj_books = Book.objects.filter(type_id=type).values()# 筛选价格obj_books = obj_books.filter(price__gte=price_min,price__lte=price_max)books = list(obj_books.values())# 遍历图书 获取类别名称for index, item in enumerate(books):# 根据类别编号获取图书类别的对象obj_type = BookType.objects.get(id=item['type_id'])# 附加typename到当前成员books[index]['typename'] = obj_type.namereturn JsonResponse({'book': books}, json_dumps_params={'ensure_ascii': False})# 设置路由 urls.py
from django.urls import path
from bookshop import views as bookshop_viewsurlpatterns = [# =========前端 url请求:返回页面============path('', bookshop_views.book, name='book'),# =========后端 url请求:返回json数据========path('booktype/get/', bookshop_views.get_booktype, name='booktype'),path('books/get/', bookshop_views.get_books, name='books'),path('books/query/', bookshop_views.query_books, name='books_query'),
]

 

  • 图书难易程度筛选
# 前端 JavaScript代码
//  入口函数添加 难度筛选事件$('.difficulty').change(function (){queryItems.difficulty = DIFF_VALUE[parseInt($('.difficulty').val())]// 实现查询queryBooks();
});# 后端图书查询函数添加
# 实现图书查询
def query_books(request):# 获取传递来的条件type = request.POST.get('type')# 价格price_min = request.POST.get('pricemin')price_max = request.POST.get('pricemax')# 难度difficulty = request.POST.get('difficulty')if type == '0':obj_books = Book.objects.all().values()else:# 通过编号筛选出图书obj_books = Book.objects.filter(type_id=type).values()# 筛选价格obj_books = obj_books.filter(price__gte=price_min,price__lte=price_max)# 筛选难度if '0' not in difficulty:obj_books = obj_books.filter(difficulty=difficulty)books = list(obj_books.values())# 遍历图书 获取类别名称for index, item in enumerate(books):# 根据类别编号获取图书类别的对象obj_type = BookType.objects.get(id=item['type_id'])# 附加typename到当前成员books[index]['typename'] = obj_type.namereturn JsonResponse({'book': books}, json_dumps_params={'ensure_ascii': False})
  • 图书关键字筛选
# 前端 JavaScript代码
//  入口函数添加 难度筛选事件
// 指定关键字查询
$('.btnquery').click(function (){// 获取输入字符串queryItems.inputstr = $('.querystr').val();// 实现查询queryBooks();
});# 后端图书查询函数添加
# 实现图书查询
def query_books(request):# 获取传递来的条件type = request.POST.get('type')# 价格price_min = request.POST.get('pricemin')price_max = request.POST.get('pricemax')# 难度difficulty = request.POST.get('difficulty')# 输入的值input_str = request.POST.get('inputstr')if type == '0':obj_books = Book.objects.all().values()else:# 通过编号筛选出图书obj_books = Book.objects.filter(type_id=type).values()# 筛选价格obj_books = obj_books.filter(price__gte=price_min,price__lte=price_max)# 筛选难度if '0' not in difficulty:obj_books = obj_books.filter(difficulty=difficulty)# 输入筛选obj_books = obj_books.filter(name__icontains=input_str)books = list(obj_books.values())# 遍历图书 获取类别名称for index, item in enumerate(books):# 根据类别编号获取图书类别的对象obj_type = BookType.objects.get(id=item['type_id'])# 附加typename到当前成员books[index]['typename'] = obj_type.namereturn JsonResponse({'book': books}, json_dumps_params={'ensure_ascii': False})
  • 图书价格升降序排序
# 前端 JavaScript代码
//  入口函数添加 难度筛选事件
// 指定关键字查询
// 升序$('.asc').click(function (){// 把所有排序的图标不选择$('.asc').css('color','#fff');$('.desc').css('color','#fff');// 选中升序$('.asc').css('color','red');// 修改orderqueryItems.order = 1;// 提交给后端queryBooks();});// 降序$('.desc').click(function (){// 把所有排序的图标不选择$('.asc').css('color','#fff');$('.desc').css('color','#fff');// 选中升序$('.desc').css('color','red');// 修改orderqueryItems.order = 2;// 提交给后端queryBooks();});# 后端图书查询函数添加
# 实现图书查询
def query_books(request):# 获取传递来的条件type = request.POST.get('type')# 价格price_min = request.POST.get('pricemin')price_max = request.POST.get('pricemax')# 难度difficulty = request.POST.get('difficulty')# 输入的值input_str = request.POST.get('inputstr')# 升降序order = request.POST.get('order')if type == '0':obj_books = Book.objects.all().values()else:# 通过编号筛选出图书obj_books = Book.objects.filter(type_id=type).values()# 筛选价格obj_books = obj_books.filter(price__gte=price_min,price__lte=price_max)# 筛选难度if '0' not in difficulty:obj_books = obj_books.filter(difficulty=difficulty)# 输入筛选obj_books = obj_books.filter(name__icontains=input_str)# 排序if order == '1':obj_books = obj_books.order_by('price').all()elif order == '2':obj_books = obj_books.order_by('-price').all()books = list(obj_books.values())# 遍历图书 获取类别名称for index, item in enumerate(books):# 根据类别编号获取图书类别的对象obj_type = BookType.objects.get(id=item['type_id'])# 附加typename到当前成员books[index]['typename'] = obj_type.namereturn JsonResponse({'book': books}, json_dumps_params={'ensure_ascii': False})

图片懒加载

为了提高用户体验度,在长页面的时候,优先加载用户屏幕区域的内容,下拉的内容暂时不加载,等滚动条拖动的时候逐步加载。

<!--引入图片懒加载js包LazyLoad-->
<script src="{% static 'js/jquery.lazyload.min.js' %}"></script>
// 修改addBooks
function addBooks(books) {//清空区域内容$('.books').html("");//定义一个基础的URLlet baseURL = "http://127.0.0.1:8000/";//遍历books.forEach(function (element, index, array) {//新建一个div-最外层的容器let outer = $('<div>').attr( "class", "col-lg-3");//新建一个div,设置类为thumbnaillet thumbnail = $('<div>').attr( "class", "thumbnail").css('overflow','hidden').appendTo(outer);//******新建一个img标签,放置图片****let img = $('<img>').attr('class','lazy').attr('width','200px').attr('height','200px').attr('src', "{% static 'images/lazy.png' %}").attr('data-original',baseURL+element.image).appendTo(thumbnail)//img.lazyload();//设置图的hover过渡特效 --- 放大1.4倍img.on('mouseenter',function () {img.transition({ scale: 1.4 }, 300);}).on('mouseleave',function () {img.transition({ scale: 1 }, 300);});//新建一个div--放置价格let price = $('<div>').html("价格:" + element.price.toFixed(2) + "元").css('text-align', 'left').css('color','red').css('margin-top','20px').css('font-size','16px').css('line-height', '30px').appendTo(thumbnail);{#$("<span>").html("类别:" + element.typename).css('text-align', 'left').css('font-size','16px')#}{#   .css('line-height', '30px').appendTo(thumbnail);#}//新建一个div--放置图书名称let bookname =$('<div>').html(element.name).css('text-align', 'left').css('color','navy').css('font-size','16px').appendTo(thumbnail);//设置图书名称的 Hoverbookname.on('mouseenter',function () {bookname.css('font-weight','bold');}).on('mouseleave',function () {bookname.css('font-weight','normal');});//展示类别let typeAndDiff = $("<div>");$("<span>").html("类别:" + element.typename).css('text-align', 'left').css('font-size','16px').css('line-height', '30px').appendTo(typeAndDiff);$("<span>").html("&nbsp&nbsp&nbsp&nbsp难易程度:" + element.difficulty).css('text-align', 'left').css('font-size','16px').css('line-height', '30px').appendTo(typeAndDiff);typeAndDiff.appendTo(thumbnail);//点击某一本触发的事件outer.click(function (){{#location.href = "/book/detail/" + element.id + "/";#}{#location.href = "/home/book/detail/?bookid=" + element.id;#}let temp = window.open('_blank');temp.location.href = "/home/book/detail/?bookid=" + element.id;});//把当前容器放置在页面上outer.appendTo($('.books'))});//设置图片懒加载$('img.lazy').lazyload({threshold: 200,effect: 'fadeIn'});}

添加图片详情功能

在图书列表页面点击某一本图书获取这本图书的图书编号,在跳转的时候,携带图书编号跳转到详情页,在详情页中通过携带过来的图书编号在数据库査询更多的信息在页面展示。

# 详情页 html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>图书详情</title><!-- 新 Bootstrap 核心 CSS 文件 --><link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><link type="text/css" rel="stylesheet" href="{% static 'css/bookdetail.base.css' %}"><!-- jQuery文件。务必在bootstrap.min.js 之前引入 --><script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script><!-- 最新的 Bootstrap 核心 JavaScript 文件 --><script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body><div class="container-fiuled header"><div class="container"><img src="{% static 'images/book_logo.png' %}"></div></div><div class="container title">图书详情</div><div class="container basic"><div class="left" ></div><div class="right" ></div></div><div class="container detail" style="padding: 25px; box-sizing: border-box"></div></body>
</html>
# 设置 views.py视图
def book_detail(request,bookid):print(bookid)"""图书详情页"""return render(request, 'bookdetail.html',context={'bookid':bookid})# 获取图书详情
def get_book_by_id(request):# 接收传递过来的 idbookid = request.POST.get('bookid')# bookid = '39001'# 获取book表信息obj_book = Book.objects.filter(id=bookid).values()[0]# 通过type_id 获取列表的对象obj_booktype = BookType.objects.get(id=obj_book['type_id'])# 通过Press_id 获取出版社的对象obj_press = Press.objects.get(id=obj_book['press_id'])# 通过author_id 获取作者的对象obj_author = Author.objects.get(id=obj_book['author_id'])# 组合拼接到obj_bookobj_book['typename'] = obj_booktype.nameobj_book['pressname'] = obj_press.nameobj_book['authorname'] = obj_author.name# 返回return JsonResponse(obj_book)# 定义前端访问路由
from django.urls import path
from bookshop import views as bookshop_viewsurlpatterns = [# =========前端 url请求:返回页面============path('', bookshop_views.book, name='book'),path('book/detail/<bookid>/', bookshop_views.book_detail, name='bookdetail'),#图书详情页# =========后端 url请求:返回json数据========path('booktype/get/', bookshop_views.get_booktype, name='booktype'),path('books/get/', bookshop_views.get_books, name='books'),path('books/query/', bookshop_views.query_books, name='books_query'),path('books/get_detail/', bookshop_views.get_book_by_id, name='books_getdetail'),#获取某一图书详情
]
// 前端 JavaScript
<script>// 入口函数$(document).ready(function (){// 入口函数代码自动执行$.ajax({url: '/home/books/get_detail/',method: 'post',data: {'bookid': {{ bookid }}},success: function (res){{#console.log(res)#}addBookDetail(res)}});// 展示图书信息function addBookDetail(book) {//清除图片区域let leftArea = $('.basic .left');leftArea.html("");//定义基础的URLBASE_URL = "http://127.0.0.1:8000/";//加载图书$('<img>').attr("width",'300px').attr('height',"300px").css('margin','50px').attr('src', BASE_URL + book.image).appendTo(leftArea);//右边区域let rightArea = $('.basic .right');rightArea.html("");//编号let arr = ["id", "name",'authorname','price','typename','difficulty','pressname','publish_date','inventory_num'];let arr_name = ['编号','名称','作者','价格','类别','等级','出版社','出版时间','库存量'];let obj_ul = $('<ul>').css('font-size','16px');//循环for(let index in arr){let temp_li = $("<li>");$("<span>").css('color','red').html(arr_name[index]+":").appendTo(temp_li);$("<span>").css('color','#000').html("&nbsp;&nbsp;&nbsp;"+ book[arr[index]]).appendTo(temp_li);temp_li.appendTo(obj_ul);}obj_ul.appendTo(rightArea);//把图书的详情展示在页面$('.detail').html(book.detail);}})
</script>

后台部署富文本编辑器

  • 安装模块
pip install django-ckeditor==5.9.0
pip install Pillow==6.2.0
  • 注册并添加配置
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','bookshop','xadmin','crispy_forms','crispy_bootstrap3','ckeditor','ckeditor_uploader'
]# ====添加CKEditor配置====
# 使用默认的主题名称
CKEDITOR_CONFIGS = {
'default':{'toolbar': 'full','height': 500,'width': 900,},}
# 上传文件存储在哪个目录 /media/uploads/
CKEDITOR_UPLOAD_PATH = "uploads/"
  • 配置url路由
from django.urls import path
# 调用其他app下定义的 url
from django.urls import include
# 引入 xadmin
import xadmin
from django.conf import settings
# 匹配静态文件
from django.conf.urls.static import staticurlpatterns = [# path('admin/', admin.site.urls),path('xadmin/', xadmin.site.urls),# 富文本编辑器path('ckeditor/',include('ckeditor_uploader.urls')),path('home/', include('bookshop.urls'))
]# 允许所有的图片被访问
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  • 模版models.py添加Book新字段
from ckeditor_uploader.fields import RichTextUploadingField
class Book(models.Model):   # 图书id = models.CharField(primary_key=True, max_length=100, null=False, verbose_name='编号')  # 编号,主键name = models.CharField(max_length=100,  null=False, verbose_name='名称')  # 名称barcode = models.CharField(max_length=100,  null=False, verbose_name='条形码')  # 条形码type = models.ForeignKey("BookType", on_delete=models.CASCADE, verbose_name='类别')  # 类别press = models.ForeignKey("Press", on_delete=models.CASCADE, verbose_name='出版社')  # 出版社author = models.ForeignKey("Author", on_delete=models.CASCADE, verbose_name='作者')  # 作者price = models.FloatField(null=False, verbose_name='单价')  # 价格difficulty = models.CharField(max_length=100,null=False, verbose_name='难易程度')  # 难易程度: 0--入门  1--中级  2--高级# path="" 默认存储到media下 variations缩略图配置image = StdImageField(max_length=200, upload_to=UploadToUUID(path=""),verbose_name='图片', variations={'thumbnail': {'width': 70, 'height': 100}})publish_date = models.DateField(null=False, verbose_name='发行时间')  # 发行时间storage_in_num = models.IntegerField(null=False, verbose_name='入库量')  # 入库量storage_in_date = models.DateField(null=False, verbose_name='入库时间') # 入库时间inventory_num = models.IntegerField(null=False, verbose_name='库存量')  # 库存量# 添加富文本编辑器字段detail = RichTextUploadingField(verbose_name="图书详情",default="")class Meta:verbose_name = "图书"verbose_name_plural = "图书"def __str__(self):return "%s" % self.name
  • 同步数据
python manage.py makemigrations
python manage.py migrate
  • 将富文本内容展示在前端页面
//把图书的详情展示在页面
$('.detail').html(book.detail);

 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 『哈哥赠书 - 55期』-『码农职场:IT人求职就业手册』
  • IT课程学习搭子
  • 【AD域】搭建AD域服务器
  • C++STL常用总结
  • 2024年7月30日~2024年8月5日周报
  • 技术速递|VS Code Java 7月更新 - Gradle 支持增强!用户体验改进与 Spring 新功能
  • 量化投资基础(四)之AR、MA、ARMA与ARIMA模型
  • NASA:气溶胶研究处 (ARB) 48 英寸激光雷达数据
  • 边缘计算在智能交通系统中的应用探究
  • qt下载安装
  • 在MINST图像分类数据集上训练一个简单的多层神经网络
  • springboot宠物在线领养系统-计算机毕业设计源码51181
  • CODESYS EtherCAT通讯状态监测
  • Java语言程序设计基础篇_编程练习题*16.7 (设置时钟的时间)
  • 【系统架构设计】数据库系统(五)
  • ES6指北【2】—— 箭头函数
  • 2017-09-12 前端日报
  • Centos6.8 使用rpm安装mysql5.7
  • Flex布局到底解决了什么问题
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • Theano - 导数
  • Vue--数据传输
  • 编写高质量JavaScript代码之并发
  • 订阅Forge Viewer所有的事件
  • 翻译:Hystrix - How To Use
  • 前端面试总结(at, md)
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 再次简单明了总结flex布局,一看就懂...
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • #### go map 底层结构 ####
  • (13)DroneCAN 适配器节点(一)
  • (4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (k8s)Kubernetes 从0到1容器编排之旅
  • (Oracle)SQL优化基础(三):看懂执行计划顺序
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (第27天)Oracle 数据泵转换分区表
  • (附源码)php投票系统 毕业设计 121500
  • (附源码)springboot教学评价 毕业设计 641310
  • (一)Linux+Windows下安装ffmpeg
  • (转)memcache、redis缓存
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .Net CoreRabbitMQ消息存储可靠机制
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .NetCore实践篇:分布式监控Zipkin持久化之殇
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • .NET开源纪元:穿越封闭的迷雾,拥抱开放的星辰
  • .NET下ASPX编程的几个小问题
  • /proc/stat文件详解(翻译)
  • @antv/x6 利用interacting方法来设置禁止结点移动的方法实现。
  • @modelattribute注解用postman测试怎么传参_接口测试之问题挖掘
  • [ vulhub漏洞复现篇 ] GhostScript 沙箱绕过(任意命令执行)漏洞CVE-2019-6116