Django学习

Django学习

1 创建项目

终端:django-admin startproject 项目名
Pycharm:新建项目选择Django

2 默认文件介绍

HelloWorld
|-- HelloWorld
|   |-- __init__.py
|   |-- asgi.py	[接收网络请求](不用动)
|   |-- settings.py	[项目配置](经常使用)
|   |-- urls.py		[URL和函数的对应关系](经常使用)
|   `-- wsgi.py	[接收网络请求](不用动)
`-- manage.py	[项目的管理,启动项目、创建app、数据管理](经常使用)

3,APP

创建APP:python mange.py startapp app名

HelloWorld
|-- HelloWorld
|   |-- __init__.py
|   |-- settings.py
|   |-- urls.py
|   `-- wsgi.py
|--app01
|	|--__init__.py
|	|--admin.py		[固定,不用动](django默认提供admin后台管理)
|	|--apps.py		[固定,不用动](app启动类)
|	|--migrations	[固定,不用动](数据库变更记录)
|	|	`--__init__.py
|	|--models.py	[重要](对数据库的操作)
|	|--tests.py		[固定,不用动](单元测试)
|	`--views.py		[重要](函数)
`-- manage.py

4 快速入手

4.1 注册app[settings.py ]

	settings.py		INSTALLED_APPS		添加:app名.apps.AppConfig(app目录下apps文件里的AppConfig类)

4.2 编写URL和视图函数对应关系[urls.py]

	#	导入app里的views文件
	from app01 import views
	#	www.xxx.com/index -> 函数	path里index是视图函数名
	urlpatterns = [
		path('index/',views.index)
	]

4.3 编写视图函数[views.py]

	def 函数名 (request):
		pass

4.4 启动

	终端:python manage.py runserver
	Pycharm直接启动

5 templates目录(模板)

from django.shortcuts import render,HttpResponse
render:返回html
HttpResponse:返回字符

5.1 在app目录下创建templates文件夹

	创建HTML文件

6 静态文件

图片
js
css
插件

6.1 创建static文件夹

	一般文件夹类包含
	img文件夹
	js文件夹
	css文件夹
	plugins文件夹

6.2 引用静态文件

	{% load static %}
	导入静态文件通常在HTML文件前加入{% load static %},路径为{% static 'img/1.png' %}

7 模板语法

7.1 传入后端数据(data)

	data为字典
	例:data = {'n1':'book'}		data = {'n1':data}
	return render(request,'1.html',data)

7.2 前端接收

	{{ 字典键 }}
	例:{{ n1 }}
	传入的是字典n1后面加上点和要取的值的索引(字典键)
	{{ n1.n1 }}
		取所有键
		{% for child in n1.keys %}
			<span>{{ child }}</span>
		{% endfor %}
		取所有值
		{% for child in n1.values %}
			<span>{{ child }}</span>
		{% endfor %}
		取所有键值
		{% for k,v in n1.items %}
			<span>{{ k }} = {{ v }}</span>
		{% endfor %}
		
	传入的是列表n1后面加上点和要取的值的编号(列表值的位置0,1,2)
	{{ n1.0 }}
		可以用for循环把列表里的每个元素的值都取到
		<div>
			{% for child in n1 %}
				<span>{{ child }}</span>
			{% endfor %}
		</div>
		要有开头{% for child in n1 %}和结尾{% endfor %}接收值要用{{ }}
	
	支持判断语句
		{% if n1 == 'xxx' %}
			<h1>xxx</h1>
		{% elif n1 == 'yyy' %}
			<h1>yyy</h1>
		{% else %}
			<h1>kkk</h1>

8 请求和响应

8.1 请求类型

request.method

8.2 接收数据(接收的数据为QueryDict类型)

GET请求
	request.GET
	获取数据(单个项数据用get不用getlist)
	request.GET.getlist('user')
	#接收到的数据生成字符串
	request.GET.urlencode()
POST请求
	request.POST
	获取数据(单个项数据用get不用getlist)
	request.POST.getlist('user')

8.3 返回数据(需要导包)

from django.shortcuts import render,HttpResponse,redirect
	render:返回html
	HttpResponse:返回字符
	redirect:重定向

9 数据库操作

9.1 安装第三方模块

pip install mysqlclient

9.2 ORM

可以创建、修改、删除数据库中的表
可以操作表里的数据
无法操作数据库
9.2.1 创建删除数据库
9.2.1.1 创建数据库
	create database 库名称;
	create database if not exists 库名称;#如果不存在该库,则创建
9.2.1.2 删除数据库
	drop database 库名称;
9.2.1.3 选择使用数据库
	use 库名称;
9.2.1.4 显示当前使用的数据库
	select database();
9.2.2 Django连接数据库
	修改settings.py文件里的DATABASES
		DATABASES = {
			'default': {
				'ENGINE': 'django.db.backends.mysql',	#数据库类型
				'NAME':'Learm',		#数据库名
				'USER':'root',		#账号
				'PASSWORD':'xxxxx',	#密码
				'HOST':'127.0.0.1',	#数据库地址
				'POST':'3306'		#数据库端口号
				}
			}
