武沛齐课程笔记链接https://poker.blog.csdn.net/article/details/128073474
图书管理系统地址
github地址https://github.com/Seasonzhang-0503/library
实现书籍的借订和归还。
Form类
Form类:表单的增删改查
models.py以theBook为例子
THEBOOK_TYPE = (('电子书', '电子书'), ('纸质书', '纸质书'), ('PPT', 'PPT'),('项目', '项目'), ('视频', '视频'),('其他', '其他'))
THESTATUS_TYPE = (('已借订', '已借订'), ('未借订', '未借订'))
class theBook(models.Model):
bid = models.AutoField(primary_key=True)
theBook_name = models.CharField(max_length=500,verbose_name=('图书名称'),)
theBook_location = models.ForeignKey(location,blank=True, null=True, on_delete=models.CASCADE,related_name='Book_location')
theBook_type = models.CharField(max_length=500, verbose_name=('电子书/纸质书'),choices=THEBOOK_TYPE,)
theBook_category = models.ForeignKey(category,blank=True, null=True, on_delete=models.CASCADE,related_name='Book_category')
theBook_logo = models.ImageField(max_length=500, verbose_name=('图书照片1'),upload_to='images/')
theBook_attachment = models.FileField(max_length=10000,verbose_name=('图书附件(备用)'),blank=True, null=True,upload_to='file')
theBook_status1 = models.CharField(max_length=500,verbose_name=('图书状态1'),choices=THESTATUS_TYPE,)
theBook_status2 = models.CharField(max_length=500,verbose_name=('图书状态2(备用)'),blank=True, null=True,)
theBook_status3 = models.CharField(max_length=500,verbose_name=('图书状态3(备用)'),blank=True, null=True,)
theBook_id = models.CharField(max_length=500,verbose_name=('图书编号(备用)'),blank=True, null=True,)
theBook_information1 = models.TextField(max_length=10000,verbose_name=('图书信息1'),blank=True, null=True,)
theBook_information2 = models.TextField(max_length=10000,verbose_name=('图书信息2(备用)'),blank=True, null=True,)
theBook_information3 = models.TextField(max_length=10000,verbose_name=('图书信息3(备用)'),blank=True, null=True,)
theBook_information4 = models.TextField(max_length=10000,verbose_name=('图书信息4(备用)'),blank=True, null=True,)
theBook_information5 = models.TextField(max_length=10000,verbose_name=('图书信息5(备用)'),blank=True, null=True,)
class Meta:
verbose_name = ('图书')
verbose_name_plural = ('图书')
ordering = ['theBook_category'] # 返回值排序
def __str__(self):
return str(self.theBook_name)
forms.py把model转化为表单
class theBookForm(ModelForm):
class Meta:
model = theBook
fields = '__all__'
exclude = ['theBook_id','theBook_attachment','theBook_status1','theBook_status2','theBook_status3','theBook_information2',
'theBook_information3','theBook_information4','theBook_information5',]
error_messages = {
'theBook_name':{'required':"theBook_name不能为空",},
}
widgets = {
"theBook_name":wid.Input(attrs={"class":"c1"}) #还可以自定义属性
}
# 循环找到所有的插件,加入css样式,添加 "class": "form-control"
bootstrap_exclude_fields = ['theBook_logo',]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
for name, field in self.fields.items():
if name in self.bootstrap_exclude_fields:
continue
# class属性追加form-control,其他属性保留
if field.widget.attrs:
field.widget.attrs["class"] = field.widget.attrs.get('class','') + ' ' + 'form-control'
else:
field.widget.attrs = {
"class": "form-control",
}
views.py处理theBook的增删改查
def theBooklist_show(request,page):
theBooklist_all = theBook.objects.all()
print('request.POST',request.POST.get('querybookcategory'))
querybookname = request.POST.get('querybookname')
# getlist呈现多选值
querybookcategory = request.POST.get('querybookcategory')
querybooktype = request.POST.get('querybooktype')
print('querybookname',querybookname,'querybookcategory',querybookcategory,'querybooktype',querybooktype)
theBooklist = theBooklist_all
if querybookname:
theBooklist = theBooklist_all.filter(theBook_name__icontains=querybookname)
if querybookcategory:
theBooklist = theBooklist.filter(theBook_category__category_keyname__icontains=querybookcategory)
if querybooktype:
theBooklist = theBooklist.filter(theBook_type__icontains=querybooktype)
# 添加分页功能
paginator = Paginator(theBooklist, 5)
try:
page_obj = paginator.page(page)
except PageNotAnInteger:
# 如果参数page 的数据类型不是整型,就返回第一页数据
page_obj = paginator.page(1)
except EmptyPage:
# 若用户访问的页数大于实际页数,则返回最后一页的数据
page_obj = paginator.page(paginator.num_pages)
# context = {
# 'theBooklist':theBooklist,
# }
return render(request,'theBooklist_show.html',locals())
def theBooklist_new(request):
if request.method == "GET":
form = theBookForm()
return render(request,'theBooklist_new.html',{'form':form})
# 用户POST请求提交数据,需要进行数据校验
form = theBookForm(data=request.POST,files=request.FILES)
if form.is_valid():
# print(form.cleaned_data)
# 直接保存至数据库
form.save()
return redirect("/theBooklist_show/")
return render(request,'theBooklist_new.html',{'form':form})
def theBooklist_edit(request,bid):
row_obj = theBook.objects.filter(bid=bid).first()
if request.method == "GET":
form = theBookForm(instance=row_obj)
# 三元表达式:判断是否存在借订记录
theBook_fk_theBorrow_status1 = ''
if row_obj.theBook_type == '纸质书':
theBook_fk_theBorrow_status1 = row_obj.theborrow_set.first().theBorrow_status1 if row_obj.theborrow_set.first() else 'no-data'
print(theBook_fk_theBorrow_status1)
return render(request,'theBooklist_edit.html',{'form':form,'theBook_fk_theBorrow_status1':theBook_fk_theBorrow_status1,})
# 用户POST请求提交数据,需要进行数据校验
form = theBookForm(data=request.POST,files=request.FILES, instance=row_obj)
if form.is_valid():
print(form.cleaned_data)
# 直接保存至数据库
form.save()
return redirect("/theBooklist_show/")
return render(request,'theBooklist_edit.html',{'form':form})
def theBooklist_delete(request,bid):
theBook.objects.get(bid=bid).delete()
return redirect("/theBooklist_show/")
templates展示增删改查的表单
theBooklist_show.html
{% extends 'base.html' %}
{% load static %}
{% block body_block %}
<div class="container">
<div class="row py-4 align-items-center">
<!--展示显示人员-->
<div class="col-lg-12 col-md-12 mt-0 table-responsive" style="background-color: #fff;">
<!--表单-->
<h3 class="font-weight-bold">图书清单<a class="ml-4 btn btn-light" href="/theBooklist_new/">+</a></h3>
<table class="table table-striped table-sm table-bordered ">
<thead>
<tr style="color:White;background-color:#3366FF;font-family:微軟正黑體,Tahoma,Arial,微軟雅黑體;font-size:15px;">
<th scope="col">#</th>
<th scope="col" style="width:20%">图书名称</th>
<th scope="col">地点</th>
<th scope="col">电子书/纸质书</th>
<th scope="col" style="width:20%">分类</th>
<th scope="col">图书照片</th>
<th scope="col">借订(外键)</th>
<th scope="col">操作</th>
</tr>
</thead>
<!-- for theBook in theBooklist -->
{% for theBook in page_obj.object_list %}
<tr valign="middle" style="color:Black;border-color:#E0E0E0;font-size:15px;"></tr>
<td>{{ theBook.bid }}</td>
<td>{{ theBook.theBook_name }}</td>
<td>{{ theBook.theBook_location }}</td>
<td>{{ theBook.theBook_type }}</td>
<td>{{ theBook.theBook_category }}</td>
<td class="" style="width:100px; height:100px">
{% if theBook.theBook_logo %}
<a target="_blank" href="{{theBook.theBook_logo.url}}" >
<img src="{{theBook.theBook_logo.url}}" alt="{{theBook.theBook_logo.url}}" width="100px" height="100px"></img>
</a>
{% endif %}
</td>
<!-- 如果是电子书,则不用记录状态了 -->
{% if theBook.theBook_type == '纸质书' %}
<td>{{ theBook.theborrow_set.first.theBorrow_status1|default:'no-data' }}</td>
{% else %}
<td></td>
{% endif %}
<td>
<a class="btn btn-primary btn-xs" href="/theBooklist_edit/{{ theBook.bid }}/">编辑</a>
<a class="btn btn-danger btn-xs" href="/theBooklist_delete/{{ theBook.bid }}/">删除</a>
<a class="btn btn-info btn-xs" href="/theBorrowlist_new_default/{{ theBook.bid }}/">借订</a>
</td>
</tr>
{% endfor %}
</table>
<!--分页功能-->
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="/theBooklist_show/1/">« first</a>
<a href="/theBooklist_show/{{ page_obj.previous_page_number }}/">previous</a>
{% endif %}
<span class="current">
{{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="/theBooklist_show/{{ page_obj.next_page_number }}/">next</a>
<a href="/theBooklist_show/{{ page_obj.paginator.num_pages }}/">last »</a>
{% endif %}
</span>
</div>
</div>
</div>
</div>
<br>
<br>
<br>
<br>
<br>
{% endblock %}
theBooklist_edit.html
{% extends 'base.html' %}
{% load static %}
{% block body_block %}
<div class="container">
<div class="row py-4 align-items-center">
<!--展示显示人员-->
<div class="col-lg-12 col-md-12 mt-0 table-responsive" style="background-color: #fff;">
<!--表单-->
<h3 class="font-weight-bold">图书-编辑</h3>
<div class="modal-body">
<form method="post" id="formSave" enctype="multipart/form-data">
{% csrf_token %}
<div>
{% for item in form %}
<div class="col-xs-6">
<div class="form-group" style="position: relative; margin-top: 5px">
<label>{{ item.label }}</label>
{{ item }}
<span class="error_msg" style="color: red;position: absolute;">{{ item.errors.0 }}</span>
</div>
</div>
{% endfor %}
<!-- 手动添加外键的借订状态 -->
<div class="col-xs-6">
<div class="form-group" style="position: relative; margin-top: 5px">
<div>借订状态</div>
<input type="text" value="{{ theBook_fk_theBorrow_status1 }}" readonly="true" class="form-control">
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">保存</button>
</form>
</div>
</div>
</div>
</div>
<br>
<br>
<br>
<br>
<br>
{% endblock %}
theBooklist_new.html
{% extends 'base.html' %}
{% load static %}
{% block body_block %}
<div class="container">
<div class="row py-4 align-items-center">
<!--展示显示人员-->
<div class="col-lg-12 col-md-12 mt-0 table-responsive" style="background-color: #fff;">
<!--表单-->
<h3 class="font-weight-bold">图书-添加</h3>
<div class="panel-body">
<form method="post" id="formSave" enctype="multipart/form-data">
{% csrf_token %}
<div>
{% for item in form %}
<div class="col-xs-6">
<div class="form-group" style="position: relative; margin-top: 5px">
<label>{{ item.label }}</label>
{{ item }}
<span class="error_msg" style="color: red;position: absolute;">{{ item.errors.0 }}</span>
</div>
</div>
{% endfor %}
</div>
<button type="submit" class="btn btn-primary">保存</button>
</form>
</div>
</div>
</div>
</div>
<br>
<br>
<br>
<br>
<br>
{% endblock %}
theBooklist_query.html
{% extends 'base.html' %}
{% load static %}
{% block body_block %}
<div class="container">
<div class="row py-4 align-items-center">
<!--展示显示人员-->
<div class="col-lg-12 col-md-12 mt-0 table-responsive" style="background-color: #fff;">
<!--表单-->
<h3 class="font-weight-bold">图书清单-查询</h3>
<br>
<form action="/theBooklist_show/1/" method="post" novalidate>
{% csrf_token %}
{{ form_obj.as_p }}
<button type="submit" class="btn btn-primary">query</button>
</form>
</div>
</div>
</div>
<br>
<br>
<br>
<br>
<br>
{% endblock %}
urls.py配置路由
# theBook增删改查
# path('theBooklist_show/', views.theBooklist_show, name='theBooklist_show'),
path('theBooklist_show/<int:page>/',views.theBooklist_show,name='theBooklist_show'),
path('getQueryBookForm/', views.getQueryBookForm, name='getQueryBookForm'),
path('theBooklist_new/', views.theBooklist_new, name='theBooklist_new'),
path('theBooklist_edit/<int:bid>/', views.theBooklist_edit,name='theBooklist_edit'),
path('theBooklist_delete/<int:bid>/', views.theBooklist_delete,name='theBooklist_delete'),
Form类:动态多选框
views.py 查询后端数据,动态生成多选框
#想办法取出多选框的选项
#querylist <QuerySet [{'category_keyname': '人力资源'}, {'category_keyname': '人力资源'}, {'category_keyname': '后端'}, {'category_keyname': '前端'}, {'category_keyname': '前端'}, {'category_keyname': '人力资源'}, {'category_keyname': '后端'}, {'category_keyname': '前端'}, {'category_keyname': '后端'}, {'category_keyname': 'Git'}, {'category_keyname': '后端'}, {'category_keyname': '服务器'}, {'category_keyname': '数据'}]>
querylist = category.objects.all().order_by('category_keyname').distinct().values('category_keyname')
query_set = set()
querybookcategory_choices = [('',''),]
for q in querylist:
query_set.add(q['category_keyname'])
for qs in query_set:
querybookcategory_choices.append((qs,qs))
# querybookcategory_choices = [('人力资源', '人力资源'), ('后端', '后端')]
print('querybookcategory_choices',querybookcategory_choices)
# querytheBookcategory_choices = [('', ''),('电子书', '电子书'), ('纸质书', '纸质书')]
querytheBookcategory_list = theBook.objects.all().order_by('theBook_type').distinct().values('theBook_type')
print('querytheBookcategory_list',querytheBookcategory_list)
query_set = set()
querytheBookcategory_choices = [('',''),]
for q in querytheBookcategory_list:
query_set.add(q['theBook_type'])
for qs in query_set:
querytheBookcategory_choices.append((qs,qs))
class QueryBookForm(forms.Form):
querybookname = forms.CharField(label='图书名称')
querybookcategory = forms.ChoiceField(label='图书分类',choices=querybookcategory_choices)
querybooktype = forms.ChoiceField(label='图书类型',choices=querytheBookcategory_choices,)
theBooklist_query.html
{% extends 'base.html' %}
{% load static %}
{% block body_block %}
<div class="container">
<div class="row py-4 align-items-center">
<!--展示显示人员-->
<div class="col-lg-12 col-md-12 mt-0 table-responsive" style="background-color: #fff;">
<!--表单-->
<h3 class="font-weight-bold">图书清单-查询</h3>
<br>
<form action="/theBooklist_show/1/" method="post" novalidate>
{% csrf_token %}
{{ form_obj.as_p }}
<button type="submit" class="btn btn-primary">query</button>
</form>
</div>
</div>
</div>
{% endblock %}
ORM
ORM:values()和values_list()
1、values返回是字典列表
2、values_list返回的是元组列表
#querylist <QuerySet [{'category_keyname': '人力资源'}, {'category_keyname': '人力资源'}, {'category_keyname': '后端'}, {'category_keyname': '前端'}, {'category_keyname': '前端'}, {'category_keyname': '人力资源'}, {'category_keyname': '后端'}, {'category_keyname': '前端'}, {'category_keyname': '后端'}, {'category_keyname': 'Git'}, {'category_keyname': '后端'}, {'category_keyname': '服务器'}, {'category_keyname': '数据'}]>
querylist = category.objects.all().order_by('category_keyname').distinct().values('category_keyname')
# querylist2 <QuerySet [('Git',), ('人力资源',), ('前端',), ('后端',), ('数据',), ('服务器',)]>
# querylist2 = category.objects.all().order_by('category_keyname').distinct().values_list('category_keyname')
ORM:外键的关联查询
1.外键的关联查询:外键 __ 属性
2.外键的外键的关联查询:外键 __ 外键 __ 属性(理论上外键可以一直连下去)
models.py有外键关系
class theBook(models.Model):
......
theBook_category = models.ForeignKey(category,blank=True, null=True, on_delete=models.CASCADE,related_name='Book_category')
......
class category(models.Model):
......
category_keyname = models.CharField(max_length=500,verbose_name=('分类主名称'),)
......
views.py中有theBooklist = theBooklist.filter(theBook_category__category_keyname__icontains=querybookcategory)
,通过查找外键的属性。
def theBooklist_show(request,page):
theBooklist_all = theBook.objects.all()
print('request.POST',request.POST.get('querybookcategory'))
querybookname = request.POST.get('querybookname')
# getlist呈现多选值
querybookcategory = request.POST.get('querybookcategory')
querybooktype = request.POST.get('querybooktype')
print('querybookname',querybookname,'querybookcategory',querybookcategory,'querybooktype',querybooktype)
theBooklist = theBooklist_all
if querybookname:
theBooklist = theBooklist_all.filter(theBook_name__icontains=querybookname)
if querybookcategory:
theBooklist = theBooklist.filter(theBook_category__category_keyname__icontains=querybookcategory)
if querybooktype:
theBooklist = theBooklist.filter(theBook_type__icontains=querybooktype)
# 添加分页功能
paginator = Paginator(theBooklist, 5)
try:
page_obj = paginator.page(page)
except PageNotAnInteger:
# 如果参数page 的数据类型不是整型,就返回第一页数据
page_obj = paginator.page(1)
except EmptyPage:
# 若用户访问的页数大于实际页数,则返回最后一页的数据
page_obj = paginator.page(paginator.num_pages)
# context = {
# 'theBooklist':theBooklist,
# }
return render(request,'theBooklist_show.html',locals())
非ORM:操作原始SQL语句
方式1:views.py执行原生SQL查询生成 RawQuerySet
# 练习原始SQL
def categorylist_show(request):
# categorylist = category.objects.all()
# 练习原始SQL
categorylist = category.objects.raw('SELECT * FROM book_category')
context = {
'categorylist':categorylist,
}
return render(request,'categorylist_show.html',context)
方式2:views.py直接执行自定义 SQL
# 直接执行自定义 SQL
# (2, '000001', 'Django基础教程(Tango with Django)', 5, 'images/tangowithdjango.png', '', '已借订', None, None, 'D:\\我的U盘-20220812\\ABE-Python\\4.python网页前后端\\Django基础教程', '', '', '', '', 3, '电子书')
def dictfetchall(cursor):
"Return all rows from a cursor as a dict"
columns = [col[0] for col in cursor.description]
return [
dict(zip(columns, row))
for row in cursor.fetchall()
]
from django.db import connection
def my_custom_sql(request):
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM book_thebook")
rows = dictfetchall(cursor)
print(rows)
for row in rows:
print('row.theBook_name',row['theBook_name'])
# return HttpResponse('rows',rows)
return HttpResponse('rows')
数据库操作(不用django,在数据库中直接增删改查)
https://poker.blog.csdn.net/article/details/128250617
增加数据
insert into 表名称(字段1, 字段2, ...) values(1, "张三", ...);
insert into tb1(name,age) values("张三",25);
删除数据
delete from 表名称; --删除所有数据
delete from 表名称 where 条件; --删除指定数据
delete from tb1 where id = 1;
delete from tb1 where id = 1 and name = "张三";
delete from tb1 where id = 1 or id = 100;
delete from tb1 where id > 100;
delete from tb1 where id != 50;
delete from tb1 where id in (10,15);
修改数据
update 表名称 set 列 = 值; --修改一列
update 表名称 set 列 = 值, 列 = 值; --修改多列
update 表名称 set 列 = 值 where 条件; --修改某行某列
update tb1 set name="李四" where id = 1;
update tb1 set age=age+10 where name=""李四;
查询数据
select 字段名(或者*) from 表名称;
select 字段名(或者*) from 表名称 where 条件;
select * from tb1;
select name from tb1;
select * from tb1 where id = 1;
分页
【Django 分页】Django 中的分页功能–20230403
自定义用户-AbstractUser模型
自定义用户:重写abstractuser模型
【Django User】Django 中的拓展自定义用户user模型–20230403
自定义用户:密码加密
settings.py默认首选第一个加密方式,其他加密方式只是第二顺位,都可以使用。
from django.conf import settings
settings.configure(DEBUG=True)
PASSWORD_HASHERS = (
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
)
forms.py–theUserForm增加clean_password()方法。
class theUserForm(ModelForm):
class Meta:
model = User
#fields = '__all__'
fields = ['username','password','dept','age','mobilenumber','role','theUser_status1',]
exclude = []
widgets = {
"password":wid.PasswordInput(render_value = True) # render_value = True显示值
}
labels= {
"username":"用户名",
"password":"密码",
}
# 循环找到所有的插件,加入css样式,添加 "class": "form-control"
bootstrap_exclude_fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
for name, field in self.fields.items():
if name in self.bootstrap_exclude_fields:
continue
# class属性追加form-control,其他属性保留
if field.widget.attrs:
field.widget.attrs["class"] = field.widget.attrs.get('class','') + ' ' + 'form-control'
else:
field.widget.attrs = {
"class": "form-control",
}
def clean_password(self):
pwd = self.cleaned_data.get("password")
# return什么.password字段保存什么
return make_password(pwd)
自定义用户:theUserForm和theUserNoPasswordForm
theUserForm用于注册,含密码。
theUserNoPasswordForm用于修改非密码的其他信息。
forms.py:定义好theUserForm和theUserNoPasswordForm
class theUserForm(ModelForm):
class Meta:
model = User
#fields = '__all__'
fields = ['username','password','dept','age','mobilenumber','role','theUser_status1',]
exclude = []
widgets = {
"password":wid.PasswordInput(render_value = True) # render_value = True显示值
}
labels= {
"username":"用户名",
"password":"密码",
}
# 循环找到所有的插件,加入css样式,添加 "class": "form-control"
bootstrap_exclude_fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
for name, field in self.fields.items():
if name in self.bootstrap_exclude_fields:
continue
# class属性追加form-control,其他属性保留
if field.widget.attrs:
field.widget.attrs["class"] = field.widget.attrs.get('class','') + ' ' + 'form-control'
else:
field.widget.attrs = {
"class": "form-control",
}
def clean_password(self):
pwd = self.cleaned_data.get("password")
# return什么.password字段保存什么
return make_password(pwd)
class theUserNoPasswordForm(ModelForm):
class Meta:
model = User
fields = ['username','dept','age','mobilenumber','role','theUser_status1',]
exclude = []
labels= {
"username":"用户名",
}
# 循环找到所有的插件,加入css样式,添加 "class": "form-control"
bootstrap_exclude_fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
for name, field in self.fields.items():
if name in self.bootstrap_exclude_fields:
continue
# class属性追加form-control,其他属性保留
if field.widget.attrs:
field.widget.attrs["class"] = field.widget.attrs.get('class','') + ' ' + 'form-control'
else:
field.widget.attrs = {
"class": "form-control",
}
views.py:theUserlist_edit使用theUserNoPasswordForm。
def theUserlist_new(request):
if request.method == "GET":
form = theUserForm()
return render(request,'theUserlist_new.html',{'form':form})
# 用户POST请求提交数据,需要进行数据校验
form = theUserForm(data=request.POST,files=request.FILES)
if form.is_valid():
print(form.cleaned_data)
# 直接保存至数据库
form.save()
return redirect("/theUserlist_show/")
return render(request,'theUserlist_new.html',{'form':form})
def theUserlist_edit(request,id):
row_obj = User.objects.filter(id=id).first()
if request.method == "GET":
form = theUserNoPasswordForm(instance=row_obj)
# 判断是否存在借订记录
theUser_fk_theBorrow_fk_theBook = []
for fk in row_obj.theborrow_set.all():
booklist = fk.theBorrow_theBook.theBook_name if fk else 'no-data'
theUser_fk_theBorrow_fk_theBook.append(booklist)
print(theUser_fk_theBorrow_fk_theBook)
return render(request,'theUserlist_edit.html',{'form':form,'theUser_fk_theBorrow_fk_theBook':theUser_fk_theBorrow_fk_theBook,})
# 用户POST请求提交数据,需要进行数据校验
form = theUserNoPasswordForm(data=request.POST,files=request.FILES, instance=row_obj)
if form.is_valid():
# print(form.cleaned_data)
# 直接保存至数据库
form.save()
return redirect("/theUserlist_show/")
return render(request,'theUserlist_edit.html',{'form':form})
自定义用户-全新User模型
自定义用户:新增newUser模型、用户与密码验证
参考Django web 开发(四) - Django项目实践(七)-账户登录
forms.py:利用forms.Form建立登录表单
# 这一次不使用ModelForm,使用Form来实现
class LoginForm(forms.Form):
username = forms.CharField(
label="用户名",
widget=forms.TextInput(attrs={"class": "form-control"}),
required=True,
)
password = forms.CharField(
label="用户名",
# render_value=True 表示当提交后,如果密码输入错误,不会自动清空密码输入框的内容
widget=forms.PasswordInput(attrs={"class": "form-control"}, ),
required=True,
)
def clean_password(self):
pwd = self.cleaned_data.get("password")
return pwd
views.py
def admin_login(request):
"""admin登录"""
print('admin_login')
if request.method == "GET":
form = LoginForm(auto_id=True)
return render(request, 'admin_login.html', {"form": form})
form = LoginForm(data=request.POST)
if form.is_valid():
# 去数据库校验用户名和密码是否正确
admin_object = Admin.objects.filter(**form.cleaned_data).first()
# print('**form.cleaned_data',**form.cleaned_data)
# 如果数据库中没有查询到数据
if not admin_object:
# 手动抛出错误显示在"password"字段下
form.add_error("password", "用户名或密码错误")
return render(request, 'admin_login.html', {"form": form})
return redirect("/")
return render(request, 'admin_login.html', {"form": form})
urls.py
# admin自定义用户登录
path('admin_login/', views.admin_login,name='admin_login'),
admin_login.html
{% extends 'base.html' %}
{% load static %}
{% block body_block %}
<div class="container card mt-3">
<div class="card-body">
<h2 class="card-title">用户登录</h2>
<div class="card-text">
<form method="post" novalidate>
{% csrf_token %}
<div class="form-group">
<label>用户名</label>
{{ form.username }}
<span style="color: red;">{{ form.errors.username.0 }}</span>
</div>
<div class="form-group">
<label>密码</label>
{{ form.password }}
<span style="color: red;">{{ form.errors.password.0 }}</span>
</div>
<button type="submit" class="btn btn-primary center-block" style="width: 80px;">登录</button>
</form>
</div>
</div>
</div>
{% endblock %}
自定义用户:显示用户信息
因为在login函数中request.session中定义的username字段对应的名称为name,所以我们可以在前端代码中直接调用。
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="/admin_login/">admin自主用户登录</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="/admin_logout/">admin自主用户登出</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">{{ request.session.info.name|default:'未登陆' }}</a>
</div>
自定义用户:注册功能和MD5加密
encrypt.py实现md5加密。
import hashlib
from django.conf import settings
def md5(data_string):
obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))
obj.update(data_string.encode('utf-8'))
return obj.hexdigest()
forms.py使用md5加密函数和modelform。
class AdminAddForm(ModelForm):
class Meta:
model = Admin
fields = ["username", "password","permission",]
widgets = {
"password": forms.PasswordInput
}
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd)
views.py 套用模板即可
def admin_add(request):
"""添加管理员"""
if request.method == "GET":
form = AdminAddForm()
return render(request, "admin_add.html", {"form": form})
# 如果是POST请求
form = AdminAddForm(data=request.POST)
print('request.POST',request.POST)
context = {
"form": form,
}
if form.is_valid():
form.save()
return redirect("/")
return render(request, "admin_add.html", context)
urls.py
path('admin_add/', views.admin_add),
自定义用户:登录Session和cookies
request.session[“info”] = {‘id’: admin_object.id, ‘name’: admin_object.username}
views.py:如果用户名密码正确,网站生成随机字符创,写到用户浏览器的cookie中,再写入到服务器的session中。
def admin_login(request):
"""admin登录"""
print('admin_login')
if request.method == "GET":
form = LoginForm(auto_id=True)
return render(request, 'admin_login.html', {"form": form})
form = LoginForm(data=request.POST)
if form.is_valid():
# 去数据库校验用户名和密码是否正确
admin_object = Admin.objects.filter(**form.cleaned_data).first()
# print('**form.cleaned_data',**form.cleaned_data)
# 如果数据库中没有查询到数据
if not admin_object:
# 手动抛出错误显示在"password"字段下
form.add_error("password", "用户名或密码错误")
return render(request, 'admin_login.html', {"form": form})
# 如果用户名密码正确
# 网站生成随机字符创,写到用户浏览器的cookie中,再写入到服务器的session中
request.session["info"] = {'id': admin_object.id, 'name': admin_object.username}
return redirect("/")
return render(request, 'admin_login.html', {"form": form})
自定义用户:中间件实现登录
middleware\auth.py :你会发现只要你没登录过,不管你访问什么页面,都会跳转到login登录界面。
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect
class AuthMiddleware(MiddlewareMixin):
def process_request(self, request):
# 0.排除不需要的页面
if request.path_info == "/admin_login/":
return
# 1.读取当前访问的用户的session信息,如果能读到,说明已登录过,就可以继续向后走
info_dict = request.session.get("info")
if info_dict:
return
# 2.如果没有登录信息
return redirect("/admin_login/")
settings.py注册中间件
MIDDLEWARE = [
......
'book.middleware.auth.AuthMiddleware',#自定义中间件
]
自定义用户:用户注销
views.py 删除session
def admin_logout(request):
""" 注销 """
# 清除当前session
# request.session.clear()
# 清除指定session
del request.session['info']
return redirect("/admin_login/")
urls.py
path('admin_logout/', views.admin_logout),
echarts和ajax
Django web 开发(四) - Django项目实践(十)-数据统计
urls.py建立空页path和3个请求数据path。
# 数据统计
path('chart/list/', views.chart_list),
path('chart/line/', views.chart_line),
path('chart/bar/', views.chart_bar),
path('chart/pie/', views.chart_pie),
views.py定义后端1个空页请求和3个数据请求。
def chart_list(request):
""" 数据统计 """
return render(request, 'chart_list.html')
def chart_line(request):
""" 构造折线图的数据 """
# 数据可以去数据库中获取
legend = ['天津分公司', '北京分公司']
xAxis = ['1月', '2月', '3月', '4月', '5月', '6月', '7月']
series_list = [
{
'name': '天津分公司',
'type': 'line',
'data': [520, 132, 101, 134, 90, 230, 210]
},
{
'name': '北京分公司',
'type': 'line',
'data': [220, 182, 191, 234, 290, 330, 310]
},
]
result = {
"status": True,
"data": {
"legend": legend,
"xAxis": xAxis,
"series_list": series_list,
}
}
return JsonResponse(result)
def chart_bar(request):
""" 构造柱状图的数据 """
# 数据可以去数据库中获取
legend = ['销量', '价格']
xAxis = ['1月', '2月', '3月', '4月', '5月', '6月']
series_list = [
{
"name": '销量',
"type": 'bar',
"data": [5, 20, 36, 10, 10, 20]
},
{
"name": '价格',
"type": 'bar',
"data": [25, 40, 80, 65, 70, 50]
}
]
result = {
"status": True,
"data": {
"legend": legend,
"xAxis": xAxis,
"series_list": series_list,
}
}
return JsonResponse(result)
def chart_pie(request):
""" 构造饼图的数据 """
data_list = [
{ "value": 10, "name": '销售部' },
{ "value": 735, "name": '运营部' },
{ "value": 580, "name": '财务部' },
]
result = {
"status": True,
"data_list": data_list,
}
return JsonResponse(result)
chart_list.html 利用ajax请求访问数据
{% extends 'base.html' %}
{% load static %}
{% block body_block %}
<div class="container">
<h1>数据统计</h1>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">折线图</h3>
<div class="panel-body">
<div id="m1" style="width: 100%;height:400px;"></div>
</div>
</div>
<div class="panel-body">
</div>
</div>
<div class="row">
<div class="col-sm-8">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">柱状图</h3>
</div>
<div class="panel-body">
<div id="m2" style="width: 600px;height:400px;"></div>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">饼图</h3>
</div>
<div class="panel-body">
<div id="m3" style="width: 300px;height:400px;"></div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block script %}
<script src="/static/js/echarts.js"></script>
<script type="text/javascript">
$(function () {
initLine();
initBar();
iniPie();
})
function initLine() {
var chartDom = document.getElementById('m1');
var myChart = echarts.init(chartDom);
var option;
option = {
title: {
text: '分公司业绩图',
left: "center",
},
tooltip: {
trigger: 'axis'
},
legend: {
// data: ['天津分公司', '北京分公司'],
data:[],
bottom: 0,
},
grid: {
left: '3%',
right: '4%',
bottom: '12%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: false,
}
},
xAxis: {
type: 'category',
boundaryGap: false,
// data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月'],
data: [],
},
yAxis: {
type: 'value'
},
series: [
// {
// name: '天津分公司',
// type: 'line',
// stack: 'Total',
// data: [120, 132, 101, 134, 90, 230, 210]
// },
// {
// name: '北京分公司',
// type: 'line',
// stack: 'Total',
// data: [220, 182, 191, 234, 290, 330, 310]
// },
],
};
$.ajax({
url: "/chart/line/",
type: "get",
dataType: "JSON",
success: function(res){
if(res.status){
// 将获取到的数据更新到 option 中
option.legend.data = res.data.legend;
option.xAxis.data = res.data.xAxis;
option.series = res.data.series_list;
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
}
}
})
}
function initBar() {
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('m2'));
// 指定图表的配置项和数据
var option = {
title: {
text: '员工业绩年度汇总信息',
subtext: "xxx公司",
textAlign: "auto",
left: "center",
},
tooltip: {},
legend: {
data: [], // 后台获取
bottom: 0,
},
xAxis: {
data: [] // 后台获取
},
yAxis: {},
series: [] // 后台获取
};
$.ajax({
url: "/chart/bar/",
type: "get",
dataType: "JSON",
success: function(res){
if(res.status){
// 将获取到的数据更新到 option 中
option.legend.data = res.data.legend;
option.xAxis.data = res.data.xAxis;
option.series = res.data.series_list;
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
}
}
})
}
// 饼图
// function iniPie() {
// var chartDom = document.getElementById('m3');
// var myChart = echarts.init(chartDom);
// var option;
// option = {
// title: {
// text: '部门预算占比',
// subtext: 'xxx公司',
// left: 'center'
// },
// tooltip: {
// trigger: 'item'
// },
// legend: {
// orient: 'vertical',
// bottom: 0,
// },
// series: [
// {
// name: 'Access From',
// type: 'pie',
// radius: '50%',
// data: [
// // { value: 1048, name: '销售部' },
// // { value: 735, name: '运营部' },
// // { value: 580, name: '财务部' },
// ],
// emphasis: {
// itemStyle: {
// shadowBlur: 10,
// shadowOffsetX: 0,
// shadowColor: 'rgba(0, 0, 0, 0.5)'
// }
// }
// }
// ]
// };
// option && myChart.setOption(option);
// }
function iniPie() {
var chartDom = document.getElementById('m3');
var myChart = echarts.init(chartDom);
var option;
option = {
title: {
text: '部门预算占比',
subtext: 'xxx公司',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
bottom: 0,
},
series: [
{
name: 'Access From',
type: 'pie',
radius: '50%',
data: [],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
$.ajax({
url: "/chart/pie/",
type: "get",
dataType: "JSON",
success: function(res) {
if(res.status){
option.series[0].data = res.data_list;
option && myChart.setOption(option);
}
}
})
}
</script>
{% endblock %}
modal和ajax
思路:利用ajax技术,区分GET和POST方法,携带id参数,进行增删改查。利用全局变量EDIT_ID和DELETE_IDk,通过url?boid来传递。单独显示readonly字段,同时无法变更的值可以选择不显示。
step1:views.py
theBorrowlist_user方法用于展示列表。
theBorrowlist_user_modal_new方法用于弹出框新增数据。
theBorrowlist_user_modal_show方法用于弹出框展示数据细节。
theBorrowlist_user_modal_save方法用于弹出框保存数据。
def theBorrowlist_user(request):
theBorrowlist = theBorrow.objects.all()
AddForm = theBorrowModalAddForm()
ShowForm = theBorrowModalShowForm()
# EditForm = theBorrowModalEditForm()
context = {
'theBorrowlist':theBorrowlist,
'AddForm':AddForm,
'ShowForm':ShowForm,
# 'EditForm':EditForm,
}
return render(request,'theBorrowlist_user.html',context)
@csrf_exempt
def theBorrowlist_user_modal_new(request):
""" 新建 """
form = theBorrowModalAddForm(data=request.POST)
if form.is_valid():
print('is_valid',request.POST)
form.save()
return JsonResponse({"status": True,"error":'fail',})
return JsonResponse({"status": False,"error":'fail',})
@csrf_exempt
def theBorrowlist_user_modal_show(request):
boid = request.GET.get('boid')
print('show-boid',boid)
# values方法形成一个对象
row_obj = theBorrow.objects.filter(boid=boid).values("boid", "theBorrow_add_datetime", "theBorrow_theUser",
'theBorrow_theBook','theBorrow_duration','theBorrow_status1'
).first()
if not row_obj:
return JsonResponse({"status": False, "error": "数据不存在!","boid":boid})
else:
# 从数据库中获取到一个对象 row_object
result = {
"status": True,
"data": row_obj,
"boid":boid,
}
print('result',result)
return JsonResponse(result)
@csrf_exempt
def theBorrowlist_user_modal_save(request):
data = request.POST
print('save-data',data)
boid = request.GET.get('boid')
print('save-boid',boid)
# values方法形成一个对象
# row_obj = theBorrow.objects.filter(boid=boid).values("boid", "theBorrow_add_datetime", "theBorrow_theUser",'theBorrow_theBook','theBorrow_duration','theBorrow_status1').first()
row_obj = theBorrow.objects.filter(boid=boid).first()
if not row_obj:
return JsonResponse({"status": False, "error": "数据不存在!"})
print('row_obj',row_obj)
form = theBorrowModalShowForm(data=data,instance=row_obj)
if form.is_valid():
print('is_valid',request.POST)
form.save()
return JsonResponse({"status": True,"error":'fail-valid',})
return JsonResponse({"status": False,"error":'fail-invalid',})
@csrf_exempt
def theBorrowlist_user_modal_delete(request):
data = request.GET
boid = request.GET.get('boid')
print('delete-boid',boid)
row_obj = theBorrow.objects.filter(boid=boid).first()
if not row_obj:
return JsonResponse({"status": False, "error": "数据不存在!"})
else:
theBorrow.objects.filter(boid=boid).first().delete()
return JsonResponse({"status": True,"error":'fail-valid',})
step2:forms.py
theBorrowModalAddForm是新增时用的表单。
theBorrowModalShowForm是查询和编辑时用的表单。
class theBorrowModalAddForm(ModelForm):
class Meta:
model = theBorrow
fields = '__all__'
exclude = ['theBorrow_status2','theBorrow_status3',]
# widgets = {
# }
# 循环找到所有的插件,加入css样式,添加 "class": "form-control"
bootstrap_exclude_fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
for name, field in self.fields.items():
if name in self.bootstrap_exclude_fields:
continue
# class属性追加form-control,其他属性保留
if field.widget.attrs:
field.widget.attrs["class"] = field.widget.attrs.get('class','') + ' ' + 'form-control'
else:
field.widget.attrs = {
"class": "form-control",
}
class theBorrowModalShowForm(ModelForm):
boid = forms.CharField(label='boid',widget=wid.Input(attrs={"class":'edit','readonly':True}))
# theBorrow_add_datetime = forms.CharField(label='theBorrow_add_datetime',widget=wid.Input(attrs={"class":'edit'}))
class Meta:
model = theBorrow
fields = '__all__'
exclude = ['theBorrow_status2','theBorrow_status3',]
widgets = {
# "theBorrow_theUser":wid.Select(attrs={"class":'edit',"readonly":'true'}),
# "theBorrow_theBook":wid.Select(attrs={"class":'edit'}),
# "theBorrow_duration":wid.NumberInput(attrs={"class":'edit'}),
# "theBorrow_status1":wid.Select(attrs={"class":'edit'}),
}
# 循环找到所有的插件,加入css样式,添加 "class": "form-control"
bootstrap_exclude_fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
for name, field in self.fields.items():
if name in self.bootstrap_exclude_fields:
continue
# class属性追加form-control,其他属性保留
if field.widget.attrs:
field.widget.attrs["class"] = field.widget.attrs.get('class','') + ' ' + 'form-control'+ ' ' + 'edit'
else:
field.widget.attrs = {
"class": "form-control"+ ' ' + 'edit',
}
step3:theBorrowlist_user.html
jQuery监听 doAdd(), doEdit(), doDelete()方法。
{% extends 'base.html' %}
{% load static %}
{% block body_block %}
<div class="container" id='app'>
<div class="row py-4 align-items-center">
<!--展示显示人员-->
<div class="col-lg-12 col-md-12 mt-0 table-responsive" style="background-color: #fff;">
<!--表单-->
<h3 class="font-weight-bold">预订清单<button class="ml-4 btn btn-light btnAdd">+</button></h3>
<table class="table table-striped table-sm table-bordered ">
<thead>
<tr style="color:White;background-color:#3366FF;font-family:微軟正黑體,Tahoma,Arial,微軟雅黑體;font-size:15px;">
<th scope="col">#</th>
<th scope="col">借订创建日期</th>
<th scope="col">借订更新日期</th>
<th scope="col">借订人</th>
<th scope="col">借订书籍</th>
<th scope="col">借订状态1</th>
<th scope="col">操作</th>
</tr>
</thead>
<!--for theBorrow in theBorrowlist-->
{% for theBorrow in page_obj %}
<tr valign="middle" style="color:Black;border-color:#E0E0E0;font-size:15px;"></tr>
<td>{{ theBorrow.boid }}</td>
<td>{{ theBorrow.theBorrow_add_datetime|date:"Y-m-d" }}</td>
<td>{{ theBorrow.theBorrow_update_datetime|date:"Y-m-d" }}</td>
<td>{{ theBorrow.theBorrow_theUser }}</td>
<td>{{ theBorrow.theBorrow_theBook }}</td>
<td>{{ theBorrow.theBorrow_status1 }}</td>
<td>
<button type="button" class="btn btn-primary btnEdit" boid="{{ theBorrow.boid }}">编辑</button>
<button type="button" class="btn btn-secondary btnDelete" boid="{{ theBorrow.boid }}">删除</button>
</td>
</tr>
{% endfor %}
</table>
<!--分页功能-->
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="/theBorrowlist_user/1/">« first</a>
<a href="/theBorrowlist_user/{{ page_obj.previous_page_number }}/">previous</a>
{% endif %}
<span class="current">
{{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="/theBorrowlist_user/{{ page_obj.next_page_number }}/">next</a>
<a href="/theBorrowlist_user/{{ page_obj.paginator.num_pages }}/">last »</a>
{% endif %}
</span>
</div>
<!-- Modal -->
<div class="modal fade" data-backdrop="static" id="myModal1" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document" style="max-width: 600px;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalCenterTitle">新增预定</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form method="post" id="formSave1" enctype="multipart/form-data">
<div>
{% for item in AddForm %}
<div class="col-12">
<div class="form-group" style="position: relative; margin-top: 5px">
<label>{{ item.label }}</label>
{{ item }}
<span class="error_msg" style="color: red;position: absolute;">{{ item.errors.0 }}</span>
</div>
</div>
{% endfor %}
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
<button type="button" id="btnSave1" class="btn btn-primary btnSave1">保存</button>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" data-backdrop="static" id="myModal2" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document" style="max-width: 600px;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalCenterTitle">查看预订情况</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form method="post" id="formSave2" enctype="multipart/form-data">
<div>
{% for item in ShowForm %}
<div class="col-12">
<div class="form-group" style="position: relative; margin-top: 5px">
<label>{{ item.label }}</label>
{{ item }}
<span class="error_msg" style="color: red;position: absolute;">{{ item.errors.0 }}</span>
</div>
</div>
{% endfor %}
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
<button type="button" id="btnSave2" class="btn btn-primary btnSave2">保存</button>
</div>
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="myModalDelete" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">是否确定删除?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>删除后所有关联的相关数据都将被删除</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">取消</button>
<button id="btnConfirmDelete" type="button" class="btn btn-danger">确定</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<br>
<br>
<br>
<br>
<br>
{% endblock %}
{% block script %}
<script type="text/javascript">
var DELETE_ID;
var EDIT_ID;
$(function(){
doAdd();
doEdit();
doDelete();
})
function doAdd(){
$(".btnAdd").click(function(){
$("#formSave1")[0].reset();
$("#myModal1").modal('show');
});
$("#btnSave1").click(function(){
$.ajax({
url: "/theBorrowlist_user_modal_new/",
type: "post",
data: $("#formSave1").serialize(),
dataType: "JSON",
success: function (res) {
if (res.status) {
$("#myModal1").modal('hide');
console.log('#btnSave1',res.status)
// 刷新页面
location.reload();
} else {
alert('form invalid')
$("#myModal1").modal('show');
console.log('#btnSave1',res.status)
}
}
})
});
}
function doDelete(){
$(".btnDelete").click(function () {
// 显示删除对话框
$("#myModalDelete").modal('show');
//获取当前行的ID
DELETE_ID = $(this).attr("boid")
console.log('Delete-boid-unconfirmed',DELETE_ID)
});
$("#btnConfirmDelete").click(function(){
console.log('Delete-boid-confirmed',DELETE_ID)
$.ajax({
url: "/theBorrowlist_user_modal_delete/",
type: "get",
data:{
boid : DELETE_ID,
},
dataType: "JSON",
success: function (res) {
if (res.status) {
console.log('Delete',res.status)
// 刷新页面
location.reload();
} else {
alert('Delete失败')
console.log('Delete',res.status)
}
}
})
});
}
function doEdit(){
//弹出
$(".btnEdit").click(function(){
EDIT_ID = $(this).attr("boid")
console.log('Edit-boid',EDIT_ID)
$.ajax({
url: "/theBorrowlist_user_modal_show/",
type: "get",
data:{
boid : EDIT_ID,
},
dataType: "JSON",
success: function (res) {
console.log('res',res)
if (res.status) {
$("#formSave2")[0].reset();
$.each(res.data, function (name, value) {
//同时选择edit类和id
$(".edit#id_" + name).val(value);
console.log('edit-show:',name,value)
});
// 显示对话框
$("#myModal2").modal('show');
} else {
alert(res.error);
$("#myModal2").modal('hide');
}
}
})
});
//保存变更
$("#btnSave2").click(function(){
console.log('EDIT_ID',EDIT_ID)
$.ajax({
url: "/theBorrowlist_user_modal_save/?boid="+EDIT_ID,
type: "post",
data: $("#formSave2").serialize(),
dataType: "JSON",
success: function (res) {
console.log('res',res)
if (res.status) {
// 显示对话框
$("#myModal2").modal('hide');
// 刷新页面
location.reload();
} else {
alert(res.error);
// 显示对话框
$("#myModal2").modal('show');
}
}
})
});
}
</script>
{% endblock %}
step4:urls.py
需定义好ajax请求的url path。
# theBorrow_user增删改查
path('theBorrowlist_user/', views.theBorrowlist_user, name='theBorrowlist_user'),
path('theBorrowlist_user_modal_show/', views.theBorrowlist_user_modal_show, name='theBorrowlist_user_modal_show'),
path('theBorrowlist_user_modal_new/', views.theBorrowlist_user_modal_new, name='theBorrowlist_user_modal_new'),
path('theBorrowlist_user_modal_save/', views.theBorrowlist_user_modal_save, name='theBorrowlist_user_modal_save'),
path('theBorrowlist_user_modal_delete/', views.theBorrowlist_user_modal_delete, name='theBorrowlist_user_modal_delete'),
step5:无法变更的值可以选择不显示
form.py利用exclude 属性隐藏不可修改的属性。
class theBorrowModalShowForm(ModelForm):
boid = forms.CharField(label='boid',widget=wid.Input(attrs={"class":'edit','readonly':True}))
# theBorrow_add_datetime = forms.CharField(label='theBorrow_add_datetime',widget=wid.Input(attrs={"class":'edit'}))
class Meta:
model = theBorrow
fields = '__all__'
# 无法更改的值直接不要显示
exclude = ['theBorrow_theUser','theBorrow_theBook','theBorrow_duration','theBorrow_status2','theBorrow_status3',]
# widgets = {
# "theBorrow_theUser":wid.Select(attrs={"class":'edit',"readonly":'true'}),
# "theBorrow_theBook":wid.Select(attrs={"class":'edit',"readonly":'true'}),
# "theBorrow_duration":wid.NumberInput(attrs={"class":'edit',"readonly":'true'}),#disabled
# }
# 循环找到所有的插件,加入css样式,添加 "class": "form-control"
bootstrap_exclude_fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
for name, field in self.fields.items():
if name in self.bootstrap_exclude_fields:
continue
# class属性追加form-control,其他属性保留
if field.widget.attrs:
field.widget.attrs["class"] = field.widget.attrs.get('class','') + ' ' + 'form-control'+ ' ' + 'edit'
else:
field.widget.attrs = {
"class": "form-control"+ ' ' + 'edit',
}
step6:单独显示readonly字段
theBorrowlist_user.html单独显示readonly字段,并利用ajax额外传值。
...
<!-- Modal -->
<div class="modal fade" data-backdrop="static" id="myModal2" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document" style="max-width: 600px;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalCenterTitle">查看预订情况</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form method="post" id="formSave2" enctype="multipart/form-data">
<div>
<div class="col-12">
<div class="form-group" style="position: relative; margin-top: 5px">
<label >名字</label>
<input class="form-control" type="text" id="theBorrow_theUser" readonly >
</div>
</div>
<div class="col-12">
<div class="form-group" style="position: relative; margin-top: 5px">
<label >书籍</label>
<input class="form-control" type="text" id="theBorrow_theBook" readonly >
</div>
</div>
{% for item in ShowForm %}
<div class="col-12">
<div class="form-group" style="position: relative; margin-top: 5px">
<label>{{ item.label }}</label>
{{ item }}
<span class="error_msg" style="color: red;position: absolute;">{{ item.errors.0 }}</span>
</div>
</div>
{% endfor %}
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
<button type="button" id="btnSave2" class="btn btn-primary btnSave2">保存</button>
</div>
</div>
</div>
</div>
....
{% endblock %}
{% block script %}
<script type="text/javascript">
var DELETE_ID;
var EDIT_ID;
$(function(){
doAdd();
doEdit();
doDelete();
})
......
function doEdit(){
//弹出
$(".btnEdit").click(function(){
EDIT_ID = $(this).attr("boid")
console.log('Edit-boid',EDIT_ID)
$.ajax({
url: "/theBorrowlist_user_modal_show/",
type: "get",
data:{
boid : EDIT_ID,
},
dataType: "JSON",
success: function (res) {
console.log('res',res)
if (res.status) {
$("#formSave2")[0].reset();
//额外传值
$("#theBorrow_theUser").val(res.readonly_data.theBorrow_theUser);
$("#theBorrow_theBook").val(res.readonly_data.theBorrow_theBook);
//表单循环赋值
$.each(res.data, function (name, value) {
//同时选择edit类和id
$(".edit#id_" + name).val(value);
console.log('edit-show:',name,value)
});
// 显示对话框
$("#myModal2").modal('show');
} else {
alert(res.error);
$("#myModal2").modal('hide');
}
}
})
});
.....
}
</script>
{% endblock %}
views.py利用row_obj2 = theBorrow.objects.filter(boid=boid).first()来实现额外传值
@csrf_exempt
def theBorrowlist_user_modal_show(request):
boid = request.GET.get('boid')
print('show-boid',boid)
# values方法形成一个对象
row_obj = theBorrow.objects.filter(boid=boid).values("boid", "theBorrow_add_datetime", "theBorrow_theUser",
'theBorrow_theBook','theBorrow_duration','theBorrow_status1'
).first()
row_obj2 = theBorrow.objects.filter(boid=boid).first()
if not row_obj:
return JsonResponse({"status": False, "error": "数据不存在!","boid":boid})
else:
# 从数据库中获取到一个对象 row_object
result = {
"status": True,
"data": row_obj,
"boid":boid,
"readonly_data":{
'theBorrow_theUser':str(row_obj2.theBorrow_theUser),
'theBorrow_theBook':str(row_obj2.theBorrow_theBook),
},
}
print('result',result)
return JsonResponse(result)
参考文档
1.Django web 开发(四) - Django项目实践(九)-订单管理
2.https://docs.djangoproject.com/zh-hans/3.2/topics/forms/modelforms/#modelform
3.Django: Disabled Dropdown 不会将信息发送回 POSThttps://devpress.csdn.net/python/6300b81c7e66823466196d6e.html
-
tips: save的功能实现时,ORM不要用value_list,否则会报错AttributeError:‘dict’对象没有属性’_meta’
python - 在Django ModelAdmin中推进queryset。 AttributeError:‘dict’对象没有属性’_meta’ -
用Jquery取label标签时要用$(“#ID名称”).text()取值
https://blog.csdn.net/zgf_along/article/details/6553045
数据验证
forms.py利用clean_theBorrow_status1(self)来实现,触发表单is_valid的认证。
class theBorrowModalShowForm(ModelForm):
# boid = forms.CharField(label='boid',widget=wid.Input(attrs={"class":'edit','readonly':True}))
# theBorrow_add_datetime = forms.CharField(label='theBorrow_add_datetime',widget=wid.Input(attrs={"class":'edit'}))
class Meta:
model = theBorrow
fields = '__all__'
# 无法更改的值直接不要显示
exclude = ['theBorrow_theUser','theBorrow_theBook','theBorrow_duration','theBorrow_status2','theBorrow_status3',]
# 循环找到所有的插件,加入css样式,添加 "class": "form-control"
bootstrap_exclude_fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
for name, field in self.fields.items():
if name in self.bootstrap_exclude_fields:
continue
# class属性追加form-control,其他属性保留
if field.widget.attrs:
field.widget.attrs["class"] = field.widget.attrs.get('class','') + ' ' + 'form-control'+ ' ' + 'edit'
else:
field.widget.attrs = {
"class": "form-control"+ ' ' + 'edit',
}
# 数据校验: 验证方式2
def clean_theBorrow_status1(self):
theBorrow_status1 = self.cleaned_data['theBorrow_status1']
if theBorrow_status1 == '借订中':
# 验证不通过
raise ValidationError('只能填写借订中')
# 验证通过
return theBorrow_status1
views.py
@csrf_exempt
def theBorrowlist_user_modal_save(request):
data = request.POST
print('save-data',data)
boid = request.GET.get('boid')
print('save-boid',boid)
# values方法形成一个对象
# row_obj = theBorrow.objects.filter(boid=boid).values("boid", "theBorrow_add_datetime", "theBorrow_theUser",'theBorrow_theBook','theBorrow_duration','theBorrow_status1').first()
row_obj = theBorrow.objects.filter(boid=boid).first()
if not row_obj:
return JsonResponse({"status": False, "error": "数据不存在!"})
print('row_obj',row_obj)
form = theBorrowModalShowForm(data=data,instance=row_obj)
if form.is_valid():
print('is_valid',request.POST)
form.save()
return JsonResponse({"status": True,"error":'fail-valid',})
return JsonResponse({"status": False,"error":'请检查借订状态',})
手机号搜索
Django web 开发(四) - Django项目实践(五)-靓号管理的【手机号搜索】
关键点在于:pretty_data = PrettyNum.objects.filter(**data_dict)
文件上传
文件上传(见武沛齐链接)
https://poker.blog.csdn.net/article/details/128736292
import re
from django.shortcuts import render,HttpResponse
def upload_list(request):
if request.method == "GET":
return render(request, "upload_list.html")
# 声明图片的对象
file_object = request.FILES.get("avatar")
# 分块进行存储
# file_object.name 表示图片上传时图片本身是什么名字,保存图片时就用什么名字
f = open(file_object.name, mode='wb')
for chunk in file_object.chunks():
f.write(chunk)
f.close()
return HttpResponse("上传成功")
Excel上传(见短信平台做法)
template:利用form的enctype=“multipart/form-data”,并使用input控件添加文件。
<div class="p-3 m-3">
<!-- <div class="p-1 m-1"><h3>WZS HR短信群发平台</h3></div> -->
<div class="p-1 m-1"><h5>1.请按格式上传excel文件</h5></div>
<div class="pl-5"><a class="btn btn-secondary" href="/download_format">下载模板</a></div>
<br>
<form enctype="multipart/form-data" action="" method="POST">
{% csrf_token %}
<div class="pl-5">{{uf.as_p}}</div>
<div class="p-1 m-1"><h5>2.请填写短信文本</h5></div>
<br>
<textarea rows="5" cols="20" class="form-control w-50" type="text" name="shortmail_text" style="font-size:10px;"></textarea>
<br>
<div class="p-1 m-1"><h5>3.发送结果保存地址(everyone权限的公共盘)</h5></div>
<div class="p-1 m-1"><input name="result_address" class="form-control w-50" /></div>
<div class="pl-5"><input class="btn btn-secondary" type="submit" value="确认群发短信"/></div>
</form>
</div>
views.py处理上传excel并存在数据库
# 群发消息
@login_required
def send_mail(request):
if request.method == "POST": #验证POST
uf = UploadEmployeeForm(request.POST,request.FILES) #.post是获取post返回字段,.FILES是获取返回的文件
# print(uf)
print(request.FILES['uploadfile'])
content = request.POST['shortmail_text']
result_address = request.POST['result_address']
print('content',content)
print('result_address',result_address)
print('-----------')
# 定义log收集
successful_logs = []
failed_logs = []
all_logs = []
if uf.is_valid() and content: #判断前台返回的表单是否为有效的类型
ShortMail_Mobile_list = []
wb = load_workbook(filename=request.FILES['uploadfile'])
print(wb)
ws = wb.get_sheet_names()
ws = wb.get_sheet_by_name(ws[0])
max_row = ws.max_row
for row in range(2,max_row+1):
#获取表单元素
ShortMail_Mobile = ws.cell(row=row,column=3).value
#非空则放入列表
if ShortMail_Mobile:
ShortMail_Mobile_list.append(ShortMail_Mobile)
print('ShortMail_Mobile',ShortMail_Mobile)
result = obtain_by_serial(ShortMail_Mobile, content)
# 结果写入表格
print('result',result)
ws.cell(row=row,column=10).value = str(result)
# 结果存入列表中并返回前端
if 'True' in str(result):
all_logs.append('手机号:'+str(ShortMail_Mobile)+',发送成功')
successful_logs.append(ShortMail_Mobile)
else:
all_logs.append('手机号:'+str(ShortMail_Mobile)+',发送失败')
failed_logs.append(ShortMail_Mobile)
# 保存结果
try:
wb.save(result_address+'result.xlsx')
print('ShortMail_Mobile-保存成功')
except Exception as e:
print('ShortMail_Mobile-保存不成功')
print(e)
return render(request,'successful.html',locals())
else:
uf = UploadEmployeeForm()
return render(request,'send_shortmail.html',{'uf':uf})
ModelForm上传照片(图书管理系统做法)
models.py
THEBOOK_TYPE = (('电子书', '电子书'), ('纸质书', '纸质书'), ('PPT', 'PPT'),('项目', '项目'), ('视频', '视频'),('其他', '其他'))
THESTATUS_TYPE = (('已借订', '已借订'), ('未借订', '未借订'))
class theBook(models.Model):
bid = models.AutoField(primary_key=True)
theBook_name = models.CharField(max_length=500,verbose_name=('图书名称'),)
theBook_location = models.ForeignKey(location,blank=True, null=True, on_delete=models.CASCADE,related_name='Book_location')
theBook_type = models.CharField(max_length=500, verbose_name=('电子书/纸质书'),choices=THEBOOK_TYPE,)
theBook_category = models.ForeignKey(category,blank=True, null=True, on_delete=models.CASCADE,related_name='Book_category')
theBook_logo = models.ImageField(max_length=500, verbose_name=('图书照片1'),upload_to='images/')
theBook_attachment = models.FileField(max_length=10000,verbose_name=('图书附件(备用)'),blank=True, null=True,upload_to='file')
theBook_status1 = models.CharField(max_length=500,verbose_name=('图书状态1'),choices=THESTATUS_TYPE,)
theBook_status2 = models.CharField(max_length=500,verbose_name=('图书状态2(备用)'),blank=True, null=True,)
theBook_status3 = models.CharField(max_length=500,verbose_name=('图书状态3(备用)'),blank=True, null=True,)
theBook_id = models.CharField(max_length=500,verbose_name=('图书编号(备用)'),blank=True, null=True,)
theBook_information1 = models.TextField(max_length=10000,verbose_name=('图书信息1'),blank=True, null=True,)
theBook_information2 = models.TextField(max_length=10000,verbose_name=('图书信息2(备用)'),blank=True, null=True,)
theBook_information3 = models.TextField(max_length=10000,verbose_name=('图书信息3(备用)'),blank=True, null=True,)
theBook_information4 = models.TextField(max_length=10000,verbose_name=('图书信息4(备用)'),blank=True, null=True,)
theBook_information5 = models.TextField(max_length=10000,verbose_name=('图书信息5(备用)'),blank=True, null=True,)
class Meta:
verbose_name = ('图书')
verbose_name_plural = ('图书')
ordering = ['theBook_category'] # 返回值排序
def __str__(self):
return str(self.theBook_category)+str(self.theBook_name)
forms.py
class theBookForm(ModelForm):
class Meta:
model = theBook
fields = '__all__'
exclude = ['theBook_id','theBook_attachment','theBook_status1','theBook_status2','theBook_status3','theBook_information2',
'theBook_information3','theBook_information4','theBook_information5',]
error_messages = {
'theBook_name':{'required':"theBook_name不能为空",},
}
# labels= {
# "theBook_name":"自定义书名"
# }
widgets = {
"theBook_name":wid.Input(attrs={"class":"c1"}) #还可以自定义属性
}
# 循环找到所有的插件,加入css样式,添加 "class": "form-control"
bootstrap_exclude_fields = ['theBook_logo',]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段的插件设置
for name, field in self.fields.items():
if name in self.bootstrap_exclude_fields:
continue
# class属性追加form-control,其他属性保留
if field.widget.attrs:
field.widget.attrs["class"] = field.widget.attrs.get('class','') + ' ' + 'form-control'
else:
field.widget.attrs = {
"class": "form-control",
}
views.py直接引用ModelForm
def theBooklist_new(request):
if request.method == "GET":
form = theBookForm()
return render(request,'theBooklist_new.html',{'form':form})
# 用户POST请求提交数据,需要进行数据校验
form = theBookForm(data=request.POST,files=request.FILES)
if form.is_valid():
# print(form.cleaned_data)
# 直接保存至数据库
form.save()
return redirect("/theBooklist_show/")
return render(request,'theBooklist_new.html',{'form':form})
参考文档
DjangoWeb框架基础
https://blog.csdn.net/qq_39330486/article/details/120292603
jQuery
Django web开发(一) - 前端中的【7.jQuery】