模块六 常规CRUD实现


常用CRUD

import json

from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, redirect

# Create your views here.
from django.contrib import auth
from app01.models import UserInfo, Book, Publish, Author
from app01.my_forms import UserForm


def login(request):
    """登录"""
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        # 认证
        user = auth.authenticate(username=user, password=pwd)

        if user:
            # request.user
            auth.login(request, user)
            next_url = request.GET.get("next", "/index/")
            return redirect(next_url)
        else:
            msg = "用户名或密码错误"

    return render(request, "login.html", locals())


def register(request):
    """注册"""
    if request.method == "POST":
        form = UserForm(request.POST)

        if form.is_valid():
            # 创建用户
            user = form.cleaned_data.get("user")
            pwd = form.cleaned_data.get("pwd")
            email = form.cleaned_data.get("email")
            telephone = form.cleaned_data.get("telephone")

            UserInfo.objects.create_user(username=user, password=pwd, email=email, telephone=telephone)

            return redirect("/login/")

        else:
            # 保留之前的输入
            print(form.cleaned_data)
            # 包含所有错误
            print(form.errors)
            errors = form.errors.get("__all__")

        return render(request, "register.html", {"form": form, "errors": errors})

    form = UserForm()

    return render(request, "register.html", {"form": form})


def logout(request):
    """注销"""
    auth.logout(request)
    return redirect("/login/")


@login_required
def add_book(request):
    """添加书籍"""
    if request.method == "POST":
        title = request.POST.get('title')
        price = request.POST.get("price")
        pub_date = request.POST.get("pub_date")
        publish_id = request.POST.get("publish_id")
        authors_id_list = request.POST.getlist("authors_id_list")

        book_obj = Book.objects.create(title=title, price=price, publishDate=pub_date,
                                       publish_id=publish_id)  # publish_obj
        book_obj.authors.add(*authors_id_list)

        return redirect('/index/')

    publish_list = Publish.objects.all()
    author_list = Author.objects.all()

    return render(request, "addbook.html", {"publish_list": publish_list, "author_list": author_list})


@login_required
def index(request):
    """books首页"""
    book_list = Book.objects.all()
    return render(request, "index.html", {"book_list": book_list})


@login_required
def delete_book(request, delete_book_id):
    """books删除"""
    Book.objects.filter(pk=delete_book_id).delete()

    return redirect("/index/")


@login_required
def change_book(request, change_book_id):
    """更新books信息"""
    print("change_book_id==>", change_book_id)
    edit_book_obj = Book.objects.filter(pk=change_book_id).first()

    if request.method == "POST":
        print("request.POST", request.POST)
        title = request.POST.get('title')
        price = request.POST.get("price")
        pub_date = request.POST.get("pub_date")
        publish_id = request.POST.get("publish_id")
        authors_id_list = request.POST.getlist("authors_id_list")

        Book.objects.filter(pk=change_book_id).update(title=title, price=price, publishDate=pub_date,
                                                      publish_id=publish_id)
        edit_book_obj.authors.set(authors_id_list)
        # edit_book_obj.authors.clear()

        return redirect("/index/")

    publish_list = Publish.objects.all()
    author_list = Author.objects.all()

    return render(request, "editbook.html",
                  {"edit_book_obj": edit_book_obj, "publish_list": publish_list, "author_list": author_list})


@login_required
def query_pub(request, query_pub_id):
    """查看出版社下的books"""
    # 方法1
    # publish_obj = Publish.objects.filter(nid=query_pub_id).first()
    # book_list = publish_obj.book_set.all()

    book_list = Book.objects.filter(publish_id=query_pub_id).all()

    return render(request, "querypub.html", {"book_list": book_list})


@login_required
def query_author(request, query_author_id):
    """查看作者下的所有书籍"""
    author_obj = Author.objects.filter(nid=query_author_id).first()
    book_list = author_obj.book_set.all()

    return render(request, "queryauthor.html", {"book_list": book_list})


def ajax_change_book(request):
    """ajax返回填充值"""
    if request.method == "POST":
        edit_book_id = request.POST.get('book_id')
        edit_book_obj = Book.objects.filter(nid=edit_book_id).first()
        publish_list = list(Publish.objects.values())
        author_list = list(Author.objects.values())
        print(author_list)  # QuerySet [<Author: alex>, <Author: egon>, <Author: yuan>]>

        ret = {}
        ret['price'] = edit_book_obj.price
        ret['pub_date'] = edit_book_obj.publishDate
        ret['title'] = edit_book_obj.title
        ret['publish'] = edit_book_obj.publish_id
        ret['author'] = list(edit_book_obj.authors.values())
        ret["publish_list"] = publish_list
        ret["author_list"] = author_list
        # print(ret)
        # ret['author'] = list(edit_book_obj.authors.all())
        # author_dict = {}
        # for item in edit_book_obj.authors.all():
        #     print(item.nid,item.name)

        return JsonResponse(ret)

    return HttpResponse("ok")


