Django框架完成读者浏览书籍,图书详情页,借阅管理

前情回顾:

使用Django框架实现简单的图书借阅系统——完成图书信息管理

文章目录

  • 1.完成展示图书信息功能
    • 1.1django 静态资源管理问题
    • 1.2编写图书展示模板HTML
  • 2.完成图书详情页功能
    • 2.1从后端获取图书详情信息
    • 2.2详情页面展示图书数据
  • 3.完成借阅管理功能
    • 3.1管理员管理借阅数据
      • 3.1.1完成用户功能,上传借阅信息
      • 3.1.2完成展示借阅数据功能
      • 3.1.3完成归还图书功能
      • 3.1.4完成删除借阅记录功能
  • 项目源码地址

1.完成展示图书信息功能

向读者展示所有图书数据,本质上和图书管理一样,向数据库查询图书数据,返回给前端,如果是前后端分离项目,可以合成一个API,但是这次项目没有用到前后端,所以要另外提供一个函数,返回给读者图书数据

def display_all_books(request):# 查询所有图书books = Book.objects.all()# 登录----- 展示-----HTMLuser_id = request.session.get('user_id')username = request.session.get('username')return render(request, 'reader/library_main.html', {'books': books, 'user_id': user_id, 'user_name': username})

与管理员稍有不同,展示读者页面的数据 ,不仅有图书数据,还有session,因为一会做借阅功能需要用户的ID,所以可以将用户的信息存放到session 传递给下一个页面

1.1django 静态资源管理问题

展示页面,用了 不少CSS和JS,如果说直接写在HTML页面,那显得太臃肿, 所以集成在外部css文件和JS文件,但是Django对这些静态资源支持的不是很好,如果我们想引用css文件不能直接引用,需要专门创建一个新的文件夹,并且告诉Django我们的静态资源在这个文件夹才可以,具体操作步骤如下:

  1. 首先在项目目录新建一个static文件夹
  2. 修改settings.py 告知Django static文件夹地址
STATIC_URL = '/static/'
# 静态文件的额外目录
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'),# 如果需要,添加更多目录
]
  1. 在HTML文件中,确保在需要使用静态文件的地方包含{% load static %}标签。
{% load static %}
<link rel="stylesheet" href="{% static 'myapp/css/style.css' %}">

1.2编写图书展示模板HTML

解决完静态资源文件问题,就可以编写HTML文件,这里用到了大量的css和js ,还用到了部分JQuery,在文章末尾会附上开源地址,大家可以去gitee下载源代码