9.2.3 Django操作表
9.2.3.1 创建表
	#在models.py文件里创建类
		#表名为APP名_类名,例:app01_User
		class User(models.Model):
			#列名			   数据类型    数据长度
			name = models.CharField(max_length=32)
			password = models.CharField(max_length=64)
			age = models.IntegerField()
			#time = models.DateTimeField()	包含年月日时分秒
            time = models.DateField()	#只包含年月日

执行命令

		python manage.py makemigrations
		python manage.py migrate
9.2.3.2 删除表或列
	注释了类重新执行命令
	
	新建一列
	
		设置默认值
		name=models.CharField(default=’xxx’)
		或者设置可以为空
		name=models.CharField(null=True,blank=True)
	调整数据表结构修改类即可
		新建数据
		类名.objects.create(name=’张三’)
9.2.3.3 新建数据
	类名.objects.create(name='我',pasword='123',age='12')
	如果有默认值则可以不写他的值
9.2.3.4 删除数据
	筛选删除
					 条件
		类名.objects.filter(id=1).delete()
	全部删除
					 全部
		类名.objects.all().delete()
9.2.3.5 获取数据(QuerySet类型)
9.2.3.5.1 QuerySet类型 -> 数据列表
	全部获取
		类名.objects.all()
		可以用for循环获取数据
		for obj in 类名.objects.all():
			print(obj.id,obj.name,obj.password,obj.age)
		
	筛选获取
		data_list = 类名.objects.filter(id=1) 
		#多条件筛选方法一
        data_list = 类名.objects.filter(phone="12695478956",id=1)
        #多条件筛选方法二
        #data_dict = {'phone':"12695478956",'id'=1}
        #data_list = 类名.objects.filter(**data_dict)
        #给字典赋值
        #data_dict["phone"]=12695478956
        对于数字(int)
		data_list = 类名.objects.filter(id=12) #等于12
        data_list = 类名.objects.filter(id__gt=12) #大于12
        data_list = 类名.objects.filter(id__gte=12) #大于等于12
        data_list = 类名.objects.filter(id__lt=12) #小于12
        data_list = 类名.objects.filter(id__lte=12) #小于等于12
        对于字符串(str)
        models.PrettyNum.objects.filter(mobile__startswith="1999")  #以1999开头
		models.PrettyNum.objects.filter(mobile__endswith="999")	#以999结尾
        models.PrettyNum.objects.filter(mobile__contains="99")	#包含99的
	 #如果只有一个数据
    #None 表示的含义,更多的是一种不存在,是真正的空,而不是空列表([])的空,也不是空的字符串
		data_list = 类名.objects.filter(id=1).first()
		print(data_list.id,data_list.name,data_list.password,data_list.age)
     #判断数据是否存在在数据库(返回	数据/None)
		data_list = 类名.objects.filter(id=1).exists()
     #判断数据是否存在排除自己(返回	数据/None)
    #id!=2 and phone="12695478956"
		data_list = 类名.objects.filter(phone="12695478956").exists(id=2)
9.2.3.5.2获取到的有多少条数据
类名.objects.all().count()
类名.objects.filter(id=1).count()
9.2.3.6更新数据
	筛选更新
		类名.objects.filter(id=1).updata(age=999)
		
	全部更新
		类名.objects.all().updata(password=666)
9.2.4 部门管理实例

​ 多表链接
​ 数据库存入ID(常见节省存储开销)(耗时)(数据库范式)
​ 数据库存入名称(加速查找,允许数据冗余)
​ 约束
​ 无约束

id = models.IntegerField(verbose_name='部门id')

​ 有约束
​ 用到ForeignKey列名自动加下划线列名 例:depart_id

9.3 显示为对象时


depart = models.ForeignKey(to='depatments',to_field='id')
#ModelForm打印有约束的字段时在models.py定义的时候在那张表的类里面要加上
def __str__(self):
    #title是表赋值的时候定义的
    return self.title

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l7BlCmGe-1657435349708)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220507150249197.png)]

​ 部门被删除员工处理
​ 一起删除(级联删除)

depart = models.ForeignKey(to='depatments',to_field='id',on_delete=models.CASCADE)

​ 置空

depart = models.ForeignKey(to='depatments',to_field='id',null=True,blank=True,on_delete=models.SET_NULL)
	
Django里的约束
	性别(1代表男,2代表女)
		gender_choices = ((1,'男'),(2,'女'))
		gender = models.SmallIntegerField(verbose_name='性别',choices=gender_choices)

9.4 数据库元组套元组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BEIharJx-1657435349709)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220506232826504.png)]

在django里用get_字段名称_display()可以自动把1输出男2输出女
obj.get_gender_display()

9.5 数据库时间

9.5.1 在python里datatima转字符串
obj.time.strftime("%Y-%m-%d-%H-%M")		#%Y-%m-%d年月日
9.5.2 django模板语法里
obj.time|date:"Y-m-d H:i:s"

10 urls.py

​ url支持正则表达式

path('zhuce/<int:nid>/list', zhuce)

