基于DRF的图书增删改查

朱雀 2023-08-17 17:45 629阅读 0赞

功能演示

信息展示

1303124-20190727153429046-1104249756.png

添加功能

1303124-20190727163053406-1219633873.gif

编辑功能

1303124-20190727163129393-1000554017.gif

删除功能

1303124-20190727163212817-1308226227.gif

DRF构建后台数据

本例的Model如下

  1. from django.db import models
  2. class Publish(models.Model):
  3. name = models.CharField(max_length=32)
  4. class Author(models.Model):
  5. name = models.CharField(max_length=32,verbose_name='姓名')
  6. class Book(models.Model):
  7. title = models.CharField(verbose_name='书名',max_length=56)
  8. price = models.DecimalField(verbose_name='价格',max_digits=8,decimal_places=2)
  9. pub_date = models.DateField(verbose_name='出版日期')
  10. publish = models.ForeignKey(to=Publish,on_delete=models.CASCADE)
  11. authors = models.ManyToManyField(to=Author)

注册DRF

  1. INSTALLED_APPS = [
  2. 'django.contrib.admin',
  3. 'django.contrib.auth',
  4. 'django.contrib.contenttypes',
  5. 'django.contrib.sessions',
  6. 'django.contrib.messages',
  7. 'django.contrib.staticfiles',
  8. 'book.apps.BookConfig',
  9. 'rest_framework',
  10. ]

路由分发如下

  1. # 查看与新增—— GET与POST
  2. url(r'^books/$',views.BookListView.as_view(),name='book_get_post'),
  3. # 修改与删除—— PUT与DELETE
  4. url(r'^book/(?P<pk>\d+)/$',views.BookView.as_view(),name='book_put_delete'),

视图函数如下

  1. from rest_framework.views import APIView
  2. from rest_framework.response import Response
  3. from book import models
  4. from book.my_serializer import BookSerializer
  5. class BookListView(APIView):
  6. def get(self, request, *args, **kwargs):
  7. """ 获取书籍信息 """
  8. # 用自定义的序列化器去实现~~~
  9. all_books = models.Book.objects.all()
  10. # 第一个参数是instance~是一个对象
  11. # 但是all()方法查出来的是一个“对象列表”——所以需要加many=True
  12. ser_obj = BookSerializer(all_books, many=True)
  13. # 返回自定义序列化器的data
  14. return Response(ser_obj.data)
  15. def post(self, request, *args, **kwargs):
  16. """新增数据 返回新建的书籍的数据 json格式 """
  17. # 用序列化器进行校验!!!
  18. # 注意:这里用的是request.data去取新增的值!!!
  19. print('>>>>>>',request.data)
  20. ser_book = BookSerializer(data=request.data)
  21. if ser_book.is_valid():
  22. ser_book.save()
  23. # 校验成功并且成功保存的话~返回新增的数据!
  24. return render(request,'book_list.html')
  25. else:
  26. print(ser_book.errors)
  27. return Response(ser_book.errors)
  28. class BookView(APIView):
  29. def get(self,request,pk,*args,**kwargs):
  30. # 找Model对象
  31. book_obj = models.Book.objects.filter(pk=pk).first()
  32. # 序列化器对象——此时instance只有一个book_obj,不用加many=True了!
  33. ser_obj = BookSerializer(instance=book_obj)
  34. # 用Response方法返回序列化器对象的data
  35. return Response(ser_obj.data)
  36. def put(self,request,pk,*args,**kwargs):
  37. book_obj = models.Book.objects.filter(pk=pk).first()
  38. # partial=True —— 表示支持“部分提交/局部更新”
  39. ser_obj = BookSerializer(instance=book_obj,data=request.data,partial=True)
  40. if ser_obj.is_valid():
  41. ser_obj.save()
  42. return Response(ser_obj.data)
  43. else:
  44. return Response(ser_obj.errors)
  45. # 删除方法不需要用序列化器了
  46. def delete(self,request,pk,*args,**kwargs):
  47. obj = models.Book.objects.filter(pk=pk).first()
  48. if obj:
  49. obj.delete()
  50. return Response({
  51. 'msg':'删除成功!'})
  52. else:
  53. return Response({
  54. "error":'数据不存在!'})