def ajax_book_save(request):
    """ajax保存更新,判断书籍重名"""
    if request.method == "POST":
        ret = {"status": None, "msg": None}
        title = request.POST.get("title")
        price = request.POST.get("price")
        pub_date = request.POST.get("pub_date")
        publish_id = request.POST.get("publish_id")
        authors_id_list = request.POST.getlist("author[]")
        book_id = request.POST.get("book_id")

        try:
            Book_list = list(Book.objects.values())
            Book_title_list = []
            for bookobj in Book_list:
                Book_title_list.append(bookobj['title'])

            if title in Book_title_list:
                ret["msg"] = "书名相同,请修改"
                ret["status"] = False
            else:
                edit_book_obj = Book.objects.filter(pk=book_id).first()
                Book.objects.filter(pk=book_id).update(title=title, price=price, publishDate=pub_date,
                                                       publish_id=publish_id)
                edit_book_obj.authors.set(authors_id_list)
                ret['status'] = True
        except Exception as e:
            ret["msg"] = e
            ret['status'] = False

        return JsonResponse(ret)

    return HttpResponse("ok")

通过form实现

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:lixiang
from django import forms
from django.core.exceptions import ValidationError
from django.forms import widgets

from app01.models import UserInfo


class UserForm(forms.Form):
    user = forms.CharField(max_length=32, min_length=4, error_messages={"required": "该字段不能为空"}, label="用户名",
                           widget=widgets.TextInput(attrs={"class": "form-control"}))

    pwd = forms.CharField(max_length=16, min_length=4, error_messages={"required": "该字段不能为空"}, label="密码",
                          widget=widgets.PasswordInput(attrs={"class": "form-control"}))

    r_pwd = forms.CharField(max_length=16, min_length=4, error_messages={"required": "该字段不能为空"}, label="确认密码",
                            widget=widgets.PasswordInput(attrs={"class": "form-control"}))

    email = forms.EmailField(max_length=32, error_messages={"required": "该字段不能为空"}, label="邮箱",
                             widget=widgets.EmailInput(attrs={"class": "form-control"}))

    telephone = forms.IntegerField(error_messages={"required": "该字段不能为空"}, label="手机号",
                                   widget=widgets.TextInput(attrs={"class": "form-control"}))

    def clean_user(self):
        username = self.cleaned_data.get("user")
        user = UserInfo.objects.filter(username=username).first()
        if not user:
            return username
        else:
            raise ValidationError('改用户已注册!')

    def clean_telephone(self):

        value = self.cleaned_data.get("telephone")
        if len(str(value)) == 11:
            return value
        else:
            raise ValidationError("手机号格式错误")

    def clean(self):
        pwd = self.cleaned_data.get("pwd")
        r_pwd = self.cleaned_data.get("r_pwd")
        if pwd and r_pwd:
            if pwd == r_pwd:
                return self.cleaned_data
            else:
                raise ValidationError("两次密码不一致!")

        else:
            return self.cleaned_data

模板

通过form提交实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bs/css/bootstrap.css">
</head>
<body>

<div class="container">
    <h3 style="text-align: center">添加书籍</h3>
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form action="" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <label for="">名称</label>
                    <input type="text" name="title" class="form-control" value="">
                </div>
                <div class="form-group">
                    <label for="">价格</label>
                    <input type="text" name="price" class="form-control" value="">
                </div>
                <div class="form-group">
                    <label for="">出版日期</label>
                    <input type="date" name="pub_date" class="form-control" value="">
                </div>
                <div class="form-group">
                    <label for="">出版社</label>
                    <select name="publish_id" id="" class="form-control">
                        {% for publish in publish_list %}
                            <option value="{{ publish.pk }}">{{ publish.name }}</option>
                        {% endfor %}

                    </select>

                </div>
                <div class="form-group">
                    <label for="">作者</label>
                    <select multiple name="authors_id_list" id="" class="form-control">
                        {% for author in author_list %}
                        <option value="{{ author.pk }}">{{ author.name }}</option>
                        {% endfor %}
                    </select>

                </div>

                <input type="submit" class="btn btn-primary">


            </form>
        </div>
    </div>
</div>


</body>
</html>

这里有个点要注意:

  • 获取表单的值 form会自动传递value,ajax不会,可以通过后端返回data 填充到input中的value
  • 通过jQuery来实现,获取对应的值并填充到input框
{% extends 'base.html' %}

{% block title %}
    <title>index</title>
{% endblock %}