#views.py文件里定义函数时接收即可
def zhuce_list(request,nid)pass

11 模板的继承

11.1 定义母版

a.html

定义所需的js与css

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>title</title>
    <!--在母版里{% block css %}{% endblock %}类似于占位符,用于一些子版里才用得到的css导入-->
    {% block css %}{% endblock %}
</head>
<body>
    <!--在母版里{% block content %}{% endblock %}类似于占位符-->
    {% block content %}{% endblock %}
    <!--在母版里{% block js %}{% endblock %}类似于占位符,用于一些子版里才用得到的js导入-->
    {% block js %}{% endblock %}
</body>
</html>

11.2 继承母版

<!--在子版里{% extends "a.html"%}用来继承母版-->
{% extends "a.html"%}

<!--在子版里{% block css %}{% endblock %}中间用来书写应该在母版里{% block css %}{% endblock %}的位置该写的css-->
{% block css %}
	<style></style>
{% endblock %}

<!--在子版里{% block content %}{% endblock %}中间用来书写应该在母版里{% block content %}{% endblock %}的位置该写的HTML-->
{% block content %}
    <div>
    title
    </div>
{% endblock %}
<!--在子版里{% block js %}{% endblock %}中间用来书写应该在母版里{% block js %}{% endblock %}的位置该写的JavaScrip-->
{% block js %}
	<script type="text/javascript"></script>
{% endblock %}

可以用多个{% block content %}{% endblock %}

12 Django组件

  • Form(字段全部需要自己写)
  • ModelForm(字段可以自动生成)

12.1 Form(验证时方便)

12.1.1 views.py
from django import forms
class MyFrom(forms.Form):
    #在html里会帮我们显示input输入框
    user = forms.CharField(widget=forms.Input)
    pwd = forms.CharField(widget=forms.Input)
    email = forms.CharField(widget=forms.Input)
    
def user(request):
    if request.method == 'GET':
        #实例化类
        form = MyFrom()
        #把form传入html
        return render(request,'a.html',{"form":form})
    
12.1.2 a.html
<from method="post">
    <!--就不用手写input标签,直接导入 -->
	{{ from.user }}
    {{ from.pwd }}
    {{ from.email }}
</from>
<from method="post">
    <!--就不用手写input标签,可以循环类里的自动创建input标签 -->
	{% for field in form %}
    	{{field}}
    {% endfor %}
    
</from>

12.2 ModelForm(增删改,最简洁)

12.2.1 models.py
from django.db import models

# Create your models here.

class userinfo(models.Model):
    # 员工表
    name = models.CharField(verbose_name='姓名',max_length=16)
    password = models.CharField(verbose_name='密码',max_length=64)
12.2.2 views.py
from django import forms
class MyFrom(Form.ModelForm):
    #required=True输入不为空
    xx = models.CharField(verbose_name='父母',max_length=16,required=True)
    #不仅支持去models里获取字段也能自定义字段
    class Meta:
        model = UserIfo
        #fields有三种写法
        #1、fields = "__all__"   取全部字段
        #1、exclude = ['name']	排除name字段
        fields = ['name','password','xx']
    	#定义标签的属性(逐一添加属性)
        widgets = {
            'name':forms.TextInput(attrs={"class":"form-control"})
            'password':forms.PasswordInput(attrs={"class":"form-control"})
        }
    def __init__(self, *args, **kwargs):
        super().__init__(*args,**kwargs)
        # 循环找到所有插件,添加class属性值
        # name是字段名
        for name, field in kwargs.items():
            #字段为password的不加class属性
            if name == "password":
                continue
             #                       添加class属性 			 显示数据库verbose_name
            field.widget.attrs = {"class":"from-contorl","placeholder":field.label}
        
        
def user(request):
    if request.method == 'GET':
        #实例化类
        form = MyFrom()
        #把form传入html
        return render(request,'a.html',{"form":form})
<from method="post">
    <!--就不用手写input标签,直接导入 -->
    <!--{{ from.user.label }} 显示数据库verbose_name -->
	{{ from.user.label }}:{{ from.user }}
    {{ from.pwd }}
    {{ from.email }}
</from>
<from method="post">
    <!--就不用手写input标签,直接导入 -->
    <!--{{ from.user.label }} 显示数据库verbose_name -->
    {% for field in form %}
		{{ field.label }}:{{ field }}
	{% endfor %}
</from>

12.4 数据校验与自动提交

数据提交
校验是否符合数据表定义的要求
import BootStrapMyFrom
class MyFrom(BootStrapMyFrom):
    class Meta:
        model = UserIfo
        fields = ['name','password']
#文件里form被定义了两次,第一次没数据只是实例化一个类,第二次包含了数据,用于验证和写入,所以尽量不改变他
def zhuce(request):
    form = MyForm()
    return render(request,'c.html', {'forms':form})
	form = MyFrom(data=request.POST)
    #判断数据是否合法
    if form.is_valid():
        #数据合法自动提交到数据库表名为MyFrom里定义的model的表(数据写入)
        form.save()
        return redirect('/zhuce')
    else:
        return render(request,'c.html', {'forms':form})
        #数据不合法校验失败