自定义的序列化器代码如下

  1. # -*- coding:utf-8 -*-
  2. from rest_framework import serializers
  3. from book import models
  4. class PublishSerializer(serializers.Serializer):
  5. id = serializers.IntegerField(read_only=True)
  6. name = serializers.CharField()
  7. class AuthorSerializer(serializers.Serializer):
  8. id = serializers.IntegerField()
  9. name = serializers.CharField()
  10. class BookSerializer(serializers.Serializer):
  11. # 与Book中的属性对应上
  12. # id 也需要~后面编辑与删除用得到~~设置read_only,添加的时候不必填
  13. id = serializers.IntegerField(read_only=True)
  14. title = serializers.CharField()
  15. price = serializers.DecimalField(max_digits=8,decimal_places=2)
  16. pub_date = serializers.DateField()
  17. # 外键的~这个字段其实存的是id~~注意这里是publish_id——数据库中存储的字段~~但是这种方式只能拿到id值
  18. # publish_id = serializers.IntegerField()
  19. # 多对一 外键关联~
  20. # 如果我们想拿publish的name的话,就需要交给上一个序列化器PublishSerializer去处理
  21. # 提交的时候~~不用填这个,所以设置required=False
  22. # 只有get请求要他而post请求不用它~所以设置 read_only=True
  23. publish = PublishSerializer(required=False,read_only=True)
  24. # 多对多~
  25. # 只有get请求要他而post请求不用它:read_only=True
  26. # 下面必须有一个 get_字段名 的方法对应!
  27. authors = serializers.SerializerMethodField(read_only=True)
  28. # post提交用这个字段~是int类型的
  29. # get请求不要他~~设置 write_only=True
  30. post_publish = serializers.IntegerField(write_only=True)
  31. # post提交用这个字段~是一个ListField~列表里是数字
  32. # get请求不要他~~设置 write_only=True
  33. post_authors = serializers.ListField(write_only=True)
  34. # 多对多关系查找authors用到的方法——与上面的SerializerMethodField对应
  35. def get_authors(self,obj):
  36. # 注意~obj是Book对象!!
  37. # print(obj)
  38. # 基于对象的跨表查询~注意是多个对象了~many应该设置为True
  39. ser_obj = AuthorSerializer(obj.authors.all(),many=True)
  40. return ser_obj.data
  41. # POST方式增加数据需要
  42. def create(self, validated_data):
  43. # post提交的时候~~重写create方法
  44. # post提交给的数据应该是这种格式的
  45. # 注意后面那两个是post_publish、post_authors~专门用于提交的字段
  46. """
  47. {
  48. "title": "西游记",
  49. "price": 12.20,
  50. "pub_date": "2019-12-22T10:10:11Z",
  51. "post_publish": 1,
  52. "post_authors": [1,2]
  53. }
  54. """
  55. print('validated_data>>>',validated_data)
  56. book_obj = models.Book.objects.create(
  57. title=validated_data.get('title'),
  58. price=validated_data.get('price'),
  59. pub_date=validated_data.get('pub_date'),
  60. publish_id=validated_data.get('post_publish'),
  61. )
  62. # 多对多插入数据~~基于对象的跨表查询
  63. # 注意用set方法存多对多关系的数据
  64. book_obj.authors.set(validated_data.get('post_authors'))
  65. return book_obj
  66. # PUT请求修改数据需要写的方法
  67. def update(self, instance, validated_data):
  68. # 如果取到了就用修改的~~如果没有就用原来的数据
  69. instance.title = validated_data.get('title',instance.title)
  70. instance.prince = validated_data.get('price',instance.price)
  71. instance.pub_date = validated_data.get('pub_date',instance.pub_date)
  72. # 上面设置了post_publish为write_only了~所以修改要用post_publish
  73. instance.publish_id = validated_data.get('post_publish',instance.publish_id)
  74. # 先save~然后再处理一下多对多关系的数据
  75. instance.save()
  76. # 基于对象的跨表查询~~注意用set方法存多对多关系的数据
  77. # 如果没有的话需要用all方法取出所有对象~~
  78. # # 上面设置了post_authors为write_only了~所以修改要用post_authors
  79. instance.authors.set(validated_data.get('post_authors',instance.authors.all()))
  80. # 最后记得把instance 返回
  81. return instance

