django开发
django开发
1. 创建项目
- 创建后,删除settings.py 中
BASE_DIR / 'templates'
- 在删除templates文件
2. 创建app
python manage.py startapp myapp
- 注册app
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp.apps.MyappConfig'
]
3. 设计表结构
from django.db import models
# Create your models here.
class Department(models.Model):
"""部门表"""
title = models.CharField(max_length=32, verbose_name='标题')
class UserInfo(models.Model):
"""员工表"""
name = models.CharField(max_length=16, verbose_name="姓名")
password = models.CharField(max_length=64, verbose_name="密码")
age = models.IntegerField(verbose_name="年龄")
account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)
create_time = models.DateTimeField(verbose_name="入职时间")
# 1. 外键约束
# - to 表示与哪张表关联
# - to_field 表示表中的哪一列
# 在django中,数据表中的名称自动加上_id,也就是depart_id
# on_delete=models.CASCADE 表示级联删除(删除部门,部门下的所有员工都会被删除)
depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE, verbose_name="部门")
# on_delete=models.SET_NULL, null=True, blank=True 表示置空(删除部门,部门下的所有员工的部门字段置为空)
#depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.SET_NULL, null=True, blank=True)
gender_choices = (
(1, "男"),
(2, "女"),
)
gender = models.SmallIntegerField(choices=gender_choices,verbose_name="性别")
4. 在MySQL中生成表
-
连接MySQL生成数据库
create database department DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
-
修改配置文件【settings.py】文件
DATABASES = { 'default':{ 'ENGINE':'django.db.backends.mysql', 'NAME':'department', 'USER':'root', 'PASSWORD':'lzy13691870375', 'HOST':'127.0.0.1', 'PORT':'3306', } }
-
django命令生成数据库表
python manage.py makemigrations python manage.py migrate
mysql> desc myapp_department; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | bigint | NO | PRI | NULL | auto_increment | | title | varchar(32) | NO | | NULL | | +-------+-------------+------+-----+---------+----------------+ mysql> desc myapp_userinfo; +------------+---------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+---------------+------+-----+---------+----------------+ | id | bigint | NO | PRI | NULL | auto_increment | | name | varchar(16) | NO | | NULL | | | password | varchar(64) | NO | | NULL | | | age | int | NO | | NULL | | | account | decimal(10,2) | NO | | NULL | | | creat_time | datetime(6) | NO | | NULL | | | gender | smallint | NO | | NULL | | | depart_id | bigint | NO | MUL | NULL | | +------------+---------------+------+-----+---------+----------------+
5. 静态文件管理
app文件下创建static文件和templates文件
6.部门管理
- 添加
- 删除
- 编辑
编辑【urls.py】(动态url):
urlpatterns = [
path('depart/list/', views.depart_list),
path('depart/add/', views.depart_add),
path('depart/delete/', views.depart_delete),
path('depart/<int:nid>/edit/', views.depart_edit),
]
【view.py】
def depart_edit(request,nid):
"""部门编辑"""
# 根据nid 编辑他的数据
if request.method=="GET":
row_object = models.Department.objects.filter(id=nid).first()
return render(request,"depart_edit.html",{"row_object":row_object})
if request.method == "POST":
depart_title = request.POST.get("title")
models.Department.objects.filter(id=nid).update(title=depart_title)
#重定向部门列表
return redirect("/depart/list/")
通过nid参数来编辑数据
<a class="btn btn-primary btn-sm" href="/depart/{{ obj.id }}/edit/">编辑</a>
7. 模板的继承
定义模板:
【layout.html】
<!-- 新建区域 -->
<div>
{% block content %}{% endblock %}
</div>
继承模板
{% extends 'layout.html' %}
{% block content %}
<h1>首页</h1>
{% endblock %}
8. 用户管理
mysql> desc myapp_userinfo;
+-------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+----------------+
| id | bigint | NO | PRI | NULL | auto_increment |
| name | varchar(16) | NO | | NULL | |
| password | varchar(64) | NO | | NULL | |
| age | int | NO | | NULL | |
| account | decimal(10,2) | NO | | NULL | |
| create_time | datetime | YES | | NULL | |
| gender | smallint | NO | | NULL | |
| depart_id | bigint | NO | MUL | NULL | |
+-------------+---------------+------+-----+---------+----------------+
insert into myapp_userinfo(name,password,age,account,create_time,gender,depart_id) values("李云龙","123456",45,50000,"2020-03-24",1,1);
insert into myapp_userinfo(name,password,age,account,create_time,gender,depart_id) values("张三丰","123456",45,60000,"2021-03-24",1,1);
insert into myapp_userinfo(name,password,age,account,create_time,gender,depart_id) values("周杰伦","123456",45,70000,"2022-03-24",1,1);
mysql> select * from myapp_userinfo;
+----+--------+----------+-----+----------+---------------------+--------+-----------+
| id | name | password | age | account | create_time | gender | depart_id |
+----+--------+----------+-----+----------+---------------------+--------+-----------+
| 4 | 李云龙 | 123456 | 45 | 50000.00 | 2020-03-24 00:00:00 | 1 | 1 |
| 5 | 张三丰 | 123456 | 45 | 60000.00 | 2021-03-24 00:00:00 | 1 | 1 |
| 6 | 周杰伦 | 123456 | 45 | 70000.00 | 2022-03-24 00:00:00 | 1 | 1 |
+----+--------+----------+-----+----------+---------------------+--------+-----------+
def user_list(request):
# 获取所有用户列表
user_data = UserInfo.objects.all()
# 用 python 的语法获取数据
"""
for obj in user_data:
# obj.get_gender_display() 表示匹配 男/女,原始字段名为gender,obj.get_字段名称_display()
# obj.create_time.strftime("%Y-%m-%d") 表示将时间格式转换成固定格式的字符串
# obj.depart.title 表示获取depart_id对应的部门名称,因为我们在models中定义表时与另外一张表设置了级联关系,有外键
print(obj.id, obj.name, obj.password, obj.age, obj.account, obj.get_gender_display(), obj.depart.title, obj.create_time.strftime("%Y-%m-%d"))
"""
return render(request, "user_list.html", {"user_data": user_data})
注意: HTML 中获取数据的方式与 Python 中有些不同
例如:
1.HTML中引入函数不能带括号, obj.get_gender_display()
2.日期类型转字符串有Django自己的格式, obj.create_time|date:“Y-m-d”{% for obj in user_data %} <tr> <th scope="row">{{ obj.id }}</th> <td>{{ obj.name }}</td> <td>{{ obj.password }}</td> <td>{{ obj.age }}</td> <td>{{ obj.account }}</td> <td>{{ obj.create_time|date:"Y-m-d" }}</td> <td>{{ obj.get_gender_display }}</td> <td>{{ obj.depart.title }}</td> <td> <a class="btn btn-primary btn-sm" href="#">编辑</a> <a class="btn btn-danger btn-sm" href="#">删除</a> </td> </tr> {% endfor %}
8.1 新建用户:
-
原始思路:
-
数据校验较麻烦
-
页面没有错误提示
-
页面上每一个字段都需要重新写一遍
-
关联的数据,需要手动获取并循环展示在页面
-
-
Django组件:
-
Form组件(简便)
-
ModelForm组件(最简便)
-
1 初识Form
1. views.py
class MyForm(Form):
user = forms.CharField(widget=forms.Input)
pwd = forms.CharField(widget=forms.Input)
email = forms.CharField(widget=forms.Input)
def user_add(request):
if request.method == "GET":
form = MyForm()
return render(request, "user_add.html", {"form": form})
2. user_add.html
<form method="post">
{{ form.user }}
{{ form.pwd }}
{{ form.email }}
</form>
<form method="post">
{% for field in form %}
{{ field }}
{% endfor %}
</form>
2. ModelForm
0. models.py
class UserInfo(models.Model):
"""员工表"""
name = models.CharField(verbose_name="姓名",max_length=16)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄")
account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)
create_time = models.DateTimeField(verbose_name="入职时间")
# 级联删除
depart = models.ForeignKey(to="Department",to_field="id",on_delete=models.CASCADE,verbose_name="部门id")
#在django中做约束
gender_choices = (
(1,"男"),
(2,"女"),
)
gender = models.SmallIntegerField(verbose_name="性别",choices=gender_choices)
1. views.py
class MyForm(ModelForm):
class Meta:
model = UserInfo
field = ["name", "password", "age"]
def user_add(request):
if request.method == "GET":
form = MyForm()
return render(request, "user_add.html", {"form": form})
2. user_add.html
<form method="post">
{{ form.user }}
{{ form.pwd }}
{{ form.email }}
</form>
<form method="post">
{% for field in form %}
{{ field }}
{% endfor %}
</form>
8.2 用户添加(ModelForm)
【views.py】
#################################### Model Form演示 ###############################
from django import forms
from myapp.models import UserInfo
class UserModelForm(forms.ModelForm):
class Meta:
model = UserInfo
fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
# 逐一控制标签的样式
# widgets = {
# "name": forms.TextInput(attrs={"class": "form-control"}),
# "password": forms.PasswordInput(attrs={"class": "form-control"}),
# }
# 循环找到所有的插件,添加 "class": "form-control"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
# 可以排除指定的字段
if name == "create_time":
continue
print(name, field)
field.widget.attrs = {"class": "form-control"}
def user_add(request):
"""新建用户(ModelForm版本)"""
if request.method == "GET":
form = UserModelForm()
return render(request, "user_add.html",{"form":form})
elif request.method == "POST":
form = UserModelForm(data=request.POST)
if form.is_valid():
print(form.cleaned_data)
# 直接保存到数据库
form.save()
return redirect("/user/list/")
# 校验失败 返回页面错误信息
return render(request,"user_add.html",{"form":form})
- 展示部门名字,需要定义__str__函数,返回名称
【models.py】
class Department(models.Model):
"""部门表"""
title = models.CharField(max_length=32, verbose_name='标题')
def __str__(self):
return self.title
8.3 编辑用户
【urls.py】
path('user/<int:nid>/edit/', views.user_edit)
【views.py】
def user_edit(request , nid):
""" 编辑用户 """
#根据id去数据库获取要编辑的那一行(对象)
row_object = models.UserInfo.objects.filter(id=nid).first()
if request.method == "GET":
form = UserModelForm(instance=row_object)
return render(request,"user_edit.html",{'form':form})
elif request.method == "POST":
form = UserModelForm(data=request.POST,instance=row_object)
if form.is_valid():
form.save()
return redirect('/user/list/')
return render(request,'user_edit.html',{"form":form})
使用form = UserModelForm(instance=row_object)
instance 参数实现ModelForm展示
8.4 删除用户
和之前一样
9. 靓号管理
9.1 表结构
# models.py
class PrettyNum(models.Model):
""" 靓号表 """
mobile = models.CharField(verbose_name="手机号" , max_length=11)
#想要允许为空 null=True , blank=True
price = models.IntegerField(verbose_name="价格")
level_choices=(
(1, "1级"),
(2, "2级"),
(3, "3级"),
(4, "4级"),
)
level = models.SmallIntegerField(verbose_name="级别",choices=level_choices,default=1)
status_choices = (
(1, "已占用"),
(2, "未占用"),
)
status = models.SmallIntegerField(verbose_name="状态",choices=status_choices,default=2)
自己模拟语句:
insert into myapp_prettynum(mobile,price,level,status)values("13333333333",19,1,2);
9.2 靓号列表
def pretty_list(request):
""" 靓号列表 """
# select * from 表 order by level desc
prettyNum = models.PrettyNum.objects.all().order_by("-level")
return render(request,"pretty_list.html",{"prettyNum":prettyNum})
9.3 新建靓号
- 使用ModelForm实现
# 导入正则表达式
from django.core.validators import RegexValidator
""" 自定义ModelForm """
class PrettyModelForm(forms.ModelForm):
# 验证方式 1:
mobile = forms.CharField(
label = "手机号",
validators=[RegexValidator(r'^1[3-9]\d{9}$','手机号格式错误')]
)
class Meta:
model = models.PrettyNum
# fields = "__all__" 选择所有字段
fields = ["mobile", "price", "level", "status"]
# exclude = ['level'] 排除某个字段
# 循环找到所有的插件,添加 "class": "form-control"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
# 可以排除指定的字段
# if name == "create_time":
# continue
print(name, field)
field.widget.attrs = {"class": "form-control"}
def pretty_add(request):
""" 新建靓号 """
if request.method == "GET":
form = PrettyModelForm()
return render(request,"pretty_add.html",{"form": form})
elif request.method == "POST":
form = PrettyModelForm(data= request.POST)
if form.is_valid():
# 直接保存数据库
form.save()
return redirect("/pretty/list/")
#校验失败
return render(request,"pretty_add.html",{"form":form})
-
点击提交
-
数据校验
# 验证方式 2: def clean_mobile(self): txt_mobile = self.cleaned_data["mobile"] # 验证不通过 if len(txt_mobile)!= 11: raise ValidationError("长度不为11!") #验证通过 返回值 return txt_mobile
-
保存数据库
-
跳回靓号列表
-
9.4 编辑靓号
class PrettyEditModelForm(forms.ModelForm):
# 验证方式 1:
mobile = forms.CharField(
label = "手机号",
validators=[RegexValidator(r'^1[3-9]\d{9}$','手机号格式错误')]
)
# 不给用户编辑手机号
# mobile = forms.CharField(disabled=True,label="手机号")
class Meta:
model = models.PrettyNum
# fields = "__all__" 选择所有字段
fields = ["mobile", "price", "level", "status"]
# exclude = ['level'] 排除某个字段
# 循环找到所有的插件,添加 "class": "form-control"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
# 可以排除指定的字段
# if name == "create_time":
# continue
print(name, field)
field.widget.attrs = {"class": "form-control"}
# 钩子函数 判断手机号是否存在
def clean_mobile(self):
txt_mobile = self.cleaned_data['mobile']
if len(txt_mobile) != 11:
# 验证不通过
raise ValidationError('格式错误')
# exclude 表示排除哪一个数据
# self.instance.pk 表示当前编辑的哪一行 id
exists_data = models.PrettyNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists()
if exists_data:
raise ValidationError("手机号已存在")
# 验证通过
return txt_mobile
def pretty_edit(request,nid):
""" 编辑靓号 """
row_object = models.PrettyNum.objects.filter(id=nid).first()
if request.method=="GET":
form = PrettyEditModelForm(instance=row_object)
return render(request,"pretty_edit.html",{"form": form})
elif request.method=="POST":
form = PrettyEditModelForm(data = request.POST,instance=row_object)
if form.is_valid():
form.save()
return redirect("/pretty/list/")
return render(request,"pretty_edit.html",{"form":form})
不允许手机号重复:
- 添加: 如果手机号已存在,提示"手机号已存在"
# 验证方式 2 验证是否存在:
def clean_mobile(self):
txt_mobile = self.cleaned_data["mobile"]
# 验证是否存在
exist = models.PrettyNum.objects.filter(mobile=txt_mobile).exists()
if exist:
raise ValidationError("手机号已存在")
#验证通过 返回值
return txt_mobile
- 编辑: 如果手机号除了当前手机号以外已存在,提示"手机号已存在"
def clean_mobile(self):
txt_mobile = self.cleaned_data['mobile']
if len(txt_mobile) != 11:
# 验证不通过
raise ValidationError('格式错误')
# exclude 表示排除哪一个数据
# self.instance.pk 表示当前编辑的哪一行 id
exists_data = models.PrettyNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists()
if exists_data:
raise ValidationError("手机号已存在")
# 验证通过
return txt_mobile
9.5 搜索手机号
query1 = PrettyNum.objects.filter(mobile=15576547933, id=4)
print(query1)
# 如果是空字典,表示获取所有
query_dict = {"mobile": "15576547933", "id": 4}
query2 = PrettyNum.objects.filter(**query_dict)
print(query2)
PrettyNum.objects.filter(id=4) # 等于4
PrettyNum.objects.filter(id__gt=4) # 大于4
PrettyNum.objects.filter(id__gte=4) # 大于等于4
PrettyNum.objects.filter(id__lt=4) # 小于4
PrettyNum.objects.filter(id__lte=4) # 小于等于4
PrettyNum.objects.filter(mobile__startswith="1999") # 筛选出以"1999"开头的
PrettyNum.objects.filter(mobile__endswith="1999") # 筛选出以"1999"结尾的
PrettyNum.objects.filter(mobile__contains="1999") # 筛选出包含"1999"开头的
使用get请求来获取用户输入内容,然后来查询
# views.py
def pretty_list(request):
""" 靓号列表 """
data_dict={}
search_data = request.GET.get("q","")
if search_data:
data_dict["mobile__contains"] = search_data
# select * from 表 order by level desc
prettyNum = models.PrettyNum.objects.filter(**data_dict).order_by("-level")
return render(request,"pretty_list.html",{"prettyNum":prettyNum,"search_data":search_data})
9.6 分页
def pretty_list(request):
# for i in range(100,1000):
# models.PrettyNum.objects.create(mobile="18888888{}".format(str(i)),price=88,level=2,status=2)
""" 靓号列表 """
data_dict = {}
search_data = request.GET.get("q", "")
if search_data:
data_dict["mobile__contains"] = search_data
# 1.根据用户访问的页码,计算起始位置和末尾位置
page = int(request.GET.get("page", 1))
page_size = 8
start = (page - 1) * page_size
end = page * page_size
# 配置页码页面
# select * from 表 order by level desc
prettyNum = models.PrettyNum.objects.filter(**data_dict).order_by("-level")[start:end]
# 数据总条数
total_count = models.PrettyNum.objects.filter(**data_dict).order_by("-level").count()
# 计算总页码
total_page, div = divmod(total_count, page_size)
if div:
total_page += 1
# 计算出当前页的前5页和后5页
plus = 5
if total_page <= 2 * plus + 1: # 判断总页数是否大于11,数据库数据较少的情况
start_page = 1
end_page = total_page
else:
# 数据库数据大于11页
# 当前页小于等于5时
if page <= plus:
start_page = 1
end_page = 2 * plus + 1
# 当前页
elif page + plus >= total_page:
start_page = total_page - 2 * plus
end_page = total_page
else:
start_page = page - plus
end_page = page + plus + 1
page_str_list = []
#首页
page_str_list.append('<li class="page-item "><a class="page-link" href="/pretty/list/?page=1">首页</a></li>')
# 上一页
if page > 1:
prev = '<li class="page-item "><a class="page-link" href="/pretty/list/?page={}">上一页</a></li>'.format(page - 1)
else:
prev = '<li class="page-item "><a class="page-link" href="/pretty/list/?page={}">上一页</a></li>'.format(1)
page_str_list.append(prev)
for i in range(start_page, end_page + 1):
if i == page:
ele = '<li class="page-item active"><a class="page-link" href="/pretty/list/?page={}">{}</a></li>'.format(i,
i)
else:
ele = '<li class="page-item"><a class="page-link" href="/pretty/list/?page={}">{}</a></li>'.format(i, i)
page_str_list.append(ele)
# 下一页
if page < total_page:
prev = '<li class="page-item "><a class="page-link" href="/pretty/list/?page={}">下一页</a></li>'.format(page + 1)
else:
prev = '<li class="page-item "><a class="page-link" href="/pretty/list/?page={}">下一页</a></li>'.format(total_page)
# 尾页
page_str_list.append('<li class="page-item "><a class="page-link" href="/pretty/list/?page={}">尾页</a></li>'.format(total_page))
page_string = mark_safe("".join(page_str_list))
return render(request, "pretty_list.html",
{"prettyNum": prettyNum, "search_data": search_data, "page_string": page_string})
9.7 时间插件
<script>
$(function(){
$('#id_create_time').datetimepicker({
format: 'yyyy-mm-dd',
startDate:'0',
language:"zh-CN",
autoclose:true
});
})
</script>
9.8 ModelForm 和 BootStrap
-
ModelForm 可以帮我们生成HTML标签
class UserModelForm(forms.ModelForm): class Meta: model = UserInfo fields = ["name", "password", "age", "account", "create_time", "gender", "depart"] form = UseModelForm()
{{form.name}} 普通的input 框
-
定义插件
class UserModelForm(forms.ModelForm): class Meta: model = UserInfo fields = ["name", "password", "age", "account", "create_time", "gender", "depart"] # 逐一控制标签的样式 widgets = { "name": forms.TextInput(attrs={"class": "form-control"}), "password": forms.PasswordInput(attrs={"class": "form-control"}), }
class UserModelForm(forms.ModelForm): name = forms.CharField( min_length = 3, label = "用户名", widget = forms.TextInput(attrs={"class":"form-control"}) ) class Meta: model = UserInfo fields = ["name", "password", "age", "account", "create_time", "gender", "depart"] # 逐一控制标签的样式
-
重新定义的init方法,批量设置
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for name, field in self.fields.items(): # 可以排除指定的字段 # if name == "create_time": # continue print(name, field) field.widget.attrs = {"class": "form-control"}
10. 管理员操作
添加管理员
新增确认密码字段:
class AdminModelForm(forms.ModelForm):
confirm_password = forms.CharField(
label="确认密码",
widget=forms.PasswordInput
)
class Meta:
model = models.Guanly
fields = ["name" , "password", "confirm_password"]
widgets={
"password":forms.PasswordInput
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
# 可以排除指定的字段
# if name == "create_time":
# continue
print(name, field)
field.widget.attrs = {"class": "form-control"}
验证确认密码是否一致(钩子函数):
def clean_confirm_password(self):
pwd = self.cleaned_data.get("password")
confirm = self.cleaned_data.get("confirm_password")
if confirm!=pwd:
raise ValidationError("密码不一致,请重新输入")
密码和确认密码报错后不想让它删除:加上关键字 render_value = True
class AdminModelForm(forms.ModelForm):
confirm_password = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(render_value=True)
)
class Meta:
model = models.Guanly
fields = ["name" , "password", "confirm_password"]
widgets={
"password":forms.PasswordInput(render_value=True)
}
md5 加密
from django.conf import settings
import hashlib
def md5(data_string):
obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))
obj.update(data_string.encode('utf-8'))
return obj.hexdigest()
# 加密数据
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd)
重置密码
使新密码不与旧密码相同:self.instance.pk
获取当前对象的id(主键)
def clean_password(self):
pwd = self.cleaned_data.get("password")
md5_pwd = md5(pwd)
# 去数据库校验当前密码是否一致
exists = models.Guanly.objects.filter(id=self.instance.pk,password=md5_pwd).exists()
if exists:
raise ValidationError("密码不能与之前的相同")
return md5(pwd)
用户登入
- 无状态 & 短连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M6fLIQLA-1692900549258)(C:\Users\camel\Documents\WeChat Files\wxid_hfsdabob3o0j22\FileStorage\Temp\1692000273292.png)]
cookie 和 session
http://127.0.0.1:8000/admin/list/
https://127.0.0.1:8000/admin/list/
用户认证
- 验证输入是否正确
- 验证用户名和密码是否正确(去数据库校验)
class LoginForm(forms.Form):
name = forms.CharField(
label = "用户名",
widget=forms.TextInput(attrs={"class":"form-control","id":"exampleInputEmail1"," aria-describedby":"emailHelp", "placeholder":"请输入用户名"})
)
password = forms.CharField(
label="密码",
widget=forms.PasswordInput(render_value=True,attrs={"class":"form-control","id":"exampleInputPassword1", "placeholder":"请输入密码"})
)
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd)
def account_login(request):
""" 登入 """
if request.method == "GET":
form = LoginForm()
return render(request,"login.html",{"form":form})
elif request.method == "POST":
form = LoginForm(data = request.POST)
if form.is_valid():
# 验证成功 获取用户名和密码 form.cleaned_data
# 去数据库校验用户名和密码是否正确
print(form.cleaned_data)
# 去数据库校验 用户名和密码是否正确,获取用户对象
# admin_object = models.Guanly.objects.filter(username = "xxx",password = "xxx").first()
admin_object = models.Guanly.objects.filter(**form.cleaned_data).first()
# 输入错误
if not admin_object:
form.add_error("password","用户名或者密码错误")
return render(request,'login.html',{"form":form})
# 输入正确
# 网站生成随机字符串; 写到用户浏览器cookie中; 再写入到session中;
request.session['info'] = {'id':admin_object.id,'name':admin_object.name}
return redirect('/admin/list/')
return render(request,"login.html",{"form":form})
-
登入成功才能有后台管理,没有登入的用户看不到内容,返回登入窗口
- cookie,随机字符串
- session,用户信息
在其他需求登入才能访问的页面中。都需要加入这段代码(也可以用中间件实现)
def index(request):
info = request.session.get('info')
if not info:
return redirect('/login/')
...
中间件
-
定义中间件,新建个目录middleware 目录下新建auth.py
class M1(MiddlewareMixin): """ 中间件1 """ def process_request(self, request): print("M1 进来了") def process_response(self, request, response): print("M1 走了") return response class M2(MiddlewareMixin): """ 中间件2 """ def process_request(self, request): print("M2 进来了") def process_response(self, request, response): print("M2 走了") return response
-
应用中间件settings.py
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'myapp.middleware.auth.M1', 'myapp.middleware.auth.M2', ]
-
中间件的process_request 方法
方法中没有返回值则继续往后走 有返回值 HttpResponse render redirect,则不再继续向后执行
中间件实现登入校验
-
定义中间件,新建个目录middleware 目录下新建auth.py
class AuthMiddleware(MiddlewareMixin): """ 中间件 """ def process_request(self, request): # 排除不需要登入就能访问的页面 获取当前用户URL:request.path_info if request.path_info == "/account/login/": return # 读取当前访问用户的session信息,若能读到,说明登入过,就可以向后走 info_dict = request.session.get("info") print(info_dict) # 如果登入过 if info_dict: return # 没有登入过 return redirect('/account/login/') def process_response(self, request, response): print("M1 走了") return response
-
应用中间件settings.py
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'myapp.middleware.auth.AuthMiddleware', ]
-
中间件的process_request 方法
方法中没有返回值则继续往后走 有返回值 HttpResponse render redirect,则不再继续向后执行
注销
def account_logout(request):
""" 注销 """
request.session.clear()
return redirect('/account/login/')
HTML中使用{{ request.session.info.name }} 显示用户名称
图片验证码
生成图片
pip install pillow
- 生成图片验证码函数:
import random
from PIL import Image,ImageDraw,ImageFont,ImageFilter
"""
生成随机图片验证码
"""
def check_code(width=120, height=30, char_length=5, font_file='kumo.ttf', font_size=28):
"""
生成随机图片验证码
"""
code = []
img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
def rndChar():
"""
生成随机字母
:return:
"""
return chr(random.randint(65, 90))
def rndColor():
"""
生成随机颜色
:return:
"""
return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))
# 写文字
font = ImageFont.truetype(font_file, font_size)
for i in range(char_length):
char = rndChar()
code.append(char)
h = random.randint(0, 4)
draw.text([i * width / char_length, h], char, font=font, fill=rndColor())
# 写干扰点
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
# 写干扰圆圈
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
x = random.randint(0, width)
y = random.randint(0, height)
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())
# 画干扰线
for i in range(5):
x1 = random.randint(0, width)
y1 = random.randint(0, height)
x2 = random.randint(0, width)
y2 = random.randint(0, height)
draw.line((x1, y1, x2, y2), fill=rndColor())
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
return img, ''.join(code)
- 生成图片:
from io import BytesIO
def image_code(request):
""" 生成图片验证码 """
# 调用函数
img,code_string = check_code()
print(code_string)
# 写入session中(以便今后获取验证码校验)
request.session['image_code'] = code_string
# 给session设置60秒超时
request.session.set_expiry(60)
print(img,code_string)
# 使用内存文件
stream = BytesIO()
img.save(stream,'png')
return HttpResponse(stream.getvalue())
- 验证码校验:
def account_login(request):
""" 登入 """
if request.method == "GET":
form = LoginForm()
return render(request,"login.html",{"form":form})
elif request.method == "POST":
form = LoginForm(data = request.POST)
if form.is_valid():
# 验证码校验
user_input_code = form.cleaned_data.pop('image_code') # 获取用户填写的code 并且剔除去
code = request.session.get('image_code',"")
if code.upper()!=user_input_code.upper(): # 验证码填写错误
form.add_error("image_code", "验证码错误")
return render(request, 'login.html', {"form": form})
- 设置session保存7天
# session 保存7天
request.session.set_expiry(60 * 60 * 24 * 7)
11. Ajax 请求
浏览器向网站发送请求时,URL 和 表单的形式提交
- GET
- POST
特点:页面刷新。
Ajax可以实现向后台偷偷发送请求
-
依赖jQuery
-
编写ajax代码
$.ajax({ url:"发送的地址", type: "get", data:{ n1:123, n2:456 }, success:function(res){ console.log(res); } })
11.1 get请求
<script type="text/javascript">
function clickMe(){
$.ajax({
url:'/task/ajax/',
type:'get',
data:{
n1:123,
n2:456,
},
success:function(res){
console.log(res)
}
})
}
</script>
path('task/ajax/', task.task_ajax),
def task_ajax(request):
print(request.GET)
return HttpResponse("成功了")
11.2 POST请求
添加 @csrf_exempt 可以免除 {% csrf_token %}
from django.shortcuts import render,HttpResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def task_ajax(request):
print("get请求: ", request.GET)
print("post请求: ", request.POST)
return HttpResponse("成功了")
11.3 绑定事件
<script type="text/javascript">
$(function(){
// 页面加载完成后代码自动执行
bindBtn1Event();
})
function bindBtn1Event()
{
$("#btn1").click(function () {
$.ajax({
url: '/task/ajax/',
type: 'get',
data: {
n1: 123,
n2: 456,
},
success: function (res) {
console.log(res)
}
})
})
}
11.4 ajax请求的返回值
以JSON的方式返回数据
- 前端需要加上 dataType:“JSON”
<script type="text/javascript">
$(function(){
// 页面加载完成后代码自动执行
bindBtn1Event();
})
function bindBtn1Event()
{
$("#btn1").click(function () {
$.ajax({
url: '/task/ajax/',
type: 'get',
data: {
n1: 123,
n2: 456,
},
dataType:"JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
})
}
</script>
- 后端可以通过 JsonResponse(data_dict) 返回json数据
@csrf_exempt
def task_ajax(request):
print("get请求: ", request.GET)
print("post请求: ", request.POST)
data_dict = {"status":True,"data":[11,22,33,44]}
# return JsonResponse(data_dict)
return HttpResponse(json.dumps(data_dict))
- 返回的三种方式
{% extends 'layout.html' %}
{% block content %}
<h1>任务列表</h1>
<h3> 实例 1</h3>
<input type="button" value="点击我" id="btn1" onclick="clickMe()">
<h3> 实例 2</h3>
<input type="text" id="txtUser" placeholder="姓名">
<input type="text" id="txtAge" placeholder="年龄">
<input type="button" id="btn2" value="点击">
<h3> 实例 3</h3>
<form id="form3">
<input type="text" name="User" placeholder="姓名">
<input type="text" name="Age" placeholder="年龄">
<input type="text" name="pwd" placeholder="密码">
<input type="text" name="mobile" placeholder="年龄">
</form>
<input type="button" id="btn3" value="点击">
{% endblock %}
{% block js %}
<script type="text/javascript">
$(function () {
// 页面加载完成后代码自动执行
bindBtn1Event();
bindBtn2Event();
bindBtn3Event();
})
function bindBtn1Event() {
$("#btn1").click(function () {
$.ajax({
url: '/task/ajax/',
type: 'get',
data: {
n1: 123,
n2: 456,
},
dataType: "JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
})
}
function bindBtn2Event() {
$("#btn2").click(function () {
$.ajax({
url: '/task/ajax/',
type: 'get',
data: {
name: $("#txtUser").val(),
age: $("#txtAge").val(),
},
dataType: "JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
})
}
function bindBtn3Event() {
$("#btn3").click(function () {
$.ajax({
url: '/task/ajax/',
type: 'get',
data: $("#form3").serialize(),
dataType: "JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
},
})
})
}
</script>
{% endblock %}
11.5 ajax实现添加数据
@csrf_exempt
def task_add(request):
print(request.POST)
#1. 用户发来的数据校验
form = TaskModelForm(data = request.POST)
# 成功
if form.is_valid():
form.save()
data_dict = {"status": True} # 传入后台status为True
return HttpResponse(json.dumps(data_dict))
# 失败
else:
data_dict = {"status": False,'error':form.errors} # 传入后台status为False
return HttpResponse(json.dumps(data_dict,ensure_ascii=False))
-
js代码:
-
先滞空所有报错信息
-
如果status为True 则显示添加成功
-
如果为False,通过一下代码
$.each(res.error,function (name,data){ // console.log(name,data); $("#id_" + name).next().text(data[0]); })
遍历所有错误信息,并且展现出来
-
<script type="text/javascript">
$(function () {
// 页面加载完成后代码自动执行
bindBtn1Event();
bindBtn2Event();
bindBtn3Event();
bindBtnAddEvent();
})
function bindBtnAddEvent() {
$("#btnAdd").click(function () {
$(".error-msg").text("") //先滞空
$.ajax({
url: '/task/add/',
type: 'post',
data: $("#addForm").serialize(),
dataType: "JSON",
success: function (res) {
if (res.status){
alert("添加成功!")
}else{
console.log(res.error)
$.each(res.error,function (name,data){
// console.log(name,data);
$("#id_" + name).next().text(data[0]);
})
}
},
})
})
}
</script>
- 使用JS代码
location.reload()
刷新页面
11.6 模态框保存
设置有些数据不需要用户填,但是需要设置默认数据:
# 生成订单号
form.instance.oid = datetime.now().strftime("%Y%m%d%H%M%S") + str(random.randint(1000,9999))
# 设置管理员id 当前登入系统的管理员
form.instance.admin_id = request.session["info"]["id"]
js 详细代码:
<script type="text/javascript">
$(function () {
bindBtnSaveEvent()
})
function bindBtnSaveEvent() {
$("#btnSave").click(function () {
// 清楚错误信息
$(".error-msg").text("")
// 向后台发送请求
$.ajax({
url: "/order/add/",
type: "post",
data: $("#addForm").serialize(),
dataType: "JSON",
success: function (res) {
if (res.status) {
alert("创建成功!");
// 清空表单
$("#addForm")[0].reset();
// 关闭对话框
$("#MyModal").modal('hide');
// 刷新页面
location.reload()
} else {
// 把错误信息放在对话框中
$.each(res.error, function (name, errorList) {
$("#id_" + name).next().text(errorList[0])
})
}
}
})
});
}
</script>
11.7 删除数据(ajax)
点击删除后,弹出提示框,确认后再删除
后端代码:
path('order/delete/', order.order_delete),
def order_delete(request):
""" 删除订单 """
uid = request.GET.get('uid')
exists = models.Order.objects.filter(id=uid).exists()
if not exists:
return JsonResponse({"status":False,"error":"删除失败,数据不存在"})
else:
models.Order.objects.filter(id=uid).delete()
return JsonResponse({"status": True})
前端js代码:
<script type="text/javascript">
var DELETE_ID;
$(function () {
bindBtnSaveEvent()
bindBtnDeleteEvent()
bindBtnConfirmDeleteEvent()
})
function bindBtnDeleteEvent() {
$(".btn-delete").click(function () {
// 展示模态框
$("#deleteModal").modal("show");
// 获取当前行的ID并且赋值给全局变量
DELETE_ID = $(this).attr("uid");
});
}
function bindBtnConfirmDeleteEvent(){
$("#btnConfirmDelete").click(function (){
// 点击删除按钮,将全局变量中的ID发送到后台
$.ajax({
url:"/order/delete/",
type:"GET",
data:{
uid : DELETE_ID
},
dataType:"JSON",
success:function (res){
if(res.status){
// 删除成功
// alert("删除成功");
// 隐藏删除框
$("#deleteModal").modal("hide");
// 要删除的id至空
DELETE_ID = 0;
// 刷新页面
location.reload()
}else{
// 删除失败
alert(res.error);
}
}
})
});
}
</script>
11.8 编辑数据
数据库中获取对象时
# 对象,当前行的所有数据
row_object = models.Order.objects.filter(id=uid).first()
row_object.id
# 字典,{"id":1,"title":}
row_object = models.Order.objects.filter(id=uid).values('id','title').first()
# queryset = [obj,obj,obj]
queryset = models.Order.objects.all()
# queryset = [{"id":1,"title":"xx"},{"id":2,"title":"xx"}]
queryset = models.Order.objects.all().values('id','title')
# queryset = [(1,"xx"),(2,"xx")]
queryset = models.Order.objects.all()values_list('id','title')
后端代码:
path('order/detail/', order.order_detail),
def order_detail(request):
""" 根据ID获取订单数据 """
# 方式一
"""
uid = request.GET.get("uid")
row_object = models.Order.objects.filter(id=uid).first()
if not row_object:
return JsonResponse({"status":False,"error":"删除失败,数据不存在"})
# 从数据库获取一个对象 row_object
result = {
"status": True,
"data":{
"title": row_object.title,
"price": row_object.price,
"status": row_object.status,
}
}
return JsonResponse({"status":True,"data":result})
"""
uid = request.GET.get("uid")
# 字典
row_dict = models.Order.objects.filter(id=uid).values("title","price","status").first()
if not row_dict:
return JsonResponse({"status":False,"error":"删除失败,数据不存在"})
# 从数据库获取一个对象 row_object
result = {
"status": True,
"data":row_dict
}
# JSON序列化只能基本数据类型 其他对象类型序列化不了
return JsonResponse(result)
前端js代码:
<script type="text/javascript">
$(function () {
bindBtnSaveEvent()
bindBtnDeleteEvent()
bindBtnConfirmDeleteEvent()
bindBtnEditEvent()
})
function bindBtnEditEvent() {
$(".btn-edit").click(function () {
// 清空对话框的数据
$("#addForm")[0].reset();
var uid = $(this).attr("uid");
// alert("点击了编辑");
// 发送ajax去获取当前行的数据
$.ajax({
url: "/order/detail/",
type: "get",
data: {
uid: uid
},
dataType: "JSON",
success: function (res) {
if (res.status) {
console.log(res.data)
// 将数据赋值到对话框标签中
$.each(res.data,function (name,value){
$("#id_" + name).val(value)
})
// 修改对话框标题
$("#ModalLabel").text("编辑");
// 点击编辑模态对话框出来
$("#MyModal").modal("show");
} else {
alert(res.error);
}
}
})
// 在对话框中默认让用户看到
})
}
</script>
11.9 总结
- 后端代码:
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from myapp import models
from django import forms
from django.views.decorators.csrf import csrf_exempt
from datetime import datetime
import random
import json
from myapp.utils.pagination import Pagination
class OrderModelForm(forms.ModelForm):
class Meta:
model = models.Order
exclude = ["oid","admin"]
# 循环找到所有的插件,添加 "class": "form-control"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
# 可以排除指定的字段
# if name == "create_time":
# continue
field.widget.attrs = {"class": "form-control"}
def order_list(request):
queryset = models.Order.objects.all().order_by("-id")
page_object = Pagination(request, queryset)
form = OrderModelForm()
context ={
"form":form,
"queryset": page_object.page_queryset, #分完页的数据
"page_string": page_object.html(), #生成的页码
}
return render(request,"order_list.html",context)
@csrf_exempt
def order_add(request):
""" 新建订单(Ajax请求) """
form = OrderModelForm(data=request.POST)
if form.is_valid():
# 生成订单号
form.instance.oid = datetime.now().strftime("%Y%m%d%H%M%S") + str(random.randint(1000,9999))
# 设置管理员id 当前登入系统的管理员
form.instance.admin_id = request.session["info"]["id"]
# 保存到数据库中
form.save()
# return HttpResponse(json.dumps({"status":True}))
return JsonResponse({"status":True})
else:
return JsonResponse({"status":False,"error":form.errors})
def order_delete(request):
""" 删除订单 """
uid = request.GET.get('uid')
exists = models.Order.objects.filter(id=uid).exists()
if not exists:
return JsonResponse({"status":False,"error":"删除失败,数据不存在"})
else:
models.Order.objects.filter(id=uid).delete()
return JsonResponse({"status": True})
def order_detail(request):
""" 根据ID获取订单数据 """
# 方式一
"""
uid = request.GET.get("uid")
row_object = models.Order.objects.filter(id=uid).first()
if not row_object:
return JsonResponse({"status":False,"error":"删除失败,数据不存在"})
# 从数据库获取一个对象 row_object
result = {
"status": True,
"data":{
"title": row_object.title,
"price": row_object.price,
"status": row_object.status,
}
}
return JsonResponse({"status":True,"data":result})
"""
uid = request.GET.get("uid")
# 字典
row_dict = models.Order.objects.filter(id=uid).values("title","price","status").first()
if not row_dict:
return JsonResponse({"status":False,"error":"删除失败,数据不存在"})
# 从数据库获取一个对象 row_object
result = {
"status": True,
"data":row_dict
}
# JSON序列化只能基本数据类型 其他对象类型序列化不了
return JsonResponse(result)
@csrf_exempt
def order_edit(request):
""" 编辑订单 """
uid = request.GET.get("uid")
row_object = models.Order.objects.filter(id = uid).first()
if not row_object:
return JsonResponse({"status":False,"tips":"数据不存在,请刷新重试"})
else:
form = OrderModelForm(data=request.POST,instance=row_object)
if form.is_valid():
form.save()
return JsonResponse({"status": True})
return JsonResponse({"status": False, "error": form.errors})
- 前端js代码:
{% block js %}
<script type="text/javascript">
var DELETE_ID;
var EDIT_ID;
$(function () {
bindBtnAddEvent()
bindBtnSaveEvent()
bindBtnDeleteEvent()
bindBtnConfirmDeleteEvent()
bindBtnEditEvent()
})
function bindBtnAddEvent() {
$("#btnAdd").click(function () {
// 将正在编辑的ID设置为空
EDIT_ID = undefined;
// 清空对话框的数据
$("#addForm")[0].reset();
// 修改对话框标题
$("#ModalLabel").text("新建");
// 点击新建按钮,显示对话框
$("#MyModal").modal('show');
});
}
function bindBtnSaveEvent() {
$("#btnSave").click(function () {
// 清楚错误信息
$(".error-msg").text("")
if (EDIT_ID) {
// 编辑
doEdit();
} else {
// 添加
doAdd();
}
});
}
function doAdd() {
// 向后台发送请求(添加ajax请求)
$.ajax({
url: "/order/add/",
type: "post",
data: $("#addForm").serialize(),
dataType: "JSON",
success: function (res) {
if (res.status) {
alert("创建成功!");
// 清空表单
$("#addForm")[0].reset();
// 关闭对话框
$("#MyModal").modal('hide');
// 刷新页面
location.reload()
} else {
// 把错误信息放在对话框中
$.each(res.error, function (name, errorList) {
$("#id_" + name).next().text(errorList[0])
})
}
}
})
}
function doEdit() {
// 向后台发送请求(添加ajax请求)
$.ajax({
url: "/order/edit/" + "?uid=" + EDIT_ID,
type: "post",
data: $("#addForm").serialize(),
dataType: "JSON",
success: function (res) {
if (res.status) {
// 清空表单
$("#addForm")[0].reset();
// 关闭对话框
$("#MyModal").modal('hide');
// 刷新页面
location.reload()
} else {
if (res.tips) {
alert(res.tips);
} else {
// 把错误信息放在对话框中
$.each(res.error, function (name, errorList) {
$("#id_" + name).next().text(errorList[0])
})
}
}
}
})
}
// id = btnConfirmDelete
function bindBtnDeleteEvent() {
$(".btn-delete").click(function () {
// 展示模态框
$("#deleteModal").modal("show");
// 获取当前行的ID并且赋值给全局变量
DELETE_ID = $(this).attr("uid");
});
}
function bindBtnConfirmDeleteEvent() {
$("#btnConfirmDelete").click(function () {
// 点击删除按钮,将全局变量中的ID发送到后台
$.ajax({
url: "/order/delete/",
type: "GET",
data: {
uid: DELETE_ID
},
dataType: "JSON",
success: function (res) {
if (res.status) {
// 删除成功
// alert("删除成功");
// 隐藏删除框
$("#deleteModal").modal("hide");
// 要删除的id至空
DELETE_ID = 0;
// 刷新页面
location.reload()
} else {
// 删除失败
alert(res.error);
}
}
})
});
}
function bindBtnEditEvent() {
$(".btn-edit").click(function () {
// 清空对话框的数据
$("#addForm")[0].reset();
var uid = $(this).attr("uid");
EDIT_ID = uid;
// alert("点击了编辑");
// 发送ajax去获取当前行的数据
$.ajax({
url: "/order/detail/",
type: "get",
data: {
uid: uid
},
dataType: "JSON",
success: function (res) {
if (res.status) {
console.log(res.data)
// 将数据赋值到对话框标签中
$.each(res.data, function (name, value) {
$("#id_" + name).val(value)
})
// 修改对话框标题
$("#ModalLabel").text("编辑");
// 点击编辑模态对话框出来
$("#MyModal").modal("show");
} else {
alert(res.error);
}
}
})
// 在对话框中默认让用户看到
})
}
</script>
{% endblock %}
12. 图表
- highchart 国外
- echarts 国内
动态数据库获取数据形成柱状图
-
后端代码
def chart_bar(request): """ 构造柱状图数据 """ # 数据可以从数据库获取 legend = ['销量', '业绩'] series_list = [ { "name": '销量', "type": 'bar', "data": [5, 20, 36, 10, 10, 20] }, { "name": '业绩', "type": 'bar', "data": [70, 200, 36, 66, 15, 10] } ] x_axis = ['1月', '2月', '3月', '4月', '5月', '6月'] result = { "status":True, "data":{ "legend":legend, "series_list":series_list, "x_axis": x_axis, } } return JsonResponse(result)
-
前端代码
<body> <div id="m1" style="width: 100%;height:400px;"> </div> </body> <script type="text/javascript"> $(function () { initBar(); }) /** * 初始化柱状图 */ function initBar() { // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('m1')); // 指定图表的配置项和数据 var option = { title: { text: '柱状图', textAlign: "auto", left: "center" }, tooltip: {}, legend: { data: [], // 后台获取 bottom: 0, }, xAxis: { data: [], // 后台获取 }, yAxis: {}, series: [] // 后台获取 }; // 通过ajax向后台获取数据 $.ajax({ url: "/chart/bar/", type: "get", dataType: "JSON", success: function (res) { // 将后台返回的数据,更新到option中 if(res.status){ option.legend.data = res.data.legend; option.xAxis.data = res.data.x_axis; option.series = res.data.series_list; } // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); } }) } </script>
13. 关于文件上传
13.1 基本操作
- 前端:
{% extends "layout.html" %}
{% block content %}
<div class="container">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="text" name="name">
<input type="file" name="avatar">
<input type="submit" value="提交">
</form>
</div>
{% endblock %}
- 后端
def upload_list(request):
if request.method == "GET":
return render(request,"upload_list.html")
elif request.method == "POST":
# print(request.FILES)
# 声明图片对象
file_object = request.FILES.get("avatar")
# 分块存储图片
f = open(file_object.name,'wb')
for chunk in file_object.chunks():
f.write(chunk)
f.close()
return HttpResponse("上传图片成功")
案例: 批量上传数据
- 前端
<form method="post" enctype="multipart/form-data" action="/depart/multi" style="margin-top: 5px;">
{% csrf_token %}
<input type="file" name="exc">
<a class="btn btn-success btn-sm" href="/depart/add" style="margin-top: 10px;margin-bottom: 10px">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-file-earmark-plus" viewBox="0 0 16 16">
<path d="M8 6.5a.5.5 0 0 1 .5.5v1.5H10a.5.5 0 0 1 0 1H8.5V11a.5.5 0 0 1-1 0V9.5H6a.5.5 0 0 1 0-1h1.5V7a.5.5 0 0 1 .5-.5z"/>
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/>
</svg>
批量新建部门
</a>
</form>
- 后端
def depart_multi(request):
""" 批量添加部门(Excel) """
from openpyxl import load_workbook
# 获取上传的excel文件
file_object = request.FILES.get('exc')
#打开Excel文件读取内容
wb = load_workbook(file_object)
sheet = wb.worksheets[0]
#循环获取每一行代码
for row in sheet.iter_rows(min_row=2):
exc_title = row[0].value
# 如果表格数据不存在数据库中,则可以创建
if not models.Department.objects.filter(title=exc_title).exists():
models.Department.objects.create(title=exc_title)
# 重定向网页
return redirect("/depart/list/")
案例:混合数据(Form)
提交页面时:用户输入数据 + 文件 (输入不能为空)
- 后端:
class UpForm(forms.Form):
name = forms.CharField(label="姓名")
age = forms.IntegerField(label="年龄")
img = forms.FileField(label="头像")
# 循环找到所有的插件,添加 "class": "form-control"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
exclude_fields = ["img"]
for name, field in self.fields.items():
# 可以排除指定的字段
# if name == "create_time":
# continue
if name in exclude_fields:
continue
field.widget.attrs = {"class": "form-control"}
def upload_form(request):
""" 上传混合数据 """
if request.method == "GET":
form = UpForm()
return render(request, 'upload_form.html', {"form": form})
elif request.method == "POST":
form = UpForm(data=request.POST, files=request.FILES)
# {'name': '张三', 'age': 21, 'img': <InMemoryUploadedFile: code.png (image/png)>}
# 1.读取图片内容,写入到文件夹中
if form.is_valid():
image_object = form.cleaned_data.get("img")
# 路径
db_file_path = os.path.join("static", "img", image_object.name)
file_path = os.path.join("myapp", db_file_path)
print(file_path)
f = open(file_path, mode='wb')
for chunk in image_object.chunks():
f.write(chunk)
f.close()
# 2.将图片文件路径写到数据库
models.Boss.objects.create(
name=form.cleaned_data['name'],
age = form.cleaned_data['age'],
img = db_file_path,
)
return HttpResponse("成功")
return render(request, 'upload_form.html', {"form": form})
启用media
django开发过程两个特殊文件:
- static,存放静态文件的路径,包括:CSS JS 项目图片。
- media,用户上传的数据的目录。
url.py 中配置:
from django.urls import path, re_path
from django.conf import settings
from django.views.static import serve
urlpatterns = [
re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}, name='media'),
]
setting.py 中配置
import os
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = '/media/'
后端获取路径:
# media_path = os.path.join(settings.MEDIA_ROOT,image_object.name) 绝对路径
media_path = os.path.join("media", image_object.name)
案例:混合数据(ModelForm)
- models.py
class Player(models.Model):
""" 游戏人物 """
name = models.CharField(verbose_name="游戏ID", max_length=32)
level_choices = (
(1, "D"),
(2, "D+"),
(3, "C"),
(4, "C+"),
(5, "B"),
(6, "B+"),
(7, "A"),
(8, "A+"),
(9, "S"),
)
level = models.SmallIntegerField(verbose_name="段位",choices=level_choices,default=1)
# 本质上数据库也是CharField,自动保存数据
img = models.FileField(verbose_name="头像", max_length=128, upload_to='player/')
- 后端代码
class PlayerModelForm(forms.ModelForm):
class Meta:
model = models.Player
fields = "__all__"
# 循环找到所有的插件,添加 "class": "form-control"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
exclude_fields = ["img"]
for name, field in self.fields.items():
# 可以排除指定的字段
# if name == "create_time":
# continue
if name in exclude_fields:
continue
field.widget.attrs = {"class": "form-control"}
def player_add(request):
""" 新建玩家"""
if request.method == "GET":
form = PlayerModelForm()
return render(request,"player_add.html",{"form":form})
elif request.method == "POST":
form = PlayerModelForm(data=request.POST,files = request.FILES)
if form.is_valid():
# 保存到数据库
form.save()
return redirect("/player/list/")
return render(request,"player_add.html",{"form":form})
小节
-
自己手动写
file_object = request.FILES.get("exc")
-
Form组件
request.POST file_object = request.FILES.get("exc") 具体文件操作还是要自己写
-
MediaForm组件
- media 文件夹 - Models.py 中定义类文件要: img = models.FileField(verbose_name = "LOGO", max_length = 128,upload_to = "city/")
c,存放静态文件的路径,包括:CSS JS 项目图片。
- media,用户上传的数据的目录。
url.py 中配置:
from django.urls import path, re_path
from django.conf import settings
from django.views.static import serve
urlpatterns = [
re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}, name='media'),
]
setting.py 中配置
import os
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = '/media/'
后端获取路径:
# media_path = os.path.join(settings.MEDIA_ROOT,image_object.name) 绝对路径
media_path = os.path.join("media", image_object.name)
案例:混合数据(ModelForm)
- models.py
class Player(models.Model):
""" 游戏人物 """
name = models.CharField(verbose_name="游戏ID", max_length=32)
level_choices = (
(1, "D"),
(2, "D+"),
(3, "C"),
(4, "C+"),
(5, "B"),
(6, "B+"),
(7, "A"),
(8, "A+"),
(9, "S"),
)
level = models.SmallIntegerField(verbose_name="段位",choices=level_choices,default=1)
# 本质上数据库也是CharField,自动保存数据
img = models.FileField(verbose_name="头像", max_length=128, upload_to='player/')
- 后端代码
class PlayerModelForm(forms.ModelForm):
class Meta:
model = models.Player
fields = "__all__"
# 循环找到所有的插件,添加 "class": "form-control"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
exclude_fields = ["img"]
for name, field in self.fields.items():
# 可以排除指定的字段
# if name == "create_time":
# continue
if name in exclude_fields:
continue
field.widget.attrs = {"class": "form-control"}
def player_add(request):
""" 新建玩家"""
if request.method == "GET":
form = PlayerModelForm()
return render(request,"player_add.html",{"form":form})
elif request.method == "POST":
form = PlayerModelForm(data=request.POST,files = request.FILES)
if form.is_valid():
# 保存到数据库
form.save()
return redirect("/player/list/")
return render(request,"player_add.html",{"form":form})
小节
-
自己手动写
file_object = request.FILES.get("exc")
-
Form组件
request.POST file_object = request.FILES.get("exc") 具体文件操作还是要自己写
-
MediaForm组件
- media 文件夹 - Models.py 中定义类文件要: img = models.FileField(verbose_name = "LOGO", max_length = 128,upload_to = "city/")