<from method="post">
    {% for field in form %}
		{{ field.label }}:{{ field }}
    	{{ field.errors.0 }}<!--显示第一个错误-->
	{% endfor %}
</from>
错误信息改中文

在settings.py文件里改LANDUAGE_CODE = ‘zh-hans’

添加验证项(重写更多校验)
from django import forms
class MyFrom(Form.ModelForm):
    #添加校验name字段最小长度为3
    name = forms.CharFiled(min_length=3,label='用户名')
    class Meta:
        model = UserIfo
        fields = ['name','password']
        widgets = {
            'name':forms.TextInput(attrs={"class":"form-control"})
            'password':forms.PasswordInput(attrs={"class":"form-control"})
        }
修改数据
#nid是url携带过来的参考上面	10	urls.py
def zhuce(request,nid):
    if request.method == 'GET':
        #从数据库获取id为要修改那行的id的一行数据
        ord_object = models.UserInfo.objects.filter(id=nid).first()
        #给修改页面添加默认值 
        form = MyForm(instance = ord_object)
        return render(request,'c.html', {'forms':form})
    else:
        ord_object = models.UserInfo.objects.filter(id=nid).first()
        #告诉jango不是新增而是更新到id=nid那一行(编辑数据)
        form = MyFrom(data=request.POST,instance = ord_object)
        if form.is_valid():
        #默认保存用户输入的值,如果想要在用户输入的值以外增加一些值
        #form.instance.字段名 = 值
        	form.save()
        	return redirect('/zhuce')
    	else:
        	return render(request,'c.html', {'forms':form})
格式校验
方式一(正则方法)
from django import forms

from django.core.validators import RegexValidator
class MyFrom(Form.ModelForm):
    #validators后面可以写正则表达式		^开始 $结束 [3-9]3到9里的一个数字 \d{9}9位数字
    phone = forms.CharFiled(label='手机号',validators=[RegexValidator(r'^1[3-9]\d{9}$','必须以159开头')])
    class Meta:
        model = UserIfo
        fields = ['name','password']
        widgets = {
            'name':forms.TextInput(attrs={"class":"form-control"})
            'password':forms.PasswordInput(attrs={"class":"form-control"})
        }
方式二(钩子方法)
from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
class MyFrom(Form.ModelForm):
    #validators后面可以写正则表达式		^开始 $结束 [3-9]3到9里的一个数字 \d{9}9位数字
    phone = forms.CharFiled(label='手机号',validators=[RegexValidator(r'^1[3-9]\d{9}$','必须以159开头')])
    class Meta:
        model = UserIfo
        fields = ['name','password']
        
        #在函数里新建一个函数用于验证
        def clean_phone(self):
            #获取当前编辑那一行的id(用法配合9.2.3.5.1排除自己使用)
            self.instance.pk
            
            #获取用户输入的phone值
            txt_phone = self.cleaned_data["phone"]
            if len(txt_phone) != 11:
                #验证不通过抛出错误信息
                raise ValidationError('格式错误')
                #验证提供,把用户输入的值返回(返回的值就修改了用户输入的phone值,如果写入数据库就会写入返回的值,我们不能随意返回值)
            return txt_phone
不让浏览器帮我们校验
<!--novalidate不让浏览器帮我们校验-->
<form novalidate>
    
</form>

12.5 分页

#类似于for in 取列表 并拼成一句字符串
list=[]
lists = "".join(list)
后端写的标签传到前端
#需要拦截非法字符(xss攻击)

#需要把字符串标记为安全的
from django.utils.safestring import mark_safe
#包裹后传入前端即可编译
mark_safe(str)


#也可以在前端里
{{ obj.title|safe }}
计算需要分几页
divmod(100,3)  #得到--->(33,1)商33余1
a,b = divmod(100,3)		#a=33 b=1
#如果b不为空页数就是a+1
#b为空时为False
if b:

封装一个分页类运用到所有页面

class Pagination(object):
    def __init__(self,request,page_param="page")
    	page = page_param="page"
        #.isdecimal()如果字符串是否只包含十进制字符返回True,否则返回False。(判读是不是int类型)
        if page.isdecimal():
            page = int(page)
        else:
            page = 1
        self.page = page

要注意分页的时候把搜索值带上

import copy
#深拷贝一份
get_object = copy.deepcopy(request.GET)
#修改django里的._mutable为True才可以修改值
get_object._mutable = True
#设置值-->127.0.0.1/url?a=12
get_object.setlist("a",[12])

直接跳转无法携带搜索值

案例

分页类