在DRF自带的页面进行数据的增删改查测试

至此DRF就写好了,我们可以根据路由去访问对应的页面进行数据的增删改查操作(需要注意,必须先在settings中注册了rest_framework后才能访问DRF自带的页面)

DRF自带的页面是这样的:

1303124-20190727155135117-1469287451.png

当然,我们不能让用户看这样的页面,这就需要前端请求DRF构建好的数据进行标签的构建了。

前端请求DRF构建好的数据并构建页面效果

测试路由如下

  1. # 书籍展示的页面
  2. url(r'^book_list/$',views.book_list,name='book_list'),
  3. # 添加书籍的页面
  4. url(r'^add_book_view/$',views.add_book,name='add_book_view'),# 编辑书籍的展示页面~~
  5. url(r'edit_book_view/(?P<pk>\d+)/$',views.edit_book,name='edit_book'),

视图函数如下

视图函数非常简单,再加上是进行数据测试,所以这里的视图函数只负责返回页面。

数据的操作全部是用ajax与js做的。

  1. # 展示 书籍列表
  2. def book_list(request):
  3. return render(request,'book_list.html')
  4. # 展示 添加书籍页面
  5. def add_book(request):
  6. return render(request,'add_book.html')
  7. # 编辑书籍的展示页面
  8. def edit_book(request,pk):
  9. return render(request,'edit_book.html')

所有页面的母版