{% load static %}
<!DOCTYPE html>
<html class="no-js" lang="zxx"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="meta description"><title></title><meta name="referrer" content="no-referrer"><!--=== Favicon ===-->{#    更改css 地址     {% static 'css/your_stylesheet.css' %}#}<link rel="shortcut icon" href="{% static 'assets/img/favicon.ico' %}" type="image/x-icon"/><!-- Google fonts include --><link href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,500,500i,700,900%7CYesteryear"rel="stylesheet"><!-- All Vendor & plugins CSS include --><link href="{% static 'assets/css/vendor.css' %}" rel="stylesheet"><!-- Main Style CSS {% static 'assets/css/style.css' %}  --><link href="{% static 'assets/css/style.css' %}" rel="stylesheet"></head><body><!-- Start Header Area -->
<header class="header-area"><!-- main menu area end --><!-- mini cart area start --><div class="col-lg-3"><div class="header-configure-wrapper"><div class="header-configure-area"><ul class="nav justify-content-end"><li class="user-hover"><a href="#"><i class="lnr lnr-user"></i></a><ul class="dropdown-list"><li>{% if user_name %}<a href="#" id="local_user_id">用户ID:{{ user_id}}</a><a href="#" id="user_name">当前用户:{{ user_name }}</a>{% else %}<a href="../login"  >请登录</a>{% endif %}</li></ul></li></ul></div></div></div><!-- mini cart area end --></header>
<!-- end Header Area --><!-- off-canvas menu start -->
<aside class="off-canvas-wrapper"><div class="off-canvas-overlay"></div><div class="off-canvas-inner-content"><div class="btn-close-off-canvas"><i class="lnr lnr-cross"></i></div></div></div>
</aside>
<!-- off-canvas menu end --><!-- main wrapper start -->
<main><!-- breadcrumb area start --><div class="breadcrumb-area common-bg"><div class="container"><div class="row"><div class="col-12"><div class="breadcrumb-wrap"><nav aria-label="breadcrumb"><h1 id="web_title">弄墨小轩</h1></nav></div></div></div></div></div><!-- breadcrumb area end --><!-- page main wrapper start --><div class="shop-main-wrapper section-space pb-0"><div class="container"><div class="row"><!-- shop main wrapper start --><div class="col-lg-9 order-1 order-lg-2"><div class="shop-product-wrapper"><!-- shop product top wrap start --><div class="shop-top-bar"><div class="row align-items-center"><div class="col-lg-7 col-md-6 order-2 order-md-1"><div class="top-bar-left"><div class="product-view-mode"><a class="active" href="#" data-target="grid-view" data-toggle="tooltip"title="Grid View"><i class="fa fa-th"></i></a><a href="#" data-target="list-view" data-toggle="tooltip" title="List View"><iclass="fa fa-list"></i></a></div></div></div></div></div><!-- shop product top wrap start --><!-- product item list wrapper start --><div class="shop-product-wrap grid-view row mbn-40" id="shopContainer">{% for book in books %}<!--      for 循环 解决--><div class="col-md-4 col-sm-6"><!-- product grid start --><div class="product-item"><figure class="product-thumb"><a href="/reader/{{ book.id }}/display"><img class="pri-img" src={{ book.image_link }} alt="product"></a></figure><div class="product-caption"><p class="product-name"><a href="/reader/{{ book.id }}/display">{{ book.title }}</a></p><div class="price-box"><span class="price-regular">{{ book.author }}</span></div></div></div><!-- product grid end --><!-- product list item begin --><div class="product-list-item"><figure class="product-thumb"><a href="/reader/{{ book.id }}/display"><img class="pri-img" src={{ book.image_link }}  alt="product"></a></figure><div class="product-content-list"><h5 class="product-name"><ahref="/reader/{{ book.id }}/display">{{ book.title }}</a></h5><div class="price-box"><span class="price-regular">{{ book.title }}</span></div><p> 简介:{{ book.details }} </p><div class="button-group-list"><a class="btn-big" href="/reader/{{ book.id }}/display"data-toggle="tooltip"title="Add to Cart"><i class="lnr lnr-cart"></i>添加到书架</a></div></div></div><!-- product list item end --></div>{% endfor %}</div><!-- product item list wrapper end --><!-- start pagination area --><div class="paginatoin-area text-center"><input type="hidden" id="currentPage"><input type="hidden" id="pageSize"><!--                            绑定按钮事件 --><ul class="pagination-box" id="paginate"></ul></div><!-- end pagination area --></div></div><!-- shop main wrapper end --></div></div></div><!-- page main wrapper end -->
</main>
<!-- main wrapper end --><!-- Start Footer Area Wrapper -->
<footer class="footer-wrapper"><!-- footer widget area start --><!-- footer widget area end --><!-- footer bottom area start --><div class="footer-bottom-area"><div class="container"><div class="row align-items-center"><div class="col-md-6 order-2 order-md-1"><div class="copyright-text"><p>Copyright &copy; 2019.Company name All rights reserved.<a target="_blank"href="http://sc.chinaz.com/moban/">&#x7F51;&#x9875;&#x6A21;&#x677F;</a></p></div></div><div class="col-md-6 order-1 order-md-2"><div class="footer-social-link"><a href="#"><i class="fa fa-twitter"></i></a><a href="#"><i class="fa fa-facebook"></i></a><a href="#"><i class="fa fa-linkedin"></i></a><a href="#"><i class="fa fa-instagram"></i></a></div></div></div></div></div><!-- footer bottom area end --></footer>
<!-- End Footer Area Wrapper --><!-- Quick view modal start -->
<div class="modal" id="quick_view"><div class="modal-dialog modal-lg modal-dialog-centered"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal">&times;</button></div><div class="modal-body"><!-- product details inner end --><div class="product-details-inner"><div class="row"><div class="col-lg-5 col-md-5"><div class="product-large-slider"><div class="pro-large-img"><img src="assets/img/product/product-details-img1.jpg" alt="product-details"/></div><div class="pro-large-img"><img src="assets/img/product/product-details-img2.jpg" alt="product-details"/></div><div class="pro-large-img"><img src="assets/img/product/product-details-img3.jpg" alt="product-details"/></div><div class="pro-large-img"><img src="assets/img/product/product-details-img4.jpg" alt="product-details"/></div></div><div class="pro-nav slick-row-10 slick-arrow-style"><div class="pro-nav-thumb"><img src="assets/img/product/product-details-img1.jpg" alt="product-details"/></div><div class="pro-nav-thumb"><img src="assets/img/product/product-details-img2.jpg" alt="product-details"/></div><div class="pro-nav-thumb"><img src="assets/img/product/product-details-img3.jpg" alt="product-details"/></div><div class="pro-nav-thumb"><img src="{% static 'assets/img/product/product-details-img4.jpg" alt="product-details' %}"/></div></div></div><div class="col-lg-7 col-md-7"><div class="product-details-des quick-details"><h3 class="product-name">Orchid flower white stick</h3><div class="ratings d-flex"><span><i class="lnr lnr-star"></i></span><span><i class="lnr lnr-star"></i></span><span><i class="lnr lnr-star"></i></span><span><i class="lnr lnr-star"></i></span><span><i class="lnr lnr-star"></i></span><div class="pro-review"><span>1 Reviews</span></div></div><div class="price-box"><span class="price-regular">$70.00</span><span class="price-old"><del>$90.00</del></span></div><h5 class="offer-text"><strong>Hurry up</strong>! offer ends in:</h5><div class="product-countdown" data-countdown="2019/08/25"></div><div class="availability"><i class="fa fa-check-circle"></i><span>200 in stock</span></div><p class="pro-desc">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diamnonumyeirmod tempor invidunt ut labore et dolore magna aliquyam erat.</p><div class="quantity-cart-box d-flex align-items-center"><h5>qty:</h5><div class="quantity"><div class="pro-qty"><input type="text" value="1"></div></div><div class="action_link"><a class="btn btn-cart2" href="#">Add to cart</a></div></div><div class="useful-links"><a href="#" data-toggle="tooltip" title="Compare"><iclass="lnr lnr-sync"></i>compare</a><a href="#" data-toggle="tooltip" title="Wishlist"><iclass="lnr lnr-heart"></i>wishlist</a></div><div class="like-icon"><a class="facebook" href="#"><i class="fa fa-facebook"></i>like</a><a class="twitter" href="#"><i class="fa fa-twitter"></i>tweet</a><a class="pinterest" href="#"><i class="fa fa-pinterest"></i>save</a><a class="google" href="#"><i class="fa fa-google-plus"></i>share</a></div></div></div></div></div> <!-- product details inner end --></div></div></div>
</div>
<!-- Quick view modal end --><!-- offcanvas search form start -->
<div class="offcanvas-search-wrapper"><div class="offcanvas-search-inner"><div class="offcanvas-close"><i class="lnr lnr-cross"></i></div><div class="container"><div class="offcanvas-search-box"><form class="d-flex bdr-bottom w-100"><input type="text" placeholder="输入鲜花名字" id="search_input"><button class="search-btn"><i class="lnr lnr-magnifier"></i>search</button></form></div></div></div>
</div><!-- Scroll to top start -->
<div class="scroll-top not-visible"><i class="fa fa-angle-up"></i>
</div>
<!-- Scroll to Top End --><!-- All vendor & plugins & active js include here -->
<!--All Vendor Js --><script src="{% static 'assets/js/vendor.js' %}"></script>
<!-- Active Js -->
<script src="{% static 'assets/js/active.js' %}"></script><script src="{% static 'assets/js/jquery.min.js ' %}"></script><script>// 当文档加载完成后执行$(document).ready(function() {//直接从获取Django渲染的值// 存储值到localStoragelocalStorage.setItem('user_id', {{user_id}});});
</script></body></html>

其中我们后端已经传了用户的ID,可以在js中直接取到值,一会这个值,需要从展示页面传递到详情页,后端之间可以用session传值,涉及到页面之间的传值可以用localStorage来传递,展示页面存储用户ID, 详情页取出用户ID,确保加入书架的时候有用户ID。

<script>// 当文档加载完成后执行$(document).ready(function() {//直接从获取Django渲染的值// 存储值到localStoragelocalStorage.setItem('user_id', {{user_id}});});
</script>

效果如下:支持两种展示方式
在这里插入图片描述
在这里插入图片描述
并且点击标题或者图片可以跳转到详情页。

2.完成图书详情页功能

2.1从后端获取图书详情信息

在图书展示页面,点击图片或者标题,将ID传递给后端,后端根据ID查询图书信息,返回给详情页面。

def display_book_by_id(request, id):# 根据ID查询bookbook = Book.objects.filter(id=id).first()return render(request, 'reader/book.html', {'book': book})

2.2详情页面展示图书数据

{% load static %}
<Html>
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="meta description"><title></title><!--=== Favicon ===--><link rel="shortcut icon" href="assets/img/favicon.ico" type="image/x-icon"/><!-- Google fonts include --><link href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,500,500i,700,900%7CYesteryear"rel="stylesheet"><!-- All Vendor & plugins CSS include --><link href="{% static 'assets/css/vendor.css' %}" rel="stylesheet"><!-- Main Style CSS --><link href="{% static 'assets/css/style.css' %}" rel="stylesheet"><!--[if lt IE 9]><script src="/oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script><script src="/oss.maxcdn.com/respond/1.4.2/respond.min.js"></script><![endif]--></head><body><!-- Start Header Area -->
<header class="header-area"><!-- main header start --><div class="main-header d-none d-lg-block"><!-- header middle area start --><div class="header-main-area sticky"><div class="container"><div class="row align-items-center position-relative"><!-- start logo area --><div class="col-lg-3"><div class="logo"><a href="index.jsp"><img src="assets/img/logo/headset.png" alt=""></a></div></div><!-- main menu area start --><div class="col-lg-6 position-static"><div class="main-menu-area"><div class="main-menu"><!-- main menu navbar start --><nav class="desktop-menu"><ul><!-- 跳转到首页 --><li><a href="/display_book">弄轩小墨</a></li></ul></nav><!-- main menu navbar end --></div></div></div><!-- main menu area end --><!-- mini cart area start --><div class="col-lg-3"><div class="header-configure-wrapper"><div class="header-configure-area"><ul class="nav justify-content-end"><li class="user-hover"><a href="#"><i class="lnr lnr-user"></i></a><ul class="dropdown-list"><li><label id="user_id_display"></label></li></ul></li></ul></div></div></div><!-- mini cart area end --></div></div></div><!-- header middle area end --></div><!-- main header start --></header>
<!-- end Header Area --><!-- off-canvas menu start -->
<aside class="off-canvas-wrapper"><div class="off-canvas-overlay"></div><div class="off-canvas-inner-content"><div class="btn-close-off-canvas"><i class="lnr lnr-cross"></i></div><div class="off-canvas-inner"><!-- search box start --><div class="search-box-offcanvas"><form><input type="text" placeholder="Search Here..."><button class="search-btn"><i class="lnr lnr-magnifier"></i></button></form></div><!-- search box end --><!-- mobile menu start --><div class="mobile-navigation"><!-- mobile menu navigation start --><nav><ul class="mobile-menu"><li><a href="index.jsp">Home</a></li><li><a href="library_main.html">Shop</a></li><li><a href="product-details.jsp">Product Details</a></li></ul></nav><!-- mobile menu navigation end --></div><!-- mobile menu end --><div class="mobile-settings"><ul class="nav"><li><div class="dropdown mobile-top-dropdown"><a href="#" class="dropdown-toggle" id="currency" data-toggle="dropdown"aria-haspopup="true" aria-expanded="false">Currency<i class="fa fa-angle-down"></i></a><div class="dropdown-menu" aria-labelledby="currency"><a class="dropdown-item" href="#">$ USD</a><a class="dropdown-item" href="#">$ EURO</a></div></div></li><li><div class="dropdown mobile-top-dropdown"><a href="#" class="dropdown-toggle" id="myaccount" data-toggle="dropdown"aria-haspopup="true" aria-expanded="false">My Account<i class="fa fa-angle-down"></i></a><div class="dropdown-menu" aria-labelledby="myaccount"><a class="dropdown-item" href="#">my account</a><a class="dropdown-item" href="#"> login</a><a class="dropdown-item" href="#">register</a></div></div></li></ul></div><!-- offcanvas widget area start --><div class="offcanvas-widget-area"><div class="off-canvas-contact-widget"><ul><li><i class="fa fa-mobile"></i><a href="#">0123456789</a></li><li><i class="fa fa-envelope-o"></i><a href="#">info@yourdomain.com</a></li></ul></div><div class="off-canvas-social-widget"><a href="#"><i class="fa fa-facebook"></i></a><a href="#"><i class="fa fa-twitter"></i></a><a href="#"><i class="fa fa-pinterest-p"></i></a><a href="#"><i class="fa fa-linkedin"></i></a><a href="#"><i class="fa fa-youtube-play"></i></a></div></div><!-- offcanvas widget area end --></div></div>
</aside>
<!-- off-canvas menu end --><!-- main wrapper start -->
<main id="app"><!-- breadcrumb area start --><div class="breadcrumb-area common-bg"><div class="container"><div class="row"><div class="col-12"><div class="breadcrumb-wrap"><nav aria-label="breadcrumb"><h1></h1><ul class="breadcrumb"><li class="breadcrumb-item"><a href="/display_book/"><i class="fa fa-home"></i></a></li><li class="breadcrumb-item active" aria-current="page">{{ book.title }}</li></ul></nav></div></div></div></div></div><!-- breadcrumb area end --><!-- page main wrapper start --><div class="shop-main-wrapper section-space"><div class="container"><div class="row"><!-- product details wrapper start --><div class="col-lg-12 order-1 order-lg-2"><!-- product details inner end --><div class="product-details-inner"><div class="row"><div class="col-lg-5"><div class="product-large-slider"><div class="pro-large-img img-zoom"><img src="{{ book.image_link }}" alt="product-details"/></div></div></div><div class="col-lg-7"><div class="product-details-des"><h3 class="product-name">{{ book.title }}</h3><input id="id" type="hidden" value={{ book.id }}><div class="ratings d-flex"><span><i class="lnr lnr-star"></i></span><span><i class="lnr lnr-star"></i></span><span><i class="lnr lnr-star"></i></span><span><i class="lnr lnr-star"></i></span><span><i class="lnr lnr-star"></i></span></div><div class="price-box"><span class="price-regular">{{ book.author }}</span></div><p class="pro-desc">简介:{{ book.details }}</p><div class="quantity-cart-box d-flex align-items-center"><div class="action_link">{#                                           请求后台方法#}<a class="btn btn-cart2">加入到书架</a>{% csrf_token %}</div></div></div></div></div></div><!-- product details inner end --></div><!-- product details wrapper end --></div></div></div><!-- page main wrapper end --></main>
<!-- main wrapper end --><!-- Scroll to top start -->
<div class="scroll-top not-visible"><i class="fa fa-angle-up"></i>
</div>
<!-- Scroll to Top End -->
<!-- All vendor & plugins & active js include here -->
<!--All Vendor Js -->
<script src="{% static 'assets/js/vendor.js' %}"></script>
<!-- Active Js -->
<script src="{% static 'assets/js/active.js' %}"></script>
<script src="{% static 'assets/js/jquery.min.js' %}"></script><script>// 当文档加载完成后执行$(document).ready(function () {//直接从获取Django渲染的值// 存储值到localStoragevar storedValue = localStorage.getItem('user_id');// 检查值是否存在if (storedValue !== null) {$('#user_id_display').text('User ID: ' + storedValue);}$(".btn-cart2").on("click", function () {// 在这里执行点击事件触发的操作//判断是否有ID值 没有的话跳转到登录页面if (storedValue == null) {alert("请先登录")window.location.href = "/login";} else {// 构建要发送的数据var data = {bookId: {{book.id}},  // 替换为实际的书籍IDuser_id: storedValue};// 获取 CSRF tokenvar csrfToken = document.querySelector('input[name="csrfmiddlewaretoken"]').value;console.log('CSRF Token:', csrfToken);console.log('data Token:', JSON.stringify(data));// 发起POST请求fetch('/borrow_book/', {method: 'POST',headers: {'Content-Type': 'application/json','X-CSRFToken': csrfToken,  // 添加 CSRF token},body: JSON.stringify(data),}).then(response => {// 处理响应if (!response.ok) {throw new Error('Network response was not ok');}return response.json();  // 如果服务器返回JSON,解析响应}).then(data => {// 处理返回的数据alert('添加成功')}).catch(error => {// 处理错误console.error('There has been a problem with your fetch operation:', error);});}});});
</script>
</body>
</Html>

敲黑板,这里有一个重点,留到借阅管理讲
详情页效果:
在这里插入图片描述

3.完成借阅管理功能

3.1管理员管理借阅数据

首先需要创建借阅管理模型

class BorrowRecord(models.Model):user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='borrow_records')book_id = models.IntegerField()returned = models.BooleanField(default=False)borrow_time = models.DateTimeField(auto_now_add=True)def __str__(self):return f"{self.user.username} borrowed book with ID {self.book_id} on {self.borrow_time}"class Meta:ordering = ['-borrow_time']

这里添加了四个字段:用户ID,并且作为借阅表的外键,绑定用户表的ID,实际开发一般不用外键,这里简单探讨Django的模型功能。图书ID,是否归还字段,借阅时间,由Django自动插入当前时间。
创建好模型,交给Django创建数据表即可。在终端中输入迁移命令

python manage.py migrate
python manage.py makemigrations bookModel  # 让 Django 知道我们在我们的模型有一些变更
python manage.py migrate bookModel   # 创建表结构

3.1.1完成用户功能,上传借阅信息

def borrow_book(request):if request.method == 'POST':try:# 从请求的 body 中获取 JSON 数据data = json.loads(request.body.decode('utf-8'))print(data)# 获取 user_id 和 book_iduser_id = data.get('user_id')book_id = data.get('bookId')# 确保 user_id 和 book_id 非空if user_id is not None and book_id is not None:# 获取用户和图书对象user = get_object_or_404(User, id=user_id)# 假设 Book 模型表示图书,你可以根据实际情况修改# book = get_object_or_404(Book, id=book_id)# 创建借阅记录BorrowRecord.objects.create(user=user, book_id=book_id)# 返回成功的 JSON 响应return JsonResponse({'status': 'success'})else:# 返回错误的 JSON 响应,表示缺少必要的数据return JsonResponse({'status': 'error', 'message': '缺少用户ID或者图书ID'}, status=400)except json.JSONDecodeError:# 返回错误的 JSON 响应,表示无法解析 JSON 数据return JsonResponse({'status': 'error', 'message': '无效的JSon数据'}, status=400)else:# 返回错误的 JSON 响应,表示不支持的请求方法return JsonResponse({'status': 'error', 'message': 'Method not allowed'}, status=405)

重点:我们是在详情页面,点击加入书架的时候,完成信息的上传。一般思路使用Form表单进行POST请求,这里使用的JQuery的fetch 来完成POST请求

<script>// 当文档加载完成后执行$(document).ready(function () {//直接从获取Django渲染的值// 存储值到localStoragevar storedValue = localStorage.getItem('user_id');// 检查值是否存在if (storedValue !== null) {$('#user_id_display').text('User ID: ' + storedValue);}$(".btn-cart2").on("click", function () {// 在这里执行点击事件触发的操作//判断是否有ID值 没有的话跳转到登录页面if (storedValue == null) {alert("请先登录")window.location.href = "/login";} else {// 构建要发送的数据var data = {bookId: {{book.id}},  // 替换为实际的书籍IDuser_id: storedValue};// 获取 CSRF tokenvar csrfToken = document.querySelector('input[name="csrfmiddlewaretoken"]').value;console.log('CSRF Token:', csrfToken);console.log('data Token:', JSON.stringify(data));// 发起POST请求fetch('/borrow_book/', {method: 'POST',headers: {'Content-Type': 'application/json','X-CSRFToken': csrfToken,  // 添加 CSRF token},body: JSON.stringify(data),}).then(response => {// 处理响应if (!response.ok) {throw new Error('Network response was not ok');}return response.json();  // 如果服务器返回JSON,解析响应}).then(data => {// 处理返回的数据alert('添加成功')}).catch(error => {// 处理错误console.error('There has been a problem with your fetch operation:', error);});}});});
</script>

上传之前,先判断有没有用户ID。之前在展示页面已经存储了用户ID,在详情页面取出ID即可,如果发现没有ID,则跳转到登录页面,让用户完成登录之后,再上传数据。
另外POST请求一定要有csrfToken ,在HTML页面加入 {% csrf_token %} 之后,Django会给我们生成csrfToken。利用JQuery提取Token的值,完成POST请求。

3.1.2完成展示借阅数据功能

与用户管理,图书管理,套路一样,这里不过多阐述

def get_all_records(request):# 查询所有记录records = BorrowRecord.objects.all()return render(request, 'borrow_list.html', {'records': records})

页面进行展示

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>管理菜单</title><style>/* 菜单样式 */body {font-family: Arial, sans-serif;margin: 0;padding: 0;}.sidebar {width: 250px;background-color: #333;height: 100%;position: fixed;left: 0;top: 0;overflow-x: hidden;{#padding-top: 20px;#}}.sidebar a {padding: 10px 16px;margin: 20px;text-decoration: none;font-size: 22px;color: #85f112;display: block;transition: 0.3s;}.sidebar a:hover {background-color: #0edcac;color: black;}.content {margin-left: 250px;padding: 20px;}.header {background-color: #f1f1f1;padding: 10px;text-align: center;}{#    美化表格#}table {border-collapse: collapse;width: 100%;}th, td {border: 1px solid #ddd;padding: 8px;text-align: left;}th {background-color: #f2f2f2;}tr:nth-child(even) {background-color: #f9f9f9;}</style>
</head>
<body><div class="sidebar"><div class="header"><h2>管理菜单</h2></div><a href="../user_list/">用户管理</a><a href="../book_list/">图书管理</a><a href="../borrow_list/">借阅管理</a><a href="../migrations_list/">迁移记录</a>
</div><div class="content"><!-- 这里是你的主要内容 --><h2>用户列表</h2><table><tr><th>编号</th><th>图书ID</th><th>用户ID</th><th>是否归还</th><th>借阅时间</th><th>操作</th><!-- 这里可以根据需要显示其他字段 --></tr>{% for record in records %}<tr><td>{{ record.id }}</td><td>{{ record.book_id }}</td><td>{{ record.user_id }}</td><td>    {% if record.returned == 0 %}未归还{% elif record.returned == 1 %}归还{% else %}Unknown{% endif %}</td><td>{{ record.borrow_time }}</td><td><a href="/book/{{  record.id  }}/returned/">归还</a> | <a href="/delete_record/{{   record.id }}/">删除</a></td><!-- 这里可以根据需要显示其他字段 --></tr>{% endfor %}</table>
</div></body>
</html>

效果如图:
在这里插入图片描述

3.1.3完成归还图书功能

这里也可以做成读者功能,这里简化了功能,只做了管理员功能,感兴趣的伙伴,可以下载源码,进行二次开发

def update_borrow_record(request, record_id):borrow_record = get_object_or_404(BorrowRecord, id=record_id)# 取反borrow_record.returned = not borrow_record.returnedborrow_record.save()return redirect('/borrow_list')

3.1.4完成删除借阅记录功能

def delete_book(request, id):try:print(id)record = BorrowRecord.objects.filter(id=id).first()print(record)record.delete()return redirect('/borrow_list')# 重定向到用借阅页面except BorrowRecord.DoesNotExist:return HttpResponse('记录不存在')

最后附上开源地址

项目源码地址

链接: Django 图书借阅系统

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/617436.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt QListWidget列表框控件

文章目录 1 属性和方法1.1 外观1.2 添加条目1.3 删除条目1.4 信号和槽 2 实例2.1 布局2.2 代码实现 Qt中的列表框控件&#xff0c;对应的类是QListWidget 它用于显示多个列表项&#xff0c;列表项对应的类是QListWidgetitem 1 属性和方法 QListWidget有很多属性和方法&#xf…

004 Golang-channel-practice 左右括号匹配

第四题 左右括号打印 一个协程负责打印“&#xff08;”&#xff0c;一个协程负责打印“&#xff09;”&#xff0c;左右括号的数量要匹配。在这道题目里&#xff0c;我在main函数里进行了一个死循环。会产生一个随机数&#xff0c;随机数就是接下来要打印的左括号的数量。 例…

解决elementUI或elementPlus的按钮点击后需要失去焦点才能恢复原本样式问题

废话不多说直接上代码&#xff0c;只需要在button中添加如下代码即可 focus"(e) > e.target.blur()"

【大厂秘籍】 - Redis持久化篇

创作不易&#xff0c;你的关注分享就是博主更新的最大动力&#xff0c; 每周持续更新 微信搜索【 企鹅君】关注还能领取学习资料喔&#xff0c;第一时间阅读(比博客早两到三篇) 求关注❤️ 求点赞❤️ 求分享❤️ 对博主真的非常重要 企鹅君原创&#xff5c;GitHub开源项目gith…

[自动驾驶算法][从0开始轨迹预测]:一、坐标和坐标系变换

既然要从0开始轨迹预测&#xff0c;那从哪开始写起呢&#xff1f;回想下自己的学习历程&#xff0c;真正有挑战性的不是模型结构&#xff0c;不是繁琐的训练和调参&#xff0c;而是数据的制作&#xff01;&#xff01;&#xff01; 笔者自认为不是一个数学基础牢固的人&#xf…

如何使用SVN查看旧版本

和目录 第一步&#xff1a;打开SVN客户端 第二步&#xff1a;浏览历史版本 第三步&#xff1a;还原历史版本 结论 Subversion (缩写为SVN)是一种常用的版本控制系统&#xff0c;它可以帮助团队协作开发软件项目。除了基本的版本控制功能外&#xff0c;SVN还提供了许多其他功…

AutoCAD保存打开新建等操作变成命令行

文章标签没cad&#xff0c;就设了个3d 变成命令行的表现形式 如图点“另存为”后的样子&#xff1a; 如图点“打开”后的样子&#xff1a; 改回图形界面 键入filedia&#xff1a; 空格确认后&#xff0c;输入1&#xff0c;再空格确认&#xff1a; 图形界面回来了&#xff1a…

Linux/SwagShop

Enumeration nmap 仍然一样&#xff0c;先使用nmap探索目标开放端口情况 看到开启了22端口和80端口&#xff0c;还是一样的&#xff0c;先从80端口开始探索&#xff0c;从nmap给出的结果&#xff0c;我们可以看到有一个域名&#xff0c;因此在/etc/hosts中添加域名到IP的映射…

Linux的权限(2)

目录 Linux的&#xff08;事物属性&#xff09;文件权限 文件权限值得表示方法 字符表示方法 8进制表示方法 文件访问权限得相关设置方法 chmod修改权限法1 chmod修改权限法2 文件的角色&#xff08;拥有者/所属者&#xff09;修改 chown拥有者 chgrp所属者 &…

业务向——基于淘宝联盟平台的CPS

业务向——基于淘宝联盟平台的CPS 导读小试牛刀签名商品活动订单获取及用户 导读 上篇文章我们分享了多多进宝平台&#xff0c;那么这篇文章想继续带来CPS业务的分享&#xff0c;这次玩转的平台是淘宝联盟。在对接的过程中&#xff0c;也是踩了一些坑&#xff0c;特别是对于订…

车规MCU开发之E2E协议

啥是E2E&#xff1f; E2E的原理&#xff1a; 1. 发送端&#xff1a;发送数据包添加E2E保护头 2. 接收端&#xff1a;接收数据包校验E2E保护头 E2E例子 - profile 11为例 E2E_P11ConfigType wk_stP11Cfg { .CounterOffset 8, .CRCOffset 0, .DataID …

Jenkins安装和配置

拉取Jenkins镜像 docker pull jenkins/jenkins 编写jenkins_docker.yml version: "3.1" services:jenkins:image: jenkins/jenkinscontainer_name: jenkinsports:- 8080:8080- 50000:50000volumes:- ./data/:/var/jenkins_home/首次启动会因为数据卷data目录没有权限…

30天精通Nodejs--第十七天:express-路由配置

目录 引言基础路由配置路由参数与查询参数路由前缀与子路由路由重定向结语 引言 上篇文章我们简单介绍了express的基础用法&#xff0c;包括express的安装、创建路由及项目启动&#xff0c;对express有了一个基础的了解&#xff0c;这篇开始我们将详细介绍express的一些高级用…

IDEA—初始化配置

注&#xff1a;以下红框圈的部分&#xff0c;均为已设置好的 外观与行为 编辑器 高级设置 按两次 shift 弹出提示问题解决

神经网络|张量tensor(待完善)

文章目录 tensor/张量什么是tensor&#xff1f;如何用代码实现tensortensor在神经网络中的应用 其他 tensor/张量 什么是tensor&#xff1f; 张量是用来探究一个点在各个切面&#xff08;一共三个切面&#xff09;和各个方向&#xff08;x&#xff0c;y&#xff0c;z三个方向&…

蚁群算法(ACO)解决旅行商(TSP)问题的python实现

TSP问题 旅行商问题&#xff08;Travelling Salesman Problem, 简记TSP&#xff0c;亦称货郎担问题)&#xff1a;设有n个城市和距离矩阵D [dij]&#xff0c;其中dij表示城市i到城市j的距离&#xff0c;i, j 1, 2 … n&#xff0c;则问题是要找出遍访每个城市恰好一次的一条回…

【kafka】记录用-----------1

主题&#xff08;topic&#xff09;&#xff1a;消息的第一次分类 根据人为的划分条件将消息分成不同的主题 主题的划分是人为的根据不同的任务情景去划分 比如&#xff0c;我们有两个主题&#xff0c;一个是"订单"&#xff0c;另一个是"库存"。每个主题代…

强化学习应用(二):基于Q-learning的无人机物流路径规划研究(提供Python代码)

一、Q-learning简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于马尔可夫决策过程&#xff08;MDP&#xff09;的问题。它通过学习一个价值函数来指导智能体在环境中做出决策&#xff0c;以最大化累积奖励。 Q-learning算法的核心思想是通过不断更新一个称为Q值的…

【JAVA】谈谈 ReadWriteLock 和 StampedLock

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 ReadWriteLock&#xff08;读写锁&#xff09; 基本原理&#xff1a; 接口和实现&#xff1a; 用法示例&#xff1a; StampedL…

Camunda Cluster

Rest API&#xff1a;无状态&#xff0c;根据权重路由。控制台API&#xff1a;webapp 登录有状态&#xff0c;根据IP路由。 nginx.conf upstream rest_proxy {server localhost:8080 weight1;server localhost:8081 weight1;server localhost:8082 weight1; }upstream webapp…