class Page(object):
    '''
    自定义分页类
    可以实现Django ORM数据的的分页展示
    输出HTML代码:

    使用说明:
    from utils import mypage
        page_obj = mypage.Page(total_num, current_page, 'publisher_list')
        publisher_list = data[page_obj.data_start:page_obj.data_end]
        page_html = page_obj.page_html()

        为了显示效果,show_page_num最好使用奇数
    '''

    def __init__(self, total_num, current_page, url_prefix, per_page=10, show_page_num=9):
        '''

        :param total_num: 数据总条数
        :param current_page: 当前访问的页码
        :param url_prefix: 分页代码里a标签的前缀
        :param per_page: 每一页显示多少数据
        :param show_page_num: 页面上最多显示多少个页码


        '''
        self.total_num = total_num
        self.url_prefix = url_prefix
        self.per_page = per_page
        self.show_page_num = show_page_num
        # 根据传入的值计算当前页码左右放置多少个页码
        self.half_show_page_num = self.show_page_num // 2
        # 计算当前数据总共需要多少页码
        total_page, more = divmod(self.total_num, self.per_page)
        # 如果有余数就把页码加1
        if more:
            total_page += 1
        self.total_page = total_page
        # 数据有效性校验
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1
        # 如果当前页码数大于总页码数,默认展示最后一页数据
        if current_page > self.total_page:
            current_page = self.total_page
        # 如果当前页码小于1,默认展示第一页数据
        if current_page < 1:
            current_page = 1
        self.current_page = current_page
        # 计算页面上需要展示的页码范围
        # 页码过小处理
        if self.current_page - self.half_show_page_num <= 1:
            page_start = 1
            page_end = show_page_num
        # 页码过大处理
        elif self.current_page + self.half_show_page_num >= self.total_page:
            page_end = self.total_page
            page_start = self.total_page - self.show_page_num + 1
        # 未超过正常范围
        else:
            page_start = self.current_page - self.half_show_page_num
            page_end = self.current_page + self.half_show_page_num

        # 计算得到的最终页码数
        self.page_start = page_start
        self.page_end = page_end
        # 如果计算得到的页码数比总共需要展示页码数多,则把页码结束指定为总页码数
        if self.page_end > self.total_page:
            self.page_end = self.total_page

    @property
    def data_start(self):
        '''

        :return: 返回当前页面应该从哪里开始切数据
        '''
        return (self.current_page - 1) * self.per_page

    @property
    def data_end(self):
        '''

        :return: 返回当前页面从哪里结束数据
        '''
        return self.current_page * self.per_page

    def page_html(self):
        li_list = []
        # 添加前面的nav和ul标签
        li_list.append("""
        <nav aria-label="Page navigation">
        <ul class="pagination">
        """)
        # 添加首页
        li_list.append('<li><a href="/{}/?page=1">首页</a></li>'.format(self.url_prefix))
        # 添加上一页
        if self.current_page <= 1:  # 没有上一页
            prev_html = '<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'
        else:
            prev_html = '<li><a href="/{}/?page={}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(
                self.url_prefix,
                self.current_page - 1)
        li_list.append(prev_html)
        # 循环添加内部所有的页码
        for i in range(self.page_start, self.page_end + 1):
            if i == self.current_page:
                tmp = '<li class="active"><a href="/{0}/?page={1}">{1}</a></li>'.format(self.url_prefix, i)
            else:
                tmp = '<li><a href="/{0}/?page={1}">{1}</a></li>'.format(self.url_prefix, i)
            li_list.append(tmp)
        # 添加下一页
        if self.current_page >= self.total_page:  # 表示没有下一页
            next_html = '<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>'
        else:
            next_html = '<li><a href="/{}/?page={}" aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>'.format(
                self.url_prefix, self.current_page + 1)
        li_list.append(next_html)
        # 添加尾页
        li_list.append('<li><a href="/{}/?page={}">尾页</a></li>'.format(self.url_prefix, self.total_page))
        # 添加结尾nav和ul标签
        li_list.append("""
        </ul>
        </nav>
        """)
        # 将生成的标签拼接成一个大字符串
        page_html = ''.join(li_list)
        return page_html

使用办法

# 获取分页所需数据
total_num = data.count() # 获取数据总计条数
current_page = request.GET.get('page') # 获取当前页页码
url_prefix = request.path_info.strip('/') # 获取a标签所需参数

# 调用分页类
# 调用类传入参数生成实例
page_obj = mypage.Page(total_num, current_page, url_prefix, per_page=1)
# 按照分页所需数据对总数据进行分割获取当前页面所需展示的数据
data = data[page_obj.data_start:page_obj.data_end]
# 获取HTML代码 
page_html = page_obj.page_html()
#封装成字典传入前端即可

12.6 时间选择插件

参考

<!-- bootstrap样式表 -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

<!-- 时间选择器样式表 -->
<link href="https://cdn.bootcss.com/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css" rel="stylesheet">


<!-- jquery -->
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>

<!-- bootstrap脚本 -->
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

<!-- 时间选择器前置脚本 -->
<script src="https://cdn.bootcss.com/moment.js/2.22.1/moment-with-locales.min.js"></script>

<!-- 时间选择器核心脚本 -->
<script src="https://cdn.bootcss.com/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>
  • jquery版本:可选用 1.8.3 到 3.3.1(最新版样式出不来)
  • bootstrap版本:选用v3的

Script标签

参考

