常用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">×</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">×</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请求)
几个问题:
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
4、views中采用 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、获取并跳转对应页面
1)form 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")