• 基于DRF的图书增删改查练习


    功能演示

    信息展示

    添加功能

    编辑功能

    删除功能

    DRF构建后台数据

    本例的Model如下

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

    注册DRF

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'book.apps.BookConfig',
        'rest_framework',
    ]

    路由分发如下

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

    视图函数如下

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from book import models
    from book.my_serializer import BookSerializer
    
    
    class BookListView(APIView):
    
        def get(self, request, *args, **kwargs):
            """ 获取书籍信息 """       
            # 用自定义的序列化器去实现~~~
            all_books = models.Book.objects.all()
            # 第一个参数是instance~是一个对象
            # 但是all()方法查出来的是一个“对象列表”——所以需要加many=True
            ser_obj = BookSerializer(all_books, many=True)
            # 返回自定义序列化器的data
            return Response(ser_obj.data)
    
    
        def post(self, request, *args, **kwargs):
            """新增数据 返回新建的书籍的数据 json格式 """
            # 用序列化器进行校验!!!
            # 注意:这里用的是request.data去取新增的值!!!
            print('>>>>>>',request.data)
    
            ser_book = BookSerializer(data=request.data)
            if ser_book.is_valid():
                ser_book.save()
                # 校验成功并且成功保存的话~返回新增的数据!
                return render(request,'book_list.html')
            else:
                print(ser_book.errors)
                return Response(ser_book.errors)
    
    
    class BookView(APIView):
    
        def get(self,request,pk,*args,**kwargs):
            # 找Model对象
            book_obj = models.Book.objects.filter(pk=pk).first()
            # 序列化器对象——此时instance只有一个book_obj,不用加many=True了!
            ser_obj = BookSerializer(instance=book_obj)
            # 用Response方法返回序列化器对象的data
            return Response(ser_obj.data)
    
        def put(self,request,pk,*args,**kwargs):
    
            book_obj = models.Book.objects.filter(pk=pk).first()
            # partial=True —— 表示支持“部分提交/局部更新”
            ser_obj = BookSerializer(instance=book_obj,data=request.data,partial=True)
            if ser_obj.is_valid():
                ser_obj.save()
                return Response(ser_obj.data)
            else:
                return Response(ser_obj.errors)
    
        # 删除方法不需要用序列化器了
        def delete(self,request,pk,*args,**kwargs):
            obj = models.Book.objects.filter(pk=pk).first()
            if obj:
                obj.delete()
                return Response({'msg':'删除成功!'})
            else:
                return Response({"error":'数据不存在!'})

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

    # -*- coding:utf-8 -*-
    from rest_framework import serializers
    
    from book import models
    
    class PublishSerializer(serializers.Serializer):
        id = serializers.IntegerField(read_only=True)
        name = serializers.CharField()
    
    
    class AuthorSerializer(serializers.Serializer):
        id = serializers.IntegerField()
        name = serializers.CharField()
    
    
    class BookSerializer(serializers.Serializer):
        # 与Book中的属性对应上
        # id 也需要~后面编辑与删除用得到~~设置read_only,添加的时候不必填
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField()
        price = serializers.DecimalField(max_digits=8,decimal_places=2)
        pub_date = serializers.DateField()
    
        # 外键的~这个字段其实存的是id~~注意这里是publish_id——数据库中存储的字段~~但是这种方式只能拿到id值
        # publish_id = serializers.IntegerField()
    
        # 多对一 外键关联~
        # 如果我们想拿publish的name的话,就需要交给上一个序列化器PublishSerializer去处理
        # 提交的时候~~不用填这个,所以设置required=False
        # 只有get请求要他而post请求不用它~所以设置 read_only=True
        publish = PublishSerializer(required=False,read_only=True)
        # 多对多~
        # 只有get请求要他而post请求不用它:read_only=True
        # 下面必须有一个 get_字段名 的方法对应!
        authors = serializers.SerializerMethodField(read_only=True)
    
        # post提交用这个字段~是int类型的
        # get请求不要他~~设置 write_only=True
        post_publish = serializers.IntegerField(write_only=True)
        # post提交用这个字段~是一个ListField~列表里是数字
        # get请求不要他~~设置 write_only=True
        post_authors = serializers.ListField(write_only=True)
    
        # 多对多关系查找authors用到的方法——与上面的SerializerMethodField对应
        def get_authors(self,obj):
            # 注意~obj是Book对象!!
            # print(obj)
            # 基于对象的跨表查询~注意是多个对象了~many应该设置为True
            ser_obj = AuthorSerializer(obj.authors.all(),many=True)
            return ser_obj.data
    
    
        # POST方式增加数据需要
        def create(self, validated_data):
            # post提交的时候~~重写create方法
            # post提交给的数据应该是这种格式的
            # 注意后面那两个是post_publish、post_authors~专门用于提交的字段
            """
             {
                "title": "西游记",
                "price": 12.20,
                "pub_date": "2019-12-22T10:10:11Z",
                "post_publish": 1,
                "post_authors": [1,2]
            }
            """
            print('validated_data>>>',validated_data)
            book_obj = models.Book.objects.create(
                title=validated_data.get('title'),
                price=validated_data.get('price'),
                pub_date=validated_data.get('pub_date'),
                publish_id=validated_data.get('post_publish'),
            )
            # 多对多插入数据~~基于对象的跨表查询
            # 注意用set方法存多对多关系的数据
            book_obj.authors.set(validated_data.get('post_authors'))
            return book_obj
    
        # PUT请求修改数据需要写的方法
        def update(self, instance, validated_data):
            # 如果取到了就用修改的~~如果没有就用原来的数据
            instance.title = validated_data.get('title',instance.title)
            instance.prince = validated_data.get('price',instance.price)
            instance.pub_date = validated_data.get('pub_date',instance.pub_date)
            # 上面设置了post_publish为write_only了~所以修改要用post_publish
            instance.publish_id = validated_data.get('post_publish',instance.publish_id)
            # 先save~然后再处理一下多对多关系的数据
            instance.save()
            # 基于对象的跨表查询~~注意用set方法存多对多关系的数据
            # 如果没有的话需要用all方法取出所有对象~~
            # # 上面设置了post_authors为write_only了~所以修改要用post_authors
            instance.authors.set(validated_data.get('post_authors',instance.authors.all()))
            # 最后记得把instance 返回
            return instance

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

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

    DRF自带的页面是这样的:

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

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

    测试路由如下

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

    视图函数如下

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

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

    # 展示 书籍列表
    def book_list(request):
        return render(request,'book_list.html')
    
    # 展示 添加书籍页面
    def add_book(request):
        return render(request,'add_book.html')
    
    # 编辑书籍的展示页面
    def edit_book(request,pk):
        return render(request,'edit_book.html')

    所有页面的母版

    {% load static %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{% block title %} {% endblock title %}</title>
        <link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.css' %}">
        <style>
            {# th中的文字剧中 bootstrap设置的是left #}
            th {
                text-align: center;
            }
        </style>
    </head>
    
    <body style="padding-top:52px;">
    <!--导航 独立于页面,不包含在盒子里面。不要放在container里面  -->
    <nav class="navbar navbar-default navbar-fixed-top navbar-inverse">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">火之国</a>
            </div>
    
            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse pull-right" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
    
                    <li><a href="#">Link</a></li>
                    <li><a href="#">Link</a></li>
    
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
    
    
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <div class="pannel panel-danger">
                    <!--panel-heading-->
                    <div class="panel-heading">
                        <!--panel-title-->
                        <h3 class="panel-title">火之国图书管理系统</h3>
                    </div>
                    <!--panel-body-->
                    <div class="panel-body">
                        <!--把其他的组件放到panel-body里面-->
                        <!--block -->
                        {% block pannel-body %}
    
    
                        {% endblock pannel-body %}
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    
    <script src="{% static 'jquery-3.4.1.js' %}"></script>
    <script src="{% static 'bootstrap-3.3.7/js/bootstrap.js' %}"></script>
    {% block script %}
    
    {% endblock script %}
    </body>
    </html>
    base.html

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

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

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

    {% extends 'base.html' %}
    
    {% block title %}
        主页
    {% endblock title %}
    
    
    {% block pannel-body %}
    
        {% csrf_token %}
        <a id="add_book" href="{% url 'add_book_view' %}" class="btn btn-success pull-right">添加书籍</a>
        <br><br>
        <div id="div_table" class="table-responsive" style="text-align: center">
            <table id="table" class="table table-striped table-bordered table-hover table-condensed">
                <thead>
                <tr class="success">
                    <th>编号</th>
                    <th>书籍名称</th>
                    <th>价格</th>
                    <th>出版日期</th>
                    <th>出版社</th>
                    <th>作者</th>
                    <th>操作</th>
                </tr>
                </thead>
                {# 委托的父级标签用tbody #}
                <tbody id="tbody">
    
    
                </tbody>
            </table>
        </div>
    
    
    
    {% endblock pannel-body %}
    
    {% block script %}
        <script>
    
            // 格式化时间的函数
            function formatDate(time) {
                var date = new Date(time);
    
                var year = date.getFullYear(),
                    month = date.getMonth() + 1,//月份是从0开始的
                    day = date.getDate(),
                    hour = date.getHours(),
                    min = date.getMinutes(),
                    sec = date.getSeconds();
                var newTime = year + '-' +
                    month + '-' +
                    day + ' ' +
                    hour + ':' +
                    min + ':' +
                    sec;
                return newTime;
            }
    
            // 页面加载自动触发ajax请求~向DRF获取所有数据并在前端渲染
            $(document).ready(function () {
                $.ajax({
                    url: '/books/',
                    type: 'get',
                    success: function (data) {
                        console.log(data, typeof (data));
                        // data是一个object
                        for (var i = 0; i < data.length; i++) {
                            // data[i]是一个个自定义对象
                            //console.log(data[i],typeof(data[i]));
    
                            var tr = document.createElement('tr');
                            var td_num = document.createElement('td');
                            // 提前把编号写进tr中去 注意同时将id也加进去
                            td_num.innerHTML = (i + 1) + '<span class="book_pk" style="display: none">' + data[i].id + '</span> </td>';
                            //这时tr的第一个元素就是一个个的编号——并且里面的span标签带着每个数据的id
                            tr.append(td_num);
    
                            for (var j in data[i]) {
                                //console.log(j);
                                // 不用填id字段
                                if (j === 'id') {
                                    continue;
                                }
                                // 新建一个td标签,把遍历的数据加进去
                                var td = document.createElement('td');
    
                                //格式化一下出版日期的格式
                                if (j === 'pub_date') {
                                    data[i][j] = formatDate(data[i][j]);
                                }
    
                                //展示出版社的名字
                                if (j === 'publish') {
                                    data[i][j] = data[i][j]['name'];
                                }
                                //展示作者的名字
                                if (j === 'authors') {
                                    //console.log(data[i][j]);
                                    var authors = '';
                                    for (var k in data[i][j]) {
                                        authors += data[i][j][k]['name'] + ' ';
                                    }
                                    data[i][j] = authors;
                                }
    
                                td.append(data[i][j]);
                                tr.append(td);
    
                            }
    
                            //循环完,最后把编辑与删除按钮添加进去
                            var tdd = document.createElement('td');
                            tdd.innerHTML = '<td><a class="btn btn-primary edit_book"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span><span>编辑</span>
    ' +
                                '</a><a class="btn btn-danger del_book"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span><span>删除</span></a></td>';
    
                            tr.append(tdd);
    
                            // 最后将tr加到tbody中
                            $('#tbody').append(tr);
    
                        }
                    }
                })
            });
    
            // 删除按钮的点击事件
            // 用委托实现
            // 这里也可以加一个"模态对话框"~给用户一个确认删除删除的提示
            $('#tbody').on('click', '.del_book', function () {
                // 找到这本书对应的id~
                // console.log($(this).parent().parent().find('.book_pk').text());
                var book_id = $(this).parent().parent().find('.book_pk').text();
                $.ajax({
                    url: '/book/' + book_id + '/',
                    type: 'delete',
                    success: function (data) {
                        location.href = '/book_list/';
                    }
                })
            });
    
            // 编辑按钮的点击事件
            // 用委托实现
            $('#tbody').on('click', '.edit_book', function () {
                // 找到这本书对应的id~
                var book_id = $(this).parent().parent().find('.book_pk').text();
                $.ajax({
                  url:'/book/'+book_id+'/',
                  type:'put',
                  success:function (data) {
                      // data是待编辑书籍的数据
                      console.log(data,typeof(data));
                      // 序列化数据
                      data_json = JSON.stringify(data);
                      // 将数据存到session中
                      sessionStorage.edit_book_data = data_json;
                      // 跳转到编辑书籍页面
                      location.href = '/edit_book_view/' + book_id +'/';
                  }
                })
            })
    
    
        </script>
    
    {% endblock script %}

    添加书籍页面

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

    {% extends 'base.html' %}
    
    {% block title %}
        主页
    {% endblock title %}
    
    
    {% block pannel-body %}
    
        <div class="col-md-8 col-md-offset-2">
            <h2 class="text-center">添加书籍</h2>
            <div>
                <div class="form-group">
                    <label for="book_name">书籍名称</label>
                    <input type="text" id="book_name" class="form-control" placeholder="书籍名称"
                           autocomplete="off">
                    <span class="help-block"></span>
                </div>
                <div class="form-group">
                    <label for="price">价格</label>
                    <input type="number" id="price" class="form-control" placeholder="价格"
                           autocomplete="off">
                    <span class="help-block"></span>
                </div>
                <div class="form-group">
                    <label for="pub_date">出版日期</label>
                    <input type="date" id="pub_date" class="form-control" placeholder="出版日期">
                    <span class="help-block"></span>
                </div>
                <div class="form-group">
                    <label for="">出版社</label>
                    <select id="publish" class="form-control">
                        <option value="1">苹果出版社</option>
                        <option value="2">西瓜出版社</option>
                    </select>
                </div>
                <div class="form-group">
                    <label for="">作者</label>
                    <select name="authors" id="authors" class="form-control" multiple>
                        <option value="1">whw</option>
                        <option value="2">naruto</option>
                        <option value="3">sasuke</option>
                    </select>
                </div>
                <div class="form-group">
                    <h4 id="add_error" class="pull-left" style="color:red;margin-top: 0"></h4>
                    <input id="confirm_add" type="button" class="btn btn-success pull-right" value="确认添加">
    
                </div>
            </div>
        </div>
    
    {% endblock pannel-body %}
    
    {% block script %}
        <script>
            {# 确认按钮 #}
            $('#confirm_add').click(function () {
                {#console.log(123123);#}
                var title = $('#book_name').val();
                var price = $('#price').val();
                var pub_date = $('#pub_date').val();
                // 下拉列表被选中的这样选取
                var publish = $('#publish option:selected').val();
    
                //ajax操作
                $.ajax({
                    url: '{% url "book_get_post" %}',
                    type: 'post',
    
                    data: {
                        title: title,
                        price: price,
                        pub_date: pub_date,
                        //pub_date: "2019-08-02T09:35:13.064532Z",
                        post_publish: publish,
                        //post_authors: authors,
                        post_authors: $('#authors').val(),
                    },
                    // 传数组
                    traditional: true,
    
                    success: function (data) {
                        console.log(data);
                        //alert('添加成功!');
                        location.href = '{% url "book_list" %}';
                    }
                })
    
            });
    
    
        </script>
    
    {% endblock script %}

    编辑书籍页面

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

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

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

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

    {% extends 'base.html' %}
    
    {% block title %}
        主页
    {% endblock title %}
    
    
    {% block pannel-body %}
    
        <div class="col-md-8 col-md-offset-2">
            <h2 class="text-center">编辑书籍</h2>
            <div>
                <div class="form-group">
                    <label for="book_name">书籍名称</label>
                    <input type="text" id="book_name" class="form-control" placeholder="书籍名称"
                           autocomplete="off">
                    <span class="help-block"></span>
                </div>
                <div class="form-group">
                    <label for="price">价格</label>
                    <input type="number" id="price" class="form-control" placeholder="价格"
                           autocomplete="off">
                    <span class="help-block"></span>
                </div>
                <div class="form-group">
                    <label for="pub_date">出版日期</label>
                    <input type="date" id="pub_date" class="form-control" placeholder="出版日期">
                    <span class="help-block"></span>
                </div>
                <div class="form-group">
                    <label for="">出版社</label>
                    <select id="publish" class="form-control">
                        <option value="1">苹果出版社</option>
                        <option value="2">西瓜出版社</option>
                    </select>
                </div>
                <div class="form-group">
                    <label for="">作者</label>
                    <select name="authors" id="authors" class="form-control" multiple>
                        <option value="1">whw</option>
                        <option value="2">naruto</option>
                        <option value="3">sasuke</option>
                    </select>
                </div>
                <div class="form-group">
                    <h4 id="add_error" class="pull-left" style="color:red;margin-top: 0"></h4>
                    <input id="confirm_add" type="button" class="btn btn-success pull-right" value="确认编辑">
    
                </div>
            </div>
        </div>
    
    {% endblock pannel-body %}
    
    {% block script %}
        <script>
    
    
            // 页面加载后将session中的数据写到上面的标签中
            $(document).ready(function () {
    
                // 获取session中的数据
                var data_session = sessionStorage['edit_book_data'];
                // 记得反序列化一下
                var data = JSON.parse(data_session);
                console.log(data, typeof (data));
                // 取出edit_book_id 把它设置为全局的变量!后面ajax提交的时候会用到
                edit_book_id = data['id'];
    
                // 将数据填在上面的input框中~注意是val方法!
                $('#book_name').val(data['title']);
                $('#price').val(data['price']);
                $('#pub_date').val(data['pub_date']);
                $('#publish').val(data['publish']['id']);
                // 让之前的作者名被选中
                var arr_val = [];
                for(var i in data['authors']){
                    //console.log(data['authors'][i]['id']);
                    arr_val.push(data['authors'][i]['id'])
                }
                // console.log(arr_val); [1,2]
                // 把数组传给复选框的val~让之前的作者被选中
                $('#authors').val(arr_val);
    
            });
    
    
            // 确认编辑按钮
            $('#confirm_add').click(function () {
                var title = $('#book_name').val();
                var price = $('#price').val();
                var pub_date = $('#pub_date').val();
                // 下拉列表被选中的这样选取
                var publish = $('#publish option:selected').val();
    
                //ajax操作
                $.ajax({
                    url: '/book/'+edit_book_id+'/',
                    type: 'put',
                    data: {
                        title: title,
                        price: price,
                        pub_date: pub_date,
                        //pub_date: "2019-08-02T09:35:13.064532Z",
                        post_publish: publish,
                        //post_authors: authors,
                        post_authors: $('#authors').val(),
                    },
                    // 传数组~
                    traditional: true,
    
                    success: function (data) {
                        console.log(data);
                        //alert('添加成功!');
                        location.href = '{% url "book_list" %}';
                    }
                })
    
            });
    
        </script>
    
    {% endblock script %}
  • 相关阅读:
    Frequency of String CodeForces
    Sign on Fence CodeForces
    洛谷 P3332 [ZJOI2013]K大数查询 || bzoj3110
    spoj DYNALCA
    洛谷 P2824 [HEOI2016/TJOI2016]排序 (线段树合并)
    洛谷 P3203 [HNOI2010]弹飞绵羊 || bzoj2002
    bzoj 1036: [ZJOI2008]树的统计Count
    Shiro Authenticator认证器
    Shiro 十分钟教程
    Shiro 架构
  • 原文地址:https://www.cnblogs.com/paulwhw/p/11255252.html
Copyright © 2020-2023  润新知