ContractedBlock.gif ExpandedBlockStart.gif

  1. {% load static %}
  2. <!DOCTYPE html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>{% block title %} {% endblock title %}</title>
  7. <link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.css' %}">
  8. <style>
  9. {
  10. # th中的文字剧中 bootstrap设置的是left #}
  11. th {
  12. text-align: center;
  13. }
  14. </style>
  15. </head>
  16. <body style="padding-top:52px;">
  17. <!--导航 独立于页面,不包含在盒子里面。不要放在container里面 -->
  18. <nav class="navbar navbar-default navbar-fixed-top navbar-inverse">
  19. <div class="container-fluid">
  20. <!-- Brand and toggle get grouped for better mobile display -->
  21. <div class="navbar-header">
  22. <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
  23. data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
  24. <span class="sr-only">Toggle navigation</span>
  25. <span class="icon-bar"></span>
  26. <span class="icon-bar"></span>
  27. <span class="icon-bar"></span>
  28. </button>
  29. <a class="navbar-brand" href="#">火之国</a>
  30. </div>
  31. <!-- Collect the nav links, forms, and other content for toggling -->
  32. <div class="collapse navbar-collapse pull-right" id="bs-example-navbar-collapse-1">
  33. <ul class="nav navbar-nav">
  34. <li><a href="#">Link</a></li>
  35. <li><a href="#">Link</a></li>
  36. </ul>
  37. </div><!-- /.navbar-collapse -->
  38. </div><!-- /.container-fluid -->
  39. </nav>
  40. <div class="container">
  41. <div class="row">
  42. <div class="col-md-12">
  43. <div class="pannel panel-danger">
  44. <!--panel-heading-->
  45. <div class="panel-heading">
  46. <!--panel-title-->
  47. <h3 class="panel-title">火之国图书管理系统</h3>
  48. </div>
  49. <!--panel-body-->
  50. <div class="panel-body">
  51. <!--把其他的组件放到panel-body里面-->
  52. <!--block -->
  53. {% block pannel-body %}
  54. {% endblock pannel-body %}
  55. </div>
  56. </div>
  57. </div>
  58. </div>
  59. </div>
  60. <script src="{% static 'jquery-3.4.1.js' %}"></script>
  61. <script src="{% static 'bootstrap-3.3.7/js/bootstrap.js' %}"></script>
  62. {% block script %}
  63. {% endblock script %}
  64. </body>
  65. </html>

base.html

书籍展示页面及删除书籍的功能

书籍展示发送的是get请求。

删除书籍发送的是delete请求。

  1. {% extends 'base.html' %}
  2. {% block title %}
  3. 主页
  4. {% endblock title %}
  5. {% block pannel-body %}
  6. {% csrf_token %}
  7. <a id="add_book" href="{% url 'add_book_view' %}" class="btn btn-success pull-right">添加书籍</a>
  8. <br><br>
  9. <div id="div_table" class="table-responsive" style="text-align: center">
  10. <table id="table" class="table table-striped table-bordered table-hover table-condensed">
  11. <thead>
  12. <tr class="success">
  13. <th>编号</th>
  14. <th>书籍名称</th>
  15. <th>价格</th>
  16. <th>出版日期</th>
  17. <th>出版社</th>
  18. <th>作者</th>
  19. <th>操作</th>
  20. </tr>
  21. </thead>
  22. {# 委托的父级标签用tbody #}
  23. <tbody id="tbody">
  24. </tbody>
  25. </table>
  26. </div>
  27. {% endblock pannel-body %}
  28. {% block script %}
  29. <script>
  30. // 格式化时间的函数
  31. function formatDate(time) {
  32. var date = new Date(time);
  33. var year = date.getFullYear(),
  34. month = date.getMonth() + 1,//月份是从0开始的
  35. day = date.getDate(),
  36. hour = date.getHours(),
  37. min = date.getMinutes(),
  38. sec = date.getSeconds();
  39. var newTime = year + '-' +
  40. month + '-' +
  41. day + ' ' +
  42. hour + ':' +
  43. min + ':' +
  44. sec;
  45. return newTime;
  46. }
  47. // 页面加载自动触发ajax请求~向DRF获取所有数据并在前端渲染
  48. $(document).ready(function () {
  49. $.ajax({
  50. url: '/books/',
  51. type: 'get',
  52. success: function (data) {
  53. console.log(data, typeof (data));
  54. // data是一个object
  55. for (var i = 0; i < data.length; i++) {
  56. // data[i]是一个个自定义对象
  57. //console.log(data[i],typeof(data[i]));
  58. var tr = document.createElement('tr');
  59. var td_num = document.createElement('td');
  60. // 提前把编号写进tr中去 注意同时将id也加进去
  61. td_num.innerHTML = (i + 1) + '<span class="book_pk" style="display: none">' + data[i].id + '</span> </td>';
  62. //这时tr的第一个元素就是一个个的编号——并且里面的span标签带着每个数据的id
  63. tr.append(td_num);
  64. for (var j in data[i]) {
  65. //console.log(j);
  66. // 不用填id字段
  67. if (j === 'id') {
  68. continue;
  69. }
  70. // 新建一个td标签,把遍历的数据加进去
  71. var td = document.createElement('td');
  72. //格式化一下出版日期的格式
  73. if (j === 'pub_date') {
  74. data[i][j] = formatDate(data[i][j]);
  75. }
  76. //展示出版社的名字
  77. if (j === 'publish') {
  78. data[i][j] = data[i][j]['name'];
  79. }
  80. //展示作者的名字
  81. if (j === 'authors') {
  82. //console.log(data[i][j]);
  83. var authors = '';
  84. for (var k in data[i][j]) {
  85. authors += data[i][j][k]['name'] + ' ';
  86. }
  87. data[i][j] = authors;
  88. }
  89. td.append(data[i][j]);
  90. tr.append(td);
  91. }
  92. //循环完,最后把编辑与删除按钮添加进去
  93. var tdd = document.createElement('td');
  94. tdd.innerHTML = '<td><a class="btn btn-primary edit_book"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span><span>编辑</span>\n' +
  95. '</a><a class="btn btn-danger del_book"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span><span>删除</span></a></td>';
  96. tr.append(tdd);
  97. // 最后将tr加到tbody中
  98. $('#tbody').append(tr);
  99. }
  100. }
  101. })
  102. });
  103. // 删除按钮的点击事件
  104. // 用委托实现
  105. // 这里也可以加一个"模态对话框"~给用户一个确认删除删除的提示
  106. $('#tbody').on('click', '.del_book', function () {
  107. // 找到这本书对应的id~
  108. // console.log($(this).parent().parent().find('.book_pk').text());
  109. var book_id = $(this).parent().parent().find('.book_pk').text();
  110. $.ajax({
  111. url: '/book/' + book_id + '/',
  112. type: 'delete',
  113. success: function (data) {
  114. location.href = '/book_list/';
  115. }
  116. })
  117. });
  118. // 编辑按钮的点击事件
  119. // 用委托实现
  120. $('#tbody').on('click', '.edit_book', function () {
  121. // 找到这本书对应的id~
  122. var book_id = $(this).parent().parent().find('.book_pk').text();
  123. $.ajax({
  124. url:'/book/'+book_id+'/',
  125. type:'put',
  126. success:function (data) {
  127. // data是待编辑书籍的数据
  128. console.log(data,typeof(data));
  129. // 序列化数据
  130. data_json = JSON.stringify(data);
  131. // 将数据存到session中
  132. sessionStorage.edit_book_data = data_json;
  133. // 跳转到编辑书籍页面
  134. location.href = '/edit_book_view/' + book_id +'/';
  135. }
  136. })
  137. })
  138. </script>
  139. {% endblock script %}

添加书籍页面

添加书籍发送的是post请求。

  1. {% extends 'base.html' %}
  2. {% block title %}
  3. 主页
  4. {% endblock title %}
  5. {% block pannel-body %}
  6. <div class="col-md-8 col-md-offset-2">
  7. <h2 class="text-center">添加书籍</h2>
  8. <div>
  9. <div class="form-group">
  10. <label for="book_name">书籍名称</label>
  11. <input type="text" id="book_name" class="form-control" placeholder="书籍名称"
  12. autocomplete="off">
  13. <span class="help-block"></span>
  14. </div>
  15. <div class="form-group">
  16. <label for="price">价格</label>
  17. <input type="number" id="price" class="form-control" placeholder="价格"
  18. autocomplete="off">
  19. <span class="help-block"></span>
  20. </div>
  21. <div class="form-group">
  22. <label for="pub_date">出版日期</label>
  23. <input type="date" id="pub_date" class="form-control" placeholder="出版日期">
  24. <span class="help-block"></span>
  25. </div>
  26. <div class="form-group">
  27. <label for="">出版社</label>
  28. <select id="publish" class="form-control">
  29. <option value="1">苹果出版社</option>
  30. <option value="2">西瓜出版社</option>
  31. </select>
  32. </div>
  33. <div class="form-group">
  34. <label for="">作者</label>
  35. <select name="authors" id="authors" class="form-control" multiple>
  36. <option value="1">whw</option>
  37. <option value="2">naruto</option>
  38. <option value="3">sasuke</option>
  39. </select>
  40. </div>
  41. <div class="form-group">
  42. <h4 id="add_error" class="pull-left" style="color:red;margin-top: 0"></h4>
  43. <input id="confirm_add" type="button" class="btn btn-success pull-right" value="确认添加">
  44. </div>
  45. </div>
  46. </div>
  47. {% endblock pannel-body %}
  48. {% block script %}
  49. <script>
  50. {# 确认按钮 #}
  51. $('#confirm_add').click(function () {
  52. {#console.log(123123);#}
  53. var title = $('#book_name').val();
  54. var price = $('#price').val();
  55. var pub_date = $('#pub_date').val();
  56. // 下拉列表被选中的这样选取
  57. var publish = $('#publish option:selected').val();
  58. //ajax操作
  59. $.ajax({
  60. url: '{% url "book_get_post" %}',
  61. type: 'post',
  62. data: {
  63. title: title,
  64. price: price,
  65. pub_date: pub_date,
  66. //pub_date: "2019-08-02T09:35:13.064532Z",
  67. post_publish: publish,
  68. //post_authors: authors,
  69. post_authors: $('#authors').val(),
  70. },
  71. // 传数组
  72. traditional: true,
  73. success: function (data) {
  74. console.log(data);
  75. //alert('添加成功!');
  76. location.href = '{% url "book_list" %}';
  77. }
  78. })
  79. });
  80. </script>
  81. {% endblock script %}

编辑书籍页面

编辑书籍这里需要说一下过程:

(1)首先我在书籍展示那里点击“编辑”的时候,先把当前点击的书籍的信息取出来,然后序列化,最后将序列化的数据存在session中。

(2)然后在编辑页面从session中获取当前需要编辑的书籍的信息并把这些信息显示在前端的input框中。

(3)最后根据用户输入的数据保存书籍信息。

  1. {% extends 'base.html' %}
  2. {% block title %}
  3. 主页
  4. {% endblock title %}
  5. {% block pannel-body %}
  6. <div class="col-md-8 col-md-offset-2">
  7. <h2 class="text-center">编辑书籍</h2>
  8. <div>
  9. <div class="form-group">
  10. <label for="book_name">书籍名称</label>
  11. <input type="text" id="book_name" class="form-control" placeholder="书籍名称"
  12. autocomplete="off">
  13. <span class="help-block"></span>
  14. </div>
  15. <div class="form-group">
  16. <label for="price">价格</label>
  17. <input type="number" id="price" class="form-control" placeholder="价格"
  18. autocomplete="off">
  19. <span class="help-block"></span>
  20. </div>
  21. <div class="form-group">
  22. <label for="pub_date">出版日期</label>
  23. <input type="date" id="pub_date" class="form-control" placeholder="出版日期">
  24. <span class="help-block"></span>
  25. </div>
  26. <div class="form-group">
  27. <label for="">出版社</label>
  28. <select id="publish" class="form-control">
  29. <option value="1">苹果出版社</option>
  30. <option value="2">西瓜出版社</option>
  31. </select>
  32. </div>
  33. <div class="form-group">
  34. <label for="">作者</label>
  35. <select name="authors" id="authors" class="form-control" multiple>
  36. <option value="1">whw</option>
  37. <option value="2">naruto</option>
  38. <option value="3">sasuke</option>
  39. </select>
  40. </div>
  41. <div class="form-group">
  42. <h4 id="add_error" class="pull-left" style="color:red;margin-top: 0"></h4>
  43. <input id="confirm_add" type="button" class="btn btn-success pull-right" value="确认编辑">
  44. </div>
  45. </div>
  46. </div>
  47. {% endblock pannel-body %}
  48. {% block script %}
  49. <script>
  50. // 页面加载后将session中的数据写到上面的标签中
  51. $(document).ready(function () {
  52. // 获取session中的数据
  53. var data_session = sessionStorage['edit_book_data'];
  54. // 记得反序列化一下
  55. var data = JSON.parse(data_session);
  56. console.log(data, typeof (data));
  57. // 取出edit_book_id 把它设置为全局的变量!后面ajax提交的时候会用到
  58. edit_book_id = data['id'];
  59. // 将数据填在上面的input框中~注意是val方法!
  60. $('#book_name').val(data['title']);
  61. $('#price').val(data['price']);
  62. $('#pub_date').val(data['pub_date']);
  63. $('#publish').val(data['publish']['id']);
  64. // 让之前的作者名被选中
  65. var arr_val = [];
  66. for(var i in data['authors']){
  67. //console.log(data['authors'][i]['id']);
  68. arr_val.push(data['authors'][i]['id'])
  69. }
  70. // console.log(arr_val); [1,2]
  71. // 把数组传给复选框的val~让之前的作者被选中
  72. $('#authors').val(arr_val);
  73. });
  74. // 确认编辑按钮
  75. $('#confirm_add').click(function () {
  76. var title = $('#book_name').val();
  77. var price = $('#price').val();
  78. var pub_date = $('#pub_date').val();
  79. // 下拉列表被选中的这样选取
  80. var publish = $('#publish option:selected').val();
  81. //ajax操作
  82. $.ajax({
  83. url: '/book/'+edit_book_id+'/',
  84. type: 'put',
  85. data: {
  86. title: title,
  87. price: price,
  88. pub_date: pub_date,
  89. //pub_date: "2019-08-02T09:35:13.064532Z",
  90. post_publish: publish,
  91. //post_authors: authors,
  92. post_authors: $('#authors').val(),
  93. },
  94. // 传数组~
  95. traditional: true,
  96. success: function (data) {
  97. console.log(data);
  98. //alert('添加成功!');
  99. location.href = '{% url "book_list" %}';
  100. }
  101. })
  102. });
  103. </script>
  104. {% endblock script %}

转载于:https://www.cnblogs.com/ryxiong-blog/p/11611405.html

发表评论

表情:
评论列表 (有 0 条评论,629人围观)

还没有评论,来说两句吧...

相关阅读