{% block booklist %}

    {% for book in book_list %}
        <tr name="{{ book.pk }}">

            <td>{{ forloop.counter }}</td>
            <td>{{ book.title }}</td>
            <td>{{ book.price }}</td>
            <td>{{ book.publishDate|date:"Y-m-d" }}</td>
            <td><a href="/query/{{ book.publish.nid }}/pub/">{{ book.publish.name }}</a></td>
            <td>{% for author in book.authors.all %}
                {% if forloop.last %}
                    <a href="/query/{{ author.nid }}/author/"><span>{{ author.name }}</span></a>
                {% else %}
                    <a href="/query/{{ author.nid }}/author/"><span>{{ author.name }}</span>,</a>
                {% endif %}

            {% endfor %}
            </td>

            <td>

                <a href="/books/{{ book.pk }}/change/" id="nid" class="btn btn-warning" name="{{ book.pk }}">编辑</a>
                {#        <button type="button" class="btn btn-warning change" clickvalue="{{ book.pk }}">修改 <input type="text" id="bookid" style="display: none" value="{{ book.pk }}"></button>#}
                <a href="/books/{{ book.pk }}/delete/" class="btn btn-danger">删除</a>


                <!-- Button trigger modal -->
                <button type="button" class="btn btn-primary btn-lg change" data-toggle="modal" data-target="#myModal">
                    ajax修改
                </button>

                <!-- Modal -->
                <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
                    <div class="modal-dialog" role="document">
                        <div class="modal-content">
                            <div class="modal-header">
                                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
                                        aria-hidden="true">&times;</span></button>
                                <h4 class="modal-title" id="myModalLabel">修改信息</h4>
                            </div>
                            <div class="modal-body">
                                <form action="" method="post" id="tijiao">
                                    {% csrf_token %}
                                    <div class="form-group">
                                        <label for="">名称</label>
                                        <input type="text" name="title" id="title" class="form-control"><span
                                            id="msgerror"></span>
                                    </div>
                                    <div class="form-group">
                                        <label for="">价格</label>
                                        <input type="text" name="price" class="form-control" id="price">
                                    </div>
                                    <div class="form-group">
                                        <label for="">出版日期</label>
                                        <input type="date" name="pub_date" class="form-control" id="pub_date">
                                    </div>
                                    <div class="form-group">
                                        <label for="">出版社</label>
                                        <select name="publish_id" id="publish_id" class="form-control">
                                            {#                        <option selected ></option>#}

                                        </select>

                                    </div>
                                    <div class="form-group">
                                        <label for="">作者</label>
                                        <select multiple name="authors_id_list" id="author" class="form-control">

                                            {#                        <option class="selectot2" selected></option>#}

                                        </select>

                                    </div>

                                    <input type="submit" value="form提交" class="btn btn-primary">


                                </form>
                            </div>
                            <div class="modal-footer">
                                <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
                                <button type="button" class="btn btn-primary" id="save">ajax提交</button>
                            </div>
                        </div>
                    </div>
                </div>

            </td>

        </tr>

    {% endfor %}



    <script src="/static/bs/js/bootstrap.js"></script>
    <script src="/static/js/jquery-3.2.1.min.js"></script>
    <script>


        $(".change").click(function () {

            window.bookId = $(this).parent().parent().attr("name");
            var newUrl = '/books/' + bookId + '/change/';
            $("#tijiao").attr('action', newUrl);
            $("#msgerror").text("");
            {#$("#tijiao").submit();#}

            $.ajax({
                url: "/books/edit/",
                type: "post",
                data: {
                    book_id: $(this).parent().parent().attr("name"),
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
                },
                success: function (data) {
                    console.log(data);
                    $("#title").val(data.title);
                    $("#price").val(data.price);
                    $("#pub_date").val(data.pub_date);

                    $("#publish_id").empty();
                    $.each(data.publish_list, function (pubidx, pubobj) {
                        if (pubobj.nid === data.publish) {

                            option_pub = "<option selected value=" + pubobj.nid + ">" + pubobj.name + "</option>";
                        } else {
                            option_pub = "<option value=" + pubobj.nid + ">" + pubobj.name + "</option>";

                        }
                        $("#publish_id").append(option_pub);

                    });

                    $("#author").empty();
                    $.each(data.author_list, function (idx, obj) {
                        $.each(data.author, function (auidx, auobj) {
                            if (obj.nid === auobj.nid) {

                                option_str = "<option selected value=" + obj.nid + ">" + obj.name + "</option>";
                                return false; // 终止循环

                            } else {
                                option_str = "<option value=" + obj.nid + ">" + obj.name + "</option>";


                            }


                        })

                        $("#author").append(option_str);

                    })


                }
            })

        })

        $("#save").click(function () {

            $.ajax({
                url: "/books/save/",
                type: "post",
                data: {
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
                    book_id: bookId,
                    title: $("#title").val(),
                    price: $("#price").val(),
                    pub_date: $("#pub_date").val(),
                    publish_id: $("#publish_id").val(),
                    author: $("#author").val(),

                },
                success: function (data) {

                    if (data.status) {

                        window.location.href = "/index/";

                    } else {
                        {#console.log(data.msg);#}
                        $("#msgerror").text(data.msg).css({"color": "red"});


                    }

                }

            })

        })


    </script>



{% endblock booklist %}
{% extends 'master/layout.html' %}

{% block css %}
    <style>
        .part4{

            background-color:#f6f6f6;
        }


    /*body
    {
        font-size: .85em;
        font-family: "Trebuchet MS",Verdana,Helvetica,Sans-serif;
        color: #232323;
        background-color: #fff;
    }*/

    #Pageing A{ background-color:#008844;
                border:1px solid White;
                text-decoration:none; color:White;
                padding:.1em .6em .2em .6em;
                }
    #Pageing A.selected{ background-color:#AA7700;}

    </style>



{% endblock %}

{% block body %}


<div class='w clearfix'>
    <div class='left bbs'>
        <div class='title'>
            <a href='/publish/' class='publish'>添加</a>
        </div>
        <div class='content'>

            {% for item in data %}
                <div class='item'>
                    <div class='part1'>
                        <a href='{{item.url}}'>{{item.title}}</a>
<!--                        <span> -- {{item.news_type.display}}</span>-->
                    </div>
                    <div class='part2'>
                        {{item.summary}}
                    </div>
                    <div class='part3'>
                        <a href='#' onclick='Favor(this,{{item.id}});'>赞{{item.favor_count}}</a>
                        <!--<a href='#' onclick='Reply("#reply_detail",{{item.id}});'>评论{{item.reply_count}}</a>-->
                        <a class='reply' href='#' func='Reply' onclick='Reply(this,{{item.id}});'>评论{{item.reply_count}}</a>
                        <span>{{ item.create_date|date:"Y-m-d H:i:s"}}</span>
                    </div>
                    <!--<div id='reply_detail' has-input='0' class='part4 hide'>-->
                    <div class='part4 hide'>
                        <div class='replys'></div>
                        <div class='input huifu-top-box' style="display: block;"><!--<label>请输入评论内容</label>-->
                            <textarea lang="8115590" maxlength="150" name="txt-huifu-top"  class="txt-huifu txt-huifu-top" style="text-indent: 0px; height: 20px; resize: none;"></textarea>
                            <input lang="8114372" class="pub-icons add-pub-btn add-pub-btn-unvalid" type='button' value='评论' onclick='Submit(this,{{item.id}});' />
                        </div>
                    </div>

                </div>
            {% endfor %}

        </div>

        <div class='bottom'>
            <div class='custom-paging'>
                {{pager}}
            </div>      

        </div>

    </div>

    <div class='right chat'>
        <div class='title'>
            新热榜 v0.1
        </div>
        <div id='chatpool' class='content'>

        </div>
        <div class='bottom clearfix'>
            <div class='left msg'>
                <textarea id='message' class='text'></textarea>
            </div>
            <div class='left submit'>
                <input type='button' class='btn' onclick='SendMsg();' value='发送'>           
            </div>
        </div>


    </div>

</div>

<!--遮罩层开始-->
<div id='shade' class='shade hide'></div>
<!--遮罩层结束-->
<!--加载层开始-->
<div id='loading' class='loading hide'></div>
<!--加载层结束-->


<!--分页-->
<!--Font-Awesome使用-->
<div class='w clearfix'><i class="icon-camera-retro"></i> icon-camera-retro</div>

<div class='w clearfix'>总条数:{{count}}</div>

    <div class='w clearfix' id='Pageing'>

        {{ page }}

    </div>


    <div class='w clearfix'>
        <select id='s1' onchange='ChangePageItem(this);'>
            <option value='5'>5</option>
            <option value='10'>10</option>
            <option value='15'>15</option>
            <option value='20'>20</option>
            <option value='30'>30</option>

        </select>

    </div>



{% endblock %}



{% block js %}

    <script type='text/javascript'>

        //点赞     onclick触发Ajax请求
        function Favor(doc,id){
            //后台数据点赞+1
            //$(doc).text('赞10');

            $.ajax({
                url:'/addfavor/',
                data:{nid:id}, // {nid,id} nid not define.
                type:'POST',
                success:function(callback){
                    var obj = jQuery.parseJSON(callback);
                    //obj是一个json对象,通过obj.key可以获取到value值,而不需要像处理字符串分割一样来获取对应的value值
                    // json反格式化,显示字典格式,其实是字符串,只能通过字符串分割才能获取到json中的值,使用parseJSON后可以像使用字典一样通过key获取value值
                    if(obj.status==1){
                        var temp = '赞' + obj.data;
                        $(doc).text(temp);

                    }else{
                        alert(obj.message);
                    }
                }   
            });
        }

        //评论弹窗,只显示一个弹窗
        function Reply(doc,id){
            // $(doc).parent().next()
            $.ajax({
                url:'/getreply/',
                data:{nid:id},
                type:'POST',
                success:function(callback){
                    // console.log(callback);
                    var obj = jQuery.parseJSON(callback);
                    $(doc).parent().next().find('.replys').empty();
                    $.each(obj,function(k,v){
                        // $(doc).parent().next().first().text('000000');
                        // temp = "<div>"+v.fields.content+"</div>";
                        temp = "<div class='corner comment-box'>"+v.user__username+":"+v.content+" ---- "+v.create_date+"</div>";
                        //$(doc).parent().next().find('.replys').empty()
                        $(doc).parent().next().find('.replys').append(temp);


                    });

                }

            });

            $(doc).parent().next().toggleClass('hide');

            //$(content).removeClass('hide');
            //if(如果没有就创建输入框)
            //方法二:直接在.part4中添加评论窗口标签来实现,使用toggleClass属性点击隐藏和显示,比方法一更简便,也不需要做判断
            //$(content).toggleClass('hide');
            /* 方法一:使用js显示评论弹窗,避免显示多个窗口在html设置自定义属性'has-input'来作为判断条件,只创建一个评论窗口,通过js的方式将标签append到'.part4'中
            console.log($(content).attr('has-input'));
            if($(content).attr('has-input')==0){
                $(content).append("<label>请输入回复内容:</label><textarea></textarea>")
                $(content).attr('has-input',1);
                }
            */



        }


        function Submit(doc,id){

            var value = $(doc).prev().val();
            $(doc).prev().val('');
            $("#shade,#loading").removeClass('hide');

            $.ajax({
                url:'/submitreply/',
                data:{nid:id,data:value},
                type:'POST',
                success:function(callback){
                    console.log(callback);
                    callback = jQuery.parseJSON(callback);
                    //console.log(callback);
                    if(callback.status==1){
                        //把数据append到回复列表
                        temp = "<div>"+callback.data.user__username+":"+callback.data.content+" ---- "+callback.data.create_date+"</div>";
                        $(doc).parent().prev().append(temp);

                        count = '评论' + callback.data.reply_count;
                        $(doc).parent().parent().prev().find('.reply').text(count)

                    }else{
                        alert('操作失败');
                    }
                    $("#shade,#loading").addClass('hide');              
                } 

            });

        }

        window.onkeyup = function(event){
            if(event && event.keyCode == 13){

            }

        }

        //显示一个用户的聊天数据
        function SendMsg(){
            var value = $('#message').val();
            $('#message').val('');

            $.ajax({
                url:'/submitchat/',
                data:{data:value},
                type:'POST',
                success:function(callback){ //插入数据库,但是不在前端显示插入的数据,跟下面动态显示聊天数据重复了,所以存在显示2次这个问题
                    //console.log(callback);
                    var callback = jQuery.parseJSON(callback);
                    if(callback.status==1){
                        var now = callback.data.create_date;
                        var name = callback.data.username;
                        var template = "<div><div>"+name+"----"+now+"</div><div>"+value+"</div></div>";
                        $('#chatpool').append(template); //方法一:注释这句避免发送重复显示聊天数据,缺点就是可能要等待2s后才能看到发送的数据,其他用户无影响,都是要等待2s后才能看到聊天数据
                        window.last_id = callback.data.id; //方法二:全局变量,自己发送聊天数据后重置window.last_id,下面还会动态显示,让自己发的聊天数据不在重复显示
                        var height = document.getElementById("chatpool").scrollHeight;
                        $("#chatpool").scrollTop(height)
                    }else{
                        alert('请求异常');
                    }
                }

            });

            /*var now = '2016-03-16 17:45';
            var name = 'lixiang';
            var template = "<div><div>"+name+" --"+now+"</div><div>"+value+"</div></div>";
            $('#chatpool').append(template);*/
        }

        // 多用户交互聊天,其他用户也可以看到所有的聊天记录,用户第一次进来显示最新的10条数据,往后就动态刷新获取所有用户最新的聊天数据

        setInterval('going()',2000); //定时,每2s执行一次going()

        window.is_first = true; //第一次进来取最新的10条数据,否则一直在里面就每2s显示所有用户的最新聊天数据,而不是不变的10条数据,动态的显示最新的数据

        //window.i = 1

        function going(){
            //i = 1;
            //window.i = window.i + 1;
            //console.log(window.i);
            //i = i + 1;

            //第一次进来显示最新的前10条数据
            if(window.is_first){
                $.ajax({
                    url:'/getchart/',
                    type:'POST',
                    success:function(callback){
                        //console.log(callback);
                        callback = jQuery.parseJSON(callback);
                        window.last_id = callback[0].id; //设置last_id为最新的id数据, json格式:[{},{},{}],使用'.'方法获取数据
                        console.log(last_id);
                        callback = callback.reverse();
                        $.each(callback,function(k,v){ //k是v对象的索引,v才是真正的数据{'xx':'xx'}
                            var now = v.create_date; //由于是json格式,所以使用json对象获取字典中的值方法'v.xx'                          
                            var name = v.user__username;
                            var value = v.content;
                            var template = "<div><div>"+name+"---"+now+"</div><div>"+value+"</div></div>";
                            $('#chatpool').append(template);

                        });
                        window.is_first = false;
                        var height = document.getElementById("chatpool").scrollHeight;
                        $("#chatpool").scrollTop(height)
                    }

                });
            }else{
                //实时显示所有用户聊天的最新数据,如果有其他用户发送聊天数据,动态的显示出来
                $.ajax({
                    url:'/getchart2/',
                    data:{lastid:window.last_id},//在其他用户没有发送新数据的时候,last_id是最新的,返回[],此时如果有用户发送数据后,就会执行下面执行if下面的操作
                    type:'POST',
                    success:function(callback){
                        console.log(callback);
                        callback = jQuery.parseJSON(callback);
                        //window.last_id = callback[0].id;
                        if(callback.length > 0){
                            window.last_id = callback[callback.length-1].id;//正序,获取callback长度如果是10,就是0-9,最后一个值就是10-1=9,callback[9].id
                            $.each(callback,function(k,v){  // 从后台获取聊天数据并显示
                                var now = v.create_date;                            
                                var name = v.user__username;
                                var value = v.content;
                                var template = "<div><div>"+name+"---"+now+"</div><div>"+value+"</div></div>";
                                $('#chatpool').append(template);

                        });


                        }
                        var height = document.getElementById("chatpool").scrollHeight;
                        $("#chatpool").scrollTop(height)



                    }


                });

            }

        }



    $(function(){

            var per_item = $.cookie("pager_num");
            if(per_item){
                $('#s1').val(per_item);
                //console.log(per_item);

            }else{
                $.cookie("pager_num",10,{ path:'/' });
            }


        })


        function ChangePageItem(arg){
            //创建或者修改cookies的值
            var value = $(arg).val();
            console.log(value);
            $.cookie("pager_num",value,{ path : '/' });


        }


    </script>

{% endblock %}

模态框

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bs/css/bootstrap.css">

</head>
<body>


<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
    修改
</button>

<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
                </button>
                <h4 class="modal-title" id="myModalLabel">修改信息</h4>
            </div>
            <div class="modal-body">
                <form action="" method="post">
                    {% csrf_token %}
                    <div class="form-group">
                        <label for="">名称</label>
                        <input type="text" name="title" class="form-control" value="{{ edit_book_obj.title }}">
                    </div>
                    <div class="form-group">
                        <label for="">价格</label>
                        <input type="text" name="price" class="form-control" value="{{ edit_book_obj.price }}">
                    </div>
                    <div class="form-group">
                        <label for="">出版日期</label>
                        <input type="date" name="pub_date" class="form-control"
                               value="{{ edit_book_obj.publishDate|date:'Y-m-d' }}">
                    </div>
                    <div class="form-group">
                        <label for="">出版社</label>
                        <select name="publish_id" id="" class="form-control">
                            {% for publish in publish_list %}
                                {% if edit_book_obj.publish == publish %}
                                    <option selected value="{{ publish.pk }}">{{ publish.name }}</option>
                                {% else %}
                                    <option value="{{ publish.pk }}">{{ publish.name }}</option>
                                {% endif %}
                            {% endfor %}

                        </select>

                    </div>
                    <div class="form-group">
                        <label for="">作者</label>
                        <select multiple name="authors_id_list" id="" class="form-control">
                            {% for author in author_list %}
                                {% if author in edit_book_obj.authors.all %}
                                    <option selected value="{{ author.pk }}">{{ author.name }}</option>
                                {% else %}
                                    <option value="{{ author.pk }}">{{ author.name }}</option>
                                {% endif %}
                            {% endfor %}
                        </select>

                    </div>

                    <input type="submit" class="btn btn-primary">


                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                <button type="button" class="btn btn-primary">Save changes</button>
            </div>
        </div>
    </div>
</div>


<script src="/static/js/jquery-3.2.1.min.js"></script>
<script src="/static/bs/js/bootstrap.js"></script>

</body>
</html>

登录

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bs/css/bootstrap.css">
</head>
<body>


<div class="container">
    <div class="row">
        <h3 style="text-align: center">登录</h3>
        <div class="col-md-6 col-md-offset-3">
            <form action="" method="post">
                {% csrf_token %}
                <div>
                    <label for="user">用户名</label>
                    <input type="text" id="user" name="user" class="form-control">
                </div>

                <div>
                    <label for="pwd">密码</label>
                    <input type="password" id="pwd" name="pwd" class="form-control">
                </div>

                <input type="submit" class="btn btn-success"><span class="error"
                                                                   style="margin-left: 50px;color: red">{{ msg }}</span>
                <a href="/register/" class="btn btn-default pull-right">注册</a>


            </form>
        </div>
    </div>
</div>


</body>
</html>

注册

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bs/css/bootstrap.css">

</head>
<body>

<div class="container">
    <div class="row">
        <h3 class="pull-center" style="text-align: center">用户注册</h3>
        <div class="col-md-6 col-md-offset-3">
            <form action="" method="post" novalidate>
                {% csrf_token %}
                <p>{{ form.user.label }}
                    {{ form.user }} <span class="pull-right error">{{ form.user.errors.0 }}</span>
                </p>
                <p>{{ form.pwd.label }}
                    {{ form.pwd }} <span class="pull-right error">{{ form.pwd.errors.0 }}</span>
                </p>
                <p>{{ form.r_pwd.label }}
                    {{ form.r_pwd }} <span class="pull-right error">{{ form.r_pwd.errors.0 }}</span><span
                            class="pull-right error">{{ errors.0 }}</span>
                </p>
                <p>{{ form.email.label }}
                    {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span>
                </p>
                <p>{{ form.telephone.label }}
                    {{ form.telephone }} <span class="pull-right error">{{ form.telephone.errors.0 }}</span>
                </p>

                <input type="submit" class="pull-right">

            </form>
        </div>
    </div>
</div>


</body>
</html>

model

from django.contrib.auth.models import AbstractUser
from django.db import models


# Create your models here.


class UserInfo(AbstractUser):
    """用户信息 关联auth_User表"""
    nid = models.AutoField(primary_key=True)
    telephone = models.CharField(max_length=11, null=True, unique=True)
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)

    def __str__(self):
        return self.username


class Book(models.Model):
    """书籍"""
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=6, decimal_places=2)
    publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.CASCADE)
    authors = models.ManyToManyField(to="Author", )

    def __str__(self):
        return self.title


class Publish(models.Model):
    """出版社"""
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Author(models.Model):
    """作者"""
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)

    def __str__(self):
        return self.name


class AuthorDetail(models.Model):
    """作者详细"""
    nid = models.AutoField(primary_key=True)
    birthday = models.DateField()
    telephone = models.BigIntegerField()
    addr = models.CharField(max_length=32)

关于前端使用form和ajax补充

ajax 登录注册 ==  页面不刷新 == 登录成功跳转 location.href="/index/"
| -- ajax 2种填充input中的value方式
| -- 通过form的value实现  == 实际验证不行,必须要后端传递数据,不然永远只显示for循环第一次获取的值
| -- 通过jQuery实现 获取标签$(this) 
| --  1、通过jQuery获取$(this)标签 填充实现     待定
| --  2、通过ajax提交数据后端返回填充实现     已实现 只是比较复杂,需要发送一次请求

form 登录注册 == 跳转刷新页面 
| -- 会自动填充input中的value  value={{ book_obj.title }} 填充之前值   需要新页面  模态框无法使用
| -- 渲染templates 3种方式  (常用第一种) as_p   输入验证  

有什么方式获取到id 并且不被下面覆盖,a标签生成静态id,在渲染的时候已经确定了id号
$(this) 获取
通过form实现  赋值  == 实现不了,同上每次只会显示第一次循环获取的值   {{ obj.title }}   需要重新渲染 
TypeError: Object of type Book is not JSON serializable    == 要转换为list() 通过values()进行转换  对于多的才可以,publish只有一个不能转换,通过publish_id直接获取  
通过jQuery实现  赋值    
| -- 上面2种方式实现    实际采用方式2实现
登录注册流程图:注意不管是form还是ajax提交后端判断逻辑都是一样,只是ajax data不刷新显示数据,form是errors刷新显示错误数据, 这个就是后端验证常用方式,就2种,如果要想实现输入判断效果,就要用前端验证,而不是ajax对每一个输入框做判断,上面方式1,基本没有这样做的,常用采用js或框架都输入判断,显示效果,减少后端请求,之后提交发送所有数据,统一给后端对输入所有数据进行校验,成功ajax返回success or form 跳转  ,否则就通过form or ajax data返回数据渲染错误信息到页面,目前做的都是后端验证,前端后期有空可以研究加下,不影响功能,只是用户体现更好(方式2没有过多的ajax请求)

01

几个问题:
1、登录验证 前端显示效果问题,结合后端验证,目前做的基本够用,ajax主流,统一验证,只做了后端验证,前端后期再研究
2、ORM 不创建外键  ==  测试django 2.2 2.0.1   mysql 5.6 都不会创建索引,更换成mysql5.7 django2.2 能够正常创建
3、更换django2.2 python3 manage.py makemigrations 报错解决  具体如上

注册登录简单总结:
简单就form实现
通常就用ajax去实现
后端代码基本一致,其他没什么要补充的,前面总结的很好,唯一一点就是CRUD的第一个问题,是实现了ajax获取数据返回input渲染,有点麻烦,通过jQuery直接去获取属性值可能更简单一点,后面有类似需要可以通过jQuery来实现下,减少一次ajax请求,最后提交的时候才用ajax提交到后端
至于之前纠结的效果,那个是前端实现的,每个input框实时验证,直接js判断并且返回结果页面展示,体验更好,后端做一个统一判断即可(填完后统一提交一次,对所有数据进行验证即可),之前考虑的ajax判断替换成前端判断(增加用户体验,减少请求)
form使用步骤
1创建myform文件定义class userinfo
2在view中实例化form对象 form = userinfo()   form传到模板中使用 渲染便签  == 三种渲染方式 
3注意form中添加 action="" method="post" novalidate   
4views中采用 userinfo(request.POST)来进行验证       form.is_valid() 判断输入是否正确  

auth的使用步骤
1在model中创建 Userinfo(AbstractUser)  继承 User表
2认证用户名和密码 userobj = auth.authenticate(username=user, password=pwd)
3登录获取user用户 auth.login(request, userobj)  全局可以获取request.userobj    userobj.username     当前登录对象
4在settings中配置/login/  配置next_url  
5装饰器 @login_required  会自动跳转next  http://127.0.0.1:8000/login/?next=/cn_backend/
6获取并跳转对应页面  
1form  views  request.GET.get("next","/index/")     redirect(next_url)    
2)  ajax  Jsonresponse() == location.href = /index/   else   location.search.slice(6)