<script type="text/javascript">
        $('#date').datetimepicker({
        forceParse: 0,//设置为0,时间不会跳转1899,会显示当前时间。
        language: 'zh-CN',//显示中文
        format: 'yyyymmdd',//显示格式
        minView: "month",//设置只显示到月份
        initialDate: new Date(),//初始化当前日期
        autoclose: true,//选中自动关闭
        todayBtn: true//显示今日按钮
    })
   $("#date").datetimepicker("setDate", new Date() );  //设置显示默认当天的时间
</script>

12.7ModelFrom和BootStrap

方法一
定义样式
from django import forms
class MyFrom(Form.ModelForm):
    xx = models.CharField(verbose_name='确认密码',max_length=16)
    #不仅支持去models里获取字段也能自定义字段
    class Meta:
        model = UserIfo
        fields = ['name','password','xx']
    	#定义标签的属性(逐一添加属性)
        #render_value=True报错不清空,一般默认不清空但是密码默认会清空
        widgets = {
            'name':forms.TextInput(attrs={"class":"form-control"})
            'password':forms.PasswordInput(attrs={"class":"form-control"},
                                           render_value=True)
        	}
    
        

from django import forms
class MyFrom(Form.ModelForm):
    xx = models.CharField(verbose_name='父母',max_length=16)
    #不仅支持去models里获取字段也能自定义字段
    class Meta:
        model = UserIfo
        fields = ['name','password','xx']
    def __init__(self, *args, **kwargs):
            super().__init__(*args,**kwargs)
            # 循环找到所有插件,添加class属性值
            # name是字段名
            for name, field in kwargs.items():
                #字段为password的不加class属性
                #if name == "password":
                 #   continue
                 #                       添加class属性 			 显示数据库verbose_name
                field.widget.attrs = {"class":"from-contorl","placeholder":field.label}

优化

from django import forms
class MyFrom(Form.ModelForm):
    class Meta:
        model = UserIfo
        fields = ['name','password']
    def __init__(self, *args, **kwargs):
            super().__init__(*args,**kwargs)
			#循环找到所有插件,添加class属性值
            for name, field in kwargs.items():
                if field.widget.attrs:
                    field.widget.attrs['class'] = 'form-control'
                    field.widget.attrs['placeholder'] = field.label
                 
                field.widget.attrs = {"class":"from-contorl","placeholder":field.label}
自定义类
from django import forms
class BootStrapMyFrom(Form.ModelForm):
    def __init__(self, *args, **kwargs):
            super().__init__(*args,**kwargs)
            for name, field in kwargs.items():
                if field.widget.attrs:
                    field.widget.attrs['class'] = 'form-control'
                    field.widget.attrs['placeholder'] = field.label
                 
                field.widget.attrs = {"class":"from-contorl","placeholder":field.label}
用法
import BootStrapMyFrom
class MyFrom(BootStrapMyFrom):
    class Meta:
        model = UserIfo
        fields = ['name','password']

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SrcJ7O7U-1657435349718)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508011705707.png)]

13 目录

创建APP:python mange.py startapp app名

