【武沛齐Django】图书管理系统的刻意练习(第12个网站)--20230406

news/2024/10/20 1:26:17/

武沛齐课程笔记链接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 = theBookfields = '__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_allif 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/">&laquo; 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 &raquo;</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_allif 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语句

Django web开发(二) - Mysql数据库

执行原生 SQL 查询
在这里插入图片描述

方式1:views.py执行原生SQL查询生成 RawQuerySet
# 练习原始SQL
def categorylist_show(request):    # categorylist = category.objects.all()# 练习原始SQLcategorylist = 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 = Userfields = ['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 settingsdef 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 = Adminfields = ["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, redirectclass 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()# 清除指定sessiondel 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_objectresult = {"status": True,"data": row_obj,"boid":boid,}print('result',result)return JsonResponse(result)@csrf_exempt
def theBorrowlist_user_modal_save(request):data = request.POSTprint('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.GETboid = 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 = theBorrowfields = '__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 = theBorrowfields = '__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/">&laquo; 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 &raquo;</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">&times;</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">&times;</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">&times;</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');//获取当前行的IDDELETE_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 = theBorrowfields = '__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">&times;</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_objectresult = {"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

  1. tips: save的功能实现时,ORM不要用value_list,否则会报错AttributeError:‘dict’对象没有属性’_meta’
    python - 在Django ModelAdmin中推进queryset。 AttributeError:‘dict’对象没有属性’_meta’

  2. 用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 = theBorrowfields = '__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',}# 数据校验: 验证方式2def 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.POSTprint('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,HttpResponsedef 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":  #验证POSTuf = 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_rowfor 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 = theBookfields = '__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】

专业图标网fontawesome

【图标】fontawesome专业图标网–20230413


http://www.ppmy.cn/news/416158.html

相关文章

腾讯确认QQ大规模盗号,iPhone14无缘Type-C,第四大运营商5G正式放号,今日更多大新闻在此...

日报君 发自 凹非寺量子位 | 公众号 QbitAI 大家好&#xff0c;今天是6月27日星期一。 全北京等了一天的暴雨&#xff0c;没来。你的城市下雨了吗&#xff1f; 新的一周&#xff0c;来看看今天有哪些值得关注的科技新闻。 今日大新闻 腾讯回应大规模用户反馈QQ账号被盗 昨天夜里…

公众号开发(二)--菜单管理

公众号开发&#xff08;二&#xff09;--菜单管理 开发说明 以下是官网的说明 1、自定义菜单最多包括3个一级菜单&#xff0c;每个一级菜单最多包含5个二级菜单。 2、一级菜单最多4个汉字&#xff0c;二级菜单最多7个汉字&#xff0c;多出来的部分将会以“...”代替。 3、创建自…

一键跳转添加QQ好友 点击链接直接跳转到QQ好友页面如何实现

网页可以唤起QQ群&#xff0c;这我们都知道可以做到&#xff0c;那如何唤起呢&#xff1f;下面就做一个简单的介绍&#xff0c;希望可以帮助到有需要的朋友 1、官方提供的几种加群的链接: 官方的加群代码的获取前提是我们具有权限&#xff08;也就是群主或管理权限&#xff09…

学成在线--认证授权模块

完整版请移步至我的个人博客查看&#xff1a;https://cyborg2077.github.io/ 学成在线–项目环境搭建 学成在线–内容管理模块 学成在线–媒资管理模块 学成在线–课程发布模块 学成在线–认证授权模块 学成在线–选课学习模块 学成在线–项目优化 Git仓库&#xff1a;…

C#图书馆租赁管理系统

本系统是为书籍管理而设计的高效管理系统,系统功能强大,同时操作界面简洁,内容比较简单,而管理人员大多受到过系统的培训,并能够操作电脑,所以只要花很少的时间,就能够让他们熟悉本系统。 系统开发的总体任务是实现各种图书信息的系统化、规范化和自动化。系统功能分析…

迈乐a100+Linux,迈乐A100双核、M3固件V4.04YYF定制版

官方更新&#xff1a; 优化输出分辨率设成1080P时&#xff0c;通过迈乐电视助手推送到手机上显示不全问题&#xff1b; 优化在线升级&#xff0c;减少升级出错几率&#xff1b; 版本&#xff1a;4.04 适用机型&#xff1a;迈乐A100双核、M3 ----------------------------------…

Redis和Redis可视化管理工具的下载和安装

文章目录 Redis 简介一&#xff0c;Redis 下载二&#xff0c;Redis 安装三&#xff0c;Redis 配置四&#xff0c;Redis 启动 Redis-Desktop-Manager 简介一&#xff0c;Redis-Desktop-Manager 下载二&#xff0c;Redis-Desktop-Manager 安装三&#xff0c;Redis-Desktop-Manage…

Linux x86_64平台同时编译x86_64和arm64两个架构的Qt应用程序出现XRes库无法找到

一 背景 在ubuntu x86_64平台上需要同时编译x86_64和arm64两个架构的Qt应用程序。在实践过程中&#xff0c;发现XRes库只能安装在其中一个平台。 二 根因 安装amd64版本的XRes库会删除arm64版本的库&#xff0c;反之亦然。 在安装amd64版本时&#xff0c;会删除arm64版本&a…