request.user.is_authenticated()  判断认证是否通过
模板自定义标签 or 过滤器步骤
1在settings中的INSTALLED_APPS配置当前app不然django无法找到自定义的simple_tag.
2创建templatetags目录  并创建自定义的py模块 
1 导入register=template.Library()
2 @register.simple_tag   # 标签 
def multi_tag(x,y):
    return x*y
案例 
@register.inclusion_tag("classification.html")
def get_classification_style(username):

    user = models.UserInfo.objects.filter(username=username).first()
    blog = user.blog

    cate_list=models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list("title","c")

    tag_list=models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title","c")

    date_list=models.Article.objects.filter(user=user).extra(select={"y_m_date":"date_format(create_time,'%%Y/%%m')"}).values("y_m_date").annotate(c=Count("nid")).values_list("y_m_date","c")


    return {"blog":blog,"cate_list":cate_list,"date_list":date_list,"tag_list":tag_list}

3在模板中引用首先加载{% load my_tags %}
4模板中使用 
过滤器  <p>{{ i|muti_filter:20 }}</p>
标签  {% get_classification_style username %}
注意filter可以用在if等语句后simple_tag不可以
模板继承
url path 自定义匹配模式步骤
path转换器 
1在文件中定义
regex 类属性字符串类型
to_python(self, value) 方法value是由类属性 regex 所匹配到的字符串返回具体的Python变量值以供Django传递到对应的视图函数中
to_url(self, value) 方法 to_python 相反value是一个具体的Python变量值返回其字符串通常用于url反向引用
案例
class FourDigitYearConverter:  
    regex = '[0-9]{4}'  
    def to_python(self, value):  
        return int(value)  
    def to_url(self, value):  
        return '%04d' % value  