HelloWorld
|-- HelloWorld
|   |-- __init__.py
|   |-- settings.py
|   |-- urls.py
|   `-- wsgi.py
| 
|--app01
|	|--templates
|	|--utils (自己写的模块)
|	|--static
|	|	|--css
|	|	|--js
|	|	|--img
|	|	`--plugins
|	|
|	|--__init__.py
|	|--admin.py		[固定,不用动](django默认提供admin后台管理)
|	|--apps.py		[固定,不用动](app启动类)
|	|--migrations	[固定,不用动](数据库变更记录)
|	|	`--__init__.py
|	|
|	|--models.py	[重要](对数据库的操作)
|	|--tests.py		[固定,不用动](单元测试)
|	`--views.py		[重要](函数)
|	
`-- manage.py

14 MD5加密存储到数据库

正常MD5加密


import hashlib
def md5(password):
    obj = hashlib.md5()
    obj.update(data_string.encode('utf-8'))
    return obj.hexdigest()

盐值加密

md5密文是固定的所以一般在自己用的时候添加上自己的盐值
import hashlib
def md5(password):
    yan = 'xxx'
    obj = hashlib.md5(yan.encode('utf-8'))
    obj.update(data_string.encode('utf-8'))
    return obj.hexdigest()
用Django为我们生成的盐来加密
from django.conf import settings
import hashlib
def md5(password):
    obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))
    obj.update(data_string.encode('utf-8'))
    return obj.hexdigest()

15 用户登录

cookie和session

  • cookie:保存在浏览器的键值,发送请求时自动携带
  • session(数据库【Django默认】、redis、文件)

解决了

  • 无状态&短链接(一次请求和响应过后就会断开连接)

访问网页的时候服务器给浏览器返回响应体和响应头

  • 响应体(请求的内容…)
  • 响应头(cookie…)
class LoginForm(forms.Form):
    username forms.CharField(
    label="用户名",
    widget=forms.TextInput(attrs={"class":"form-control"})
    )
    password forms.CharField(
    label="密码",
    widget=forms.PasswordInput(attrs={"class":"form-control"})
    )

校验密码

def login(request):
#登录
    if request.method =="GET":
        form = LoginForm()
        return render(request,'login.html',{'form':form})
    form = LoginForm(data=request.POST)
    if form.is_valid():
        admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
        if not admin_object:
            #自定义一个校验错误
            #form.add_error("password","用户名或密码错误")
            form.add_error("username,"用户名或密码错误")
            return render(request,'login.html',{'form':form})
        return HttpResponse("提交成功")
    return render(request,'login.html',{'form':form})

cookie和session生成

def login(request):
#登录
    if request.method =="GET":
        form = LoginForm()
        return render(request,'login.html',{'form':form})
    form LoginForm(data=request.POST)
    if form.is_valid():
        admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
        if not admin_object:
            #自定义一个校验错误
            #form.add_error("password","用户名或密码错误")
            form.add_error("username,"用户名或密码错误")
            return render(request,'login.html',{'form':form})
          #网站生成随机字符串;写到用户浏览器的cookie中;再写到session中
          #服务器里就保存了一个info={'id':1,'name':佩奇} 
          #session保存到了django_session表里
        request.session["info"] = {'id':admin_object.id,'name':admin_object.username}
        return HttpResponse("提交成功")
    return render(request,'login.html',{'form':form})

判断有没有session

在其它需要登录的页面加上

def login(request):
#取到上面存入数据库的字典info={'id':1,'name':佩奇}
#没有登录cookie会返回一个None
	a= request.session.get['info']
    #如果a没有值
    if not a:
        return redirect('/denglu')
    elsepass

注:用中间件代替

中间件

中间件文件夹(middleware)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zdE4yiDn-1657435349720)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508031645437.png)]

from django.utils.deprecation import MiddlewareMixin

class C1(MiddlewareMixin):
    # 中间件1
    def process_request(self, request):
        print('C1,来了')
    def process_response(self, request, response):
        print('C1,走了')
        return response

class C2(MiddlewareMixin):
    # 中间件2
    def process_request(self, request):
        #如果方法里没有返回值,那么安装黑色路径继续走
        #有返回值就直接走红色路径返回
        print('C2,来了')
    def process_response(self,request, response):
        print('C2,走了')
        #必须返回response
        return response

运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1icQk9Rr-1657435349723)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508033210917.png)]

应用中间件

在settings.py里的MIDDLEWARE列表里把中间件添加上去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kW6HRcZc-1657435349723)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508033305980.png)]

中间件实现登录校验
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect


class AuthMiddleware(MiddlewareMixin):
    # 中间件1
    def process_request(self, request):
        # 排除登录页面的请求
        # 获取当前用户访问的url /login/
        if request.path_info == '/login/':
            return None
        #读取当前访问的用户session信息
        info_dict = request.session.get('info')
        if info_dict:
            return
        else:
            return redirect('/login/')

    def process_response(self, request, response):
        return response

注销

def login(request):

	a= request.session.clear()
    return rdirect('/denglu/')

获取用户登录的session信息

dict = request.session['info']
<!--前端直接获取-->
{{ request.session.info.name }}
{{ request.session.info.id }}

图片验证码

python生成图片

PIL库

pip install pillow
#生成图片
from PIL import Image,

img = Image.new(mode='RGB',size=(120,30),color=(255,255,255))
with open('./code.png','wb')as f:
    img.save(f,format='png')
完善集成代码
用法
from Image_authentication import get_code
#内存中的文件
from io import BytesIO
#重写一个url用于验证图片
def images_code(request):
    image_txt,image = get_code()
    #把验证码写进session里
    request.session['image_txt']=image_txt
    #设置session超时60后验证码失效
    request.session.set_expiry(60)
    Memory_file = BytesIO()
    image.save(Memory_file,'png')
    #把内存里的图片返回给页面
    return Httpresponse(Memory_file.getvalue())
ModelFrom添加图片验证框

参考上面自定义字段

验证码校验(登录的视图函数)
def login(request):
#登录
    if request.method =="GET":
        form = LoginForm()
        return render(request,'login.html',{'form':form})
    form LoginForm(data=request.POST)
    if form.is_valid():
        admin_object=models.Admin.objects.filter(**form.cleaned_data).first()
        if not admin_object:
            #因为后面还要用用户名和密码去数据库验证,所以要用pop把验证码提取并剔除,验证码不会去数据库校验
			user_input_code = form.cleaned_data.pop('code') #用户输入的验证码
            #因为验证码可能为空所以后面加上取空值
            user_code= request.session.get('image_code','')  #session里正确的验证码
        	if user_input_code != user_code:
            	form.add_error("code","验证码错误")
                return render(request,'login.html',{'form':form})
            
            form.add_error("username,"用户名或密码错误")
            return render(request,'login.html',{'form':form})
        request.session["info"] = {'id':admin_object.id,'name':admin_object.username}
              #设置验证成功一天免登录(session保存1天)
        request.session.set_expiry(60*60*24)
        return HttpResponse("提交成功")
    return render(request,'login.html',{'form':form})

16 Ajax请求

浏览器向服务器发送的请求以:向URL发送表单的形式提交

  • GET

  • POST

    每次操作都要刷新页面

Ajax:偷偷发送请求(不刷新)

  • 依赖jQuery
  • Ajax代码
$.ajax({
    url:'xxx',
    type:'get',
    data:{
        n1:12,
        n2:23
    },
    //发送成功自动执行
    success:function(res){
        console.log(res);
    }
})

GET请求

$.ajax({
    url:'/task/ajax/',
    type:'get',
    data:{
        n1:12,
        n2:23
    },
    success:function(res){
        console.log(res);
    }
})

后端

def task_ajax(request):
    request.GET
    return HttpResponse(request.GET)

POST请求

$.ajax({
    url:'/task/ajax/',
    type:'post',
    data:{
        n1:12,
        n2:23
    },
    success:function(res){
        console.log(res);
    }
})
可以用上面的批量绑定click点击事件
//ajxa默认不携带cookie
//解决办法
from django.views.decorators.csrf import csrf_exempt
在函数上面加上
@csrf_exempt
def task_ajax(request):
    request.GET
    return HttpResponse(request.GET)

Ajax请求的返回值

  • 返回类型:json

后端

$.ajax({
    url:'/task/ajax/',
    type:'post',
    data:{
        n1:12,
        n2:23
    },
    dataType:'JSON'
    success:function(res){
        console.log(res);
    	console.log(res.a);
    	console.log(res.b);
    }
})
def task_ajax(request):
    dict = {
        'a':12,
        'b':34
    }
    #以json格式返回前端	JsonResponse(dict) = Httpresponse(json.dumps(dict))
    return JsonResponse(dict)

页面上的数据写入ajax

$.ajax({
    url:'/task/ajax/',
    type:'post',
    data:{
        user:$('#user').val(),
        password:$('#password').val()
    },
    dataType:'JSON'
    success:function(res){
        console.log(res);
    	console.log(res.a);
    	console.log(res.b);
    }
})

自动写ajax传入后端的值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GcGu7f9b-1657435349725)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508232152198.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nCcUSutw-1657435349730)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508232228145.png)]

案例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5HEbuD1g-1657435349731)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508234558328.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7KX9p2Eu-1657435349733)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508232228145.png)]

html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z7agGnsT-1657435349735)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508234838719.png)]

把错误写到html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oUf1AekS-1657435349748)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508234658363.png)]

17 上传文件

基本操作

  • request.FILES 请求发送过来的文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GvV0sKGQ-1657435349751)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220509002043979.png)]

不加enctype默认只是上传文件名,加上才上传了真正的文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XS9fJ6f7-1657435349754)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220509002303320.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aLGFaAPf-1657435349755)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220509002455007.png)]

  • file_object.name 文件名

  • file_object.chunks 文件数据

  • chunk 文件里的一部分数据

    一部分一部分的读取

​ 用form生成input标签

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X1x0aXny-1657435349755)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220509004540163.png)]

写上第二行的就排除img标签不给他加上bootstrap样式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fU1JPpxV-1657435349756)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220509004755564.png)]

上传文件校验的时候一定要加上files

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YMwlT853-1657435349760)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220609234355937.png)]

dataType:'JSON'
success:function(res){
    console.log(res);
	console.log(res.a);
	console.log(res.b);
}

})




```python
def task_ajax(request):
    dict = {
        'a':12,
        'b':34
    }
    #以json格式返回前端	JsonResponse(dict) = Httpresponse(json.dumps(dict))
    return JsonResponse(dict)