2在register_converter将其注册到URL配置中
from django.urls import register_converter, path  
from . import converters, views  
register_converter(converters.FourDigitYearConverter, 'yyyy')  
urlpatterns = [  
    path('articles/2003/', views.special_case_2003),  
    path('articles/<yyyy:year>/', views.year_archive),  
    ...  
]

上传文件media配置步骤: 1、在setting中添加

# 用户上传
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = "/media/"

2、在url中添加
# media配置:
re_path(r"media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT}),

可以访问media目录下的文件,跟访问static下面文件一样
3、在本地创建media文件夹 == avatars  保存用户上传 并 提供访问
补充
注册上传图像
file_path_name = os.path.join(settings.MEDIA_ROOT, "avatars", avatar_obj.name)
with open(file_path_name, 'wb') as f:
    for line in avatar_obj:
        f.write(line)
extra = {}
if avatar_obj:
    extra["avatar"] = avatar_obj
Userinfo.objects.create_user(username=user, password=pwd, email=email, telephone=telephone, **extra)

注意对应model写法
avatar = models.FileField(upload_to='avatars/', default="avatars/default.png")

在templates中添加使用模板渲染: 在 setting 中的 TEMPLATES 下的 OPTIONS 中的 context_processors 中追加:

TEMPLATES = [
    {
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        ......
        'OPTIONS': {
            'context_processors': [
                ......
                'django.template.context_processors.media',  #  django 2
                # ('django.core.context_processors.media'  # django1.x 版本)
            ],
        },
    },
]

 View Code 
  此时 就可以在 templates下的 html 模板中使用 {{ MEDIA_URL }}{{ book.image }} 自动生成 相应链接  http://127.0.0.1:8000/media/image/2019/02/10489s.jpg

常用的功能代码:

预览图片
// 头像预览
$("#avatar").change(function () {
    // 获取用户选中的文件对象
    var file_obj = $(this)[0].files[0];
    // 获取文件对象路径
    var reader = new FileReader();
    reader.readAsDataURL(file_obj);
    // 修改img src属性src=文件对象的路径
    reader.onload = function () {
        $("#avatar_img").attr("src", reader.result);
    };

})

ajax提交带图片:

$(".reg_btn").click(function () {
    //获取form数据 [{},{},{}]
    var request_data = $("#form").serializeArray();
    // 建立提交formdata对象
    var formdata = new FormData();
    $.each(request_data, function (index, data) {
        formdata.append(data.name, data.value)
    });
    //加入文件对象
    formdata.append("avatar", $("#avatar")[0].files[0]);

    $.ajax({
        url: "",
        type: "post",
        contentType: false,
        processData: false,
        data: formdata,
        success: function (data) {

            console.log(data);
            if (data.user) {
                //注册成功
                window.location.href = "/login/"

            } else {
                // 注册失败
                console.log(data.msg)
                // 清空错误信息
                $("span.error").html("")
                $(".form-group").removeClass("has_error");

                //显示错误信息
                $.each(data.msg, function (field, error_list) {
                    //console.log(field, error_list);
                    if (field == "__all__") {
                        $("#id_r_pwd").next().html(error_list[0]).parent().addClass("has-error");
                    } else {
                        $("#id_" + field).next().html(error_list[0]);
                        $("#id_" + field).parent().addClass("has-error");

                    }


                })


            }

        }

    })

})

存在问题:

没有对POST进行判断  ==  已添加不是POST or 不是ajax请求 自动返回注册页面 
图像图片没有上传到server端   == 
生成验证码  pip3 install pillow 
验证码只显示第一个 注意  坐标的设置 draw.text((i * 50 + 20, 5), random_char, get_random_color(), font=kumo_font)
登录没有做next跳转 配合@requeire_login 使用  setting中要配置/login/ 

头像未上传 显示问题
request.userobj  == 是固定用法 request.userobj.is_authenticated 实际测试发现无法显示  要修改成 request.user   
全部修改为 user后面记住这个是固定用法改成userobj就获取不到
user = auth.authenticate(username=user, password=pwd)
auth.login(request, user)
res_data["user"] = user.username

自定义模板标签 这个用法  
@register.inclusion_tag("classification.html")