页面上的数据写入ajax

$.ajax({
    url:'/task/ajax/',
    type:'post',
    data:{
        user:$('#user').val(),
        password:$('#password').val()
    },
    dataType:'JSON'
    success:function(res){
        console.log(res);
    	console.log(res.a);
    	console.log(res.b);
    }
})

自动写ajax传入后端的值

[外链图片转存中…(img-GcGu7f9b-1657435349725)]

[外链图片转存中…(img-nCcUSutw-1657435349730)]

案例

[外链图片转存中…(img-5HEbuD1g-1657435349731)]

[外链图片转存中…(img-7KX9p2Eu-1657435349733)]

html

[外链图片转存中…(img-Z7agGnsT-1657435349735)]

把错误写到html

[外链图片转存中…(img-oUf1AekS-1657435349748)]

17 上传文件

基本操作

  • request.FILES 请求发送过来的文件

[外链图片转存中…(img-GvV0sKGQ-1657435349751)]

不加enctype默认只是上传文件名,加上才上传了真正的文件

[外链图片转存中…(img-XS9fJ6f7-1657435349754)]

[外链图片转存中…(img-aLGFaAPf-1657435349755)]

  • file_object.name 文件名

  • file_object.chunks 文件数据

  • chunk 文件里的一部分数据

    一部分一部分的读取

​ 用form生成input标签

[外链图片转存中…(img-X1x0aXny-1657435349755)]

写上第二行的就排除img标签不给他加上bootstrap样式

[外链图片转存中…(img-fU1JPpxV-1657435349756)]

上传文件校验的时候一定要加上files

[外链图片转存中…(img-YMwlT853-1657435349760)]

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值