django整合版

内容目录

目录

django的下载安装创建应用

django怎么下载安装?

cmd中 PIP方式安装django
pip3 install django==1.11.9 
pip3 install django==1.11.9 -i http://xxxxxx  指定源

pycham中装模块的地方安装 或界面下Terminal安装
File | Settings | Project: tushu | Project Interpreter

django怎么创建启动项目?

创建项目
	django-admin startproject mysite   创建了一个名为"mysite"的Django 项目
启动项目
	python manage.py runserver  默认是127.0.0.1:8000
	python manage.py runserver 127.0.0.1  默认端口号是8000
    python manage.py runserver 127.0.0.1:8001 
    
pycham 创建
	注意不要用虚拟环境创建

django怎么创建应用?

python manage.py startapp book
实际也是创建了一个文件夹book

ps注意 自己手动创建需要在配置文件 注册app  

列如注册了app01的app

1578016087046

列如注册了api的app

1578016155585

django生命周期

配置文件相关

配置了django内置的app  也可以加入自己注册的app
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
]

控制可以访问的网段

ALLOWED_HOSTS = []

中间件

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',
]

数据库

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

HTTP协议

HTTP协议详细https://www.cnblogs.com/clschao/articles/9230431.html

HTTP协议是什么?
	什么是无状态 无连接?
    
HTTP请求与响应信息格式?
	http的请求方法?
    get请求和post的请求的区别?
    ttp的状态码?

HTTP协议有什么用?

HTTP协议有什么方法,怎么使用?

HTTP协议是什么?

1. 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。是WWW (World Wide Web)实现数据通信的基石。
2. 它是一种应用层协议(OSI七层模型的最顶层),它基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。
3. HTTP协议用于客户端和服务端之间的通信,通过请求和响应的交互达成通信(肯定是先从客户端开始建立通信的,服务器端在没有接收到任何请求之前不会发送响应)。
4. HTTP是 无状态 无连接 的协议,为了实现期望的保存状态的功能,引入了Cookie技术。

引申
什么是无状态 无连接?
无状态就是 使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产生。协议本身并不保留之前一切的请求或响应报文的信息。
无连接就是 每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接

HTTP请求与响应信息格式?

请求消息格式:请求行\请求头部\空行\请求数据
	请求行:GET 路径 HTTP/1.1
	
响应消息格式:响应行\响应头部\空行\响应数据
	响应行: HTTP/1.1 状态码 状态描述
引申
http的请求方法?
get\post\put\delete\head\options\trace\connect\

get请求和post的请求的区别?
get请求 请求数据部分是没有数据的,get请求的数据在url上,在请求行里面,有大小限制,常见的get请求方式: 浏览器输入网址,a标签
post请求 请求数据在请求体(请求数据部分) ,数据没有大小限制, 常见方式:form表单提交数据

http的状态码?
	1xx请求被接受但是还在处理当中 
	2xx请求成功
	3xx重定向(301永久重定向和302临时重定向)
	4xx客户端错误(请求错误)
	5xx服务器错误

http协议
请求信息格式
GET / HTTP/1.1   请求行
Host: 127.0.0.1:8003  请求头
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
#空行
请求数据  username=ziwen password=666




HTTP协议有什么用?

作用:在浏览器与服务器间传送文档。

中间件

什么是中间件?

中间件是一个用来处理Django的请求和响应的框架级别的钩子
中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。

如何自定义中间件?

3步骤

第一步 在应用下键立一个文件夹(随意取名字)在文件夹下创建一个py文件名字随意取

第2步 在py文件中引入from django.utils.deprecation import MiddlewareMixin 在定义一个中间件(类)

第3步在配置文件 配置

中间件有5个方法
process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)

实列

在应用下键立一个文件夹(随意取名字)在创建一个py文件 py文件下引入from django.utils.deprecation import MiddlewareMixin 定义一个类
配置文件配置


from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
    #自定义中间件,不是必须要有下面这两个方法,有request方法说明请求来了要处理,有response方法说明响应出去时需要处理,不是非要写这两个方法,如果你没写process_response方法,那么会一层一层的往上找,哪个中间件有process_response方法就将返回对象给哪个中间件
    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

中间件的5个方法


中间件方法执行流程

img


url路由分发

别名和反向解析

为什么要有别名 怎么使用?

我们给url 起一个别名· 以后不管url怎么改 都可以实现原来的网址 就不会写死了

url文件写法
	url(r'^index2/', views.index,name='cs'),
反向解析
	后端views:
         from django.urls import reverse 可以实现反向解析
		 reverse('别名')  例如:reverse('cs') 反向解析为别名对应的地址 /index2/
         带参数的反向解析: reverse( 'index' ,args=(10,))--- /index2/10/
	html: {% url '别名' %} -- 例如:{% url 'cs' %} -- /index2/
下面是代码


实列
使其重定向至index别名对应的地址  /index2/10/
return redirect(everse( 'index' ,args=(10,)))


rurl文件写法

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^cs/', views.home,name='cs'),
    url(r'^cs1/', views.cs1,name='cs1'),
]


后端views.py

用于重定向 redirect
我们利用别名 以后不管 别名cs1 对应的网址这么变  我们通过反解析 ,都可以找到他,写活了他 
from django.shortcuts import render,HttpResponse,redirect
from django.urls import reverse

def home(request):
    return redirect(reverse('cs1'))
def cs1(request):
    return HttpResponse('测试')


url路由分发

各个应用间怎么进行路由分发?

创建应用python manage.py startapp 子应用名称

在每app下传建urls文件导入以下配置

from django.conf.urls import url
from django.contrib import admin

from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

主项目如下 配置 先导入include

from django.conf.urls import url,include
from django.contrib import admin

urlpatterns = [
    url(r'^app01/', include('app01.urls',namespace='app01')),
    url(r'^app02/', include('app02.urls',namespace='app02')),

]



url命名空间

为什么要有url命名空间 怎么使用?

在路由分发中可能会错乱为了 避免 设置命名空间名称namespace

from django.conf.urls import url,include
from django.contrib import admin
urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls',namespace='app01')),
    url(r'^app02/', include('app02.urls',namespace='app02')),
	
]


使用:
	后端:reverse('命名空间名称:别名') -- reverse('app01:home') 
	hmtl:{% url '命名空间名称:别名' %}  -- {% url 'app01:home' %}


视图

视图是什么?
视图中的django中请求处理方式中CBV和FBV分别是什么?
书写一个FBV(函数类)
书写一个CBV(对象)

FBV(函数类) CBV(对象)加装饰器?

请求相关:request 对象  方法
响应相关:HTTPResponse 对象 方法



视图是什么?

一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应
无论视图本身包含什么逻辑,都要返回响应。
约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为views.py的文件中


视图中的django中请求处理方式中CBV和FBV分别是什么?

基于函数的view,就叫FBV
基于类的view,就叫CBV
书写一个FBV(函数类)
书写一个CBV(对象)
代码如下


书写一个FBV(函数类)

FBV(function base views) 就是在视图里使用函数处理请求。

from django.shortcuts import render,HttpResponse,redirect
def cs(request):
    return redirect('/cs1/')  #重定向  redirect(路径)


书写一个CBV(对象)

CBV(class base views) 就是在视图里使用类处理请求。

最后一步源码

 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

def dispatch(self, request, *args, **kwargs):
    # Try to dispatch to the right method; if a method doesn't exist,
    # defer to the error handler. Also defer to the error handler if the
    # request method isn't on the approved list.
    if request.method.lower() in self.http_method_names:#实现分发的
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)


通过剖析源码 我们可以在分发前执行我们的逻辑

from django.views import View
class LoginView(View):
    # def dispatch(self, request, *args, **kwargs):
    #     print('xx请求来啦!!!!')请求来之前  但不知道是什么方法前执行
    #     ret = super().dispatch(request, *args, **kwargs)
    #     print('请求处理的逻辑已经结束啦!!!')
    #     return ret
    def get(self,request):  #处理get请求直接定义get方法,不需要自己判断请求方法了,源码中用dispatch方法中使用了反射来处理的
        print('小小小小')
        return render(request,'login.html')

    def post(self,request):
        print(request.POST)
        return HttpResponse('登录成功')

注意类请求 urls.py路由写法

url(r'^路径/', views.类名.as_view()),
url(r'^login/', views.LoginView.as_view()),

FBV(函数类) CBV(对象)加装饰器?

FBV本身就是一个函数,所以和给普通的函数加装饰器无差别

CBV(对象)加装饰器需要先将其转换为方法装饰器。Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。

代码如下

装饰器装饰FBV

FBV本身就是一个函数,所以和给普通的函数加装饰器无差

def wrapper(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = func(*args, **kwargs)
        end_time = time.time()
        print("used:", end_time-start_time)
        return ret
    return inner


# FBV版添加班级
@wrapper
def add_class(request):
    if request.method == "POST":
        class_name = request.POST.get("class_name")
        models.Classes.objects.create(name=class_name)
        return redirect("/class_list/")
    return render(request, "add_class.html")


装饰器装饰CBV

类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。

第一步先引入模块from django.utils.decorators import method_decorator`
第2步 加语法糖@method_decorator(wrapper)`


from django.shortcuts import render,HttpResponse
from django.views import View
from django.utils.decorators import method_decorator
def wrapper(func):
    def inner(*args, **kwargs):
        print(11111)
        ret = func(*args, **kwargs)
        print(22222)
        return ret
    return inner

# @method_decorator(wrapper,name='get')  # 方式3给get加 用的不多
class LoginView(View):

    @method_decorator(wrapper)  #方式1
    def get(self,request):
        print('小小小小')
        return HttpResponse('登录成功')

    def post(self,request):
        print(request.POST)
        return HttpResponse('登录成功')


请求相关:request 对象 属性

path_info 返回用户访问url,不包括域名
method 请求中使用的HTTP方法的字符串表示,全大写表示。
GET 包含所有HTTP GET参数的类字典对象
POST 包含所有HTTP POST参数的类字典对象
body 请求体,byte类型 request.POST的数据就是从body里面提取到的

request.method    ——》 请求的方式 8种  GET POST PUT DELETE OPTIONS
		request.GET		  ——》 字典  url上携带的参数
		request.POST	  ——》 字典  form表单通过POST请求提交的数据
		request.path_info ——》 URL路径 不带参数 
		request.body	  ——》 请求体
		request.FILES  		上传的文件  {}
		request.COOKIES     cookie
		request.session		session
		request.META			请求头


响应相关:HTTPResponse 对象

from django.shortcuts import render,HttpResponse,redirect
HTTPResponse('字符串') #返回字符串
render(request,'xx.html')#返回html页面

redirect 重定向
def cs(request):
    return redirect('/cs1/')  #重定向到url为cs1的地址

def cs1(request):
    return HttpResponse('666') #返回字符串666
    
def cs1(request):
    render(request,'xx.html')#返回html页面


orm

什么是ORM

1. MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的方便了我们开发
2. ORM 是“对象-关系-映射”的简称。类名对应表名 字段对应属性
3. orm就是将类对象的语法翻译成sql语句的一个引擎,


单表操作

创建表

创建表3步曲

第1步 数据库连接配置
第2步 models.py文件中创建一个类 也就是写类的键表语句
第3步执行同步指令 使其类语句转化为数据库可理解的语句


第1步连接数据库前配置

settings配置文件中
自己先在数据库中创建库

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'cs',
        'USER':'root',
        'PASSWORD':'123',
        'HOST':'127.0.0.1',
        'PORT':3306,
    }
}

2 项目文件夹下的init文件中写上下面内容,用pymysql替换mysqldb
因为django链接mysql默认使用的是mysqldb但其不支持py3.7所以不用
import pymysql
pymysql.install_as_MySQLdb()


settings配置文件中将 USE_TZ 的值改为 False
    # USE_TZ = True
    USE_TZ = False  # 告诉mysql存储时间时按照当地时间来寸,不要用utc时间
使用pycharm的数据库客户端的时候,时区问题要注意


第2步 创建一个类语句

models.py文件中创建一个类 同步指令会翻译成mysql语句
UserInfo 是表名 
models 固定写法
models后面AutoField等是 字段  注意id字段不创建也可以,系统会自动创建
括号里面的primary_key等是 属性


创建学生表实列
class UserInfo(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=10)
    bday = models.DateField()
    checked = models.BooleanField()


第3步执行数据库同步指令

第1种方式
在 Terminal中执行
python manage.py makemigrations  #生成记录,每次修改了models里面的内容或者添加了新的app,新的app里面写了models里面的内容,都要执行这两条
python manage.py migrate         #执行上面这个语句的记录来创建表,生成的表名字前面会自带应用的名字,例如:你的book表在mysql里面叫做app01_book表

第2种方式 带提示
tool---Run manage.py Task....或Ctrl+Alt+R  调出提示环境
执行 makemigrations 和 migrate  

执行同步指令会在当前应用文件下下生成migrations文件夹

注意
每次修改了models里面的内容或者添加了新的app,新的app里面写了models里面的内容,都要执行这两条  添加字段的时候别忘了,该字段不能为空,所有要么给默认值,要么设置它允许为空null=True


更多字段和参数
<1> CharField
        字符串字段, 用于较短的字符串.
        CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数.
 
<2> IntegerField
       #用于保存一个整数.
 
<3> DecimalField
        一个浮点数 小数字段. 必须 提供两个参数:
        参数    描述
        max_digits    总位数(不包括小数点和符号)
        decimal_places    小数位数
                举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段:
                models.DecimalField(..., max_digits=5, decimal_places=2)

 
<4> AutoField
        一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段;
        自定义一个主键:my_id=models.AutoField(primary_key=True)
        如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.
 
<5> BooleanField    bool字段
        A true/false field. admin 用 checkbox 来表示此类字段.
 
<6> TextField
        一个容量很大的文本字段.
        admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框).
 
<7> EmailField
        一个带有检查Email合法性的 CharField,不接受 maxlength 参数.
 
<8> DateField
        一个日期字段. 共有下列额外的可选参数:
        Argument    描述
        auto_now    当对象被保存时(更新或者添加都行),自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳.
        auto_now_add    当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间.
        (仅仅在admin中有意义...)
 
<9> DateTimeField  一般用这个
         一个日期时间字段. 类似 DateField 支持同样的附加选项.
 
<10> ImageField 图片·
        类似 FileField, 不过要校验上传对象是否是一个合法图片.#它有两个可选参数:height_field和width_field,
        如果提供这两个参数,则图片将按提供的高度和宽度规格保存.    
        
<11> FileField  文件
     一个文件上传字段.
     要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime #formatting,
     该格式将被上载文件的 date/time
     替换(so that uploaded files don't fill up the given directory).
     admin 用一个<input type="file">部件表示该字段保存的数据(一个文件上传部件) .
 
     注意:在一个 model 中使用 FileField 或 ImageField 需要以下步骤:
            (1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件.
            (出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对
             WEB服务器用户帐号是可写的.
            (2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django
             使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT).
             出于习惯你一定很想使用 Django 提供的 get_<#fieldname>_url 函数.举例来说,如果你的 ImageField
             叫作 mug_shot, 你就可以在模板中以 {{ object.#get_mug_shot_url }} 这样的方式得到图像的绝对路径.
 
<12> URLField
      用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且
      没有返回404响应).
      admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框)
 
<13> NullBooleanField
       类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项
       admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据.
 
<14> SlugField
       "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.#它们通常用于URLs
       若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50.  #在
       以前的 Django 版本,没有任何办法改变50 这个长度.
       这暗示了 db_index=True.
       它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-#populate
       the slug, via JavaScript,in the object's admin form: models.SlugField
       (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields.
 
<13> XMLField
        一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema #的文件系统路径.
 
<14> FilePathField
        可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的.
        参数    描述
        path    必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目.
        Example: "/home/images".
        match    可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名. 
        注意这个正则表达式只会应用到 base filename 而不是
        路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif.
        recursive可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录.
        这三个参数可以同时使用.
        match 仅应用于 base filename, 而不是路径全名. 那么,这个例子:
        FilePathField(path="/home/images", match="foo.*", recursive=True)
        ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif
 
<15> IPAddressField
        一个字符串形式的 IP 地址, (i.e. "24.124.1.30").
<16> CommaSeparatedIntegerField
        用于存放逗号分隔的整数值. 类似 CharField, 必须要有maxlength参数.
 


更多参数

1)null
 
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
 
(1)blank
 
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
 
(2)default
 
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
 
(3)primary_key
 
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。
 
(4)unique
 
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
 
(5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。
(6)db_index
  如果db_index=True 则代表着为此字段设置数据库索引。


DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。

(7)auto_now_add
    配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

(8)auto_now
    配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。


单表增加记录

2步曲

第1步引入对应的models.py文件
第2步 书写添加数据语句 有2种方式


实列

第1种添加方式
from app01 import models
def tj(request):
    new_obj = models.UserInfo(
        id=1,
        name='小白',
        bday='2019-09-27',  # current_date
        checked=1,
    )
    new_obj.save()  # 翻译成sql语句,然后调用pymysql,发送给服务端
    print(new_obj)#UserInfo object 对象
    print(new_obj.name)
    return HttpResponse('tj成功')


第2种添加方式
from app01 import models
def tj(request):
    ret = models.UserInfo.objects.create(
        name='小白1',
        bday='2019-09-27',
        checked=0
    )
    print(ret)#UserInfo object 对象
    print(ret.name)
    return HttpResponse('tj成功')

   


增加时间出现的bug
import datetime
current_date = datetime.datetime.now()

models.UserInfo.objects.create(
        name='杨泽涛2',
        bday='2019-09-27',# 直接插入时间没有时区问题
    	cszhiduan=current_date,# 系统插入时间有时区问题current_date是当前时间
        checked=0
    )
	但是如果让这个字段自动来插入时间,就会有时区的问题, auto_now_add可以使创建记录时自动添加当前创建记录时的时间,存在时区问题
cszhiduan = models.DateTimeField(auto_now_add=True,null=True)

解决方法:
    settings配置文件中将USE_TZ的值改为False
    # USE_TZ = True
    USE_TZ = False  # 告诉mysql存储时间时按照当地时间来寸,不要用utc时间
使用pycharm的数据库客户端的时候,时区问题要注意


批量插入数据
    # bulk_create
    obj_list = []
    for i in range(20):
        obj = models.Book(
            title=f'金瓶{i}',
            price=20+i,
            publish_date=f'2019-09-{i+1}',
            publish='24期出版社'
        )
        obj_list.append(obj)

    models.Book.objects.bulk_create(obj_list)  #批量创建
    
    
request.POST --  querydict类型 {'title': ['asdf '], 'price': ['212'], 'publish_date': ['2019-09-12'], 'publish': ['asdf ']}
data = request.POST.dict() -- 能够将querydict转换为普通的python字典格式

创建数据
	models.Book.objects.create(
            # title=title,
            # price=price,
            # publish_date=publish_date,
            # publish=publish
            **data
        )


查询表纪录

在学查表前先搞清楚
queryset是查询集是什么?model对象是什么?
	queryset是类似列表 里面是model对象 queryset的方法model对象不一定可以用
	model对象可以用点取值 而queryset不可以



<1> all():                  查询所有结果,结果是queryset类型
  查询所有的数据  .all方法 返回的是queryset集合
    all_objs = models.Student.objects.all()  # 类似于列表  --  queryset集合
    for i in all_objs:
         print(i.name)
    print(all_objs)
    
    
    
<2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象,结果也是queryset类型 Book.objects.filter(title='linux',price=100) #里面的多个条件用逗号分开,并且这几个条件必须都成立,是and的关系,
  	models.Student.objects.all().filter(id=7)  queryset类型可以调用fitler在过滤
  

<3> get(**kwargs):          返回与所给筛选条件相匹配的对象,不是queryset类型,是行记录对象,返回结果有且只有一个,
                            如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。  Book.objects.get(id=1)
  
<4> exclude(**kwargs):      排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作昂,用这个exclude,返回值是queryset类型 Book.objects.exclude(id=6),返回id不等于6的所有的对象,或者在queryset基础上调用,Book.objects.all().exclude(id=6)
    exclude(**kwargs): 排除,objects控制器和queryset集合都可以调用,返回结果是queryset类型
    query = models.Student.objects.exclude(id=1)
    print(query)
    query = models.Student.objects.filter(age=38).exclude(id=6)
    print(query)
                 
<5> order_by(*field): 排序      queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型
                  models.Book.objects.all().order_by('price','id') #直接写price,默认是按照price升序排列,按照字段降序排列,就写个负号就行了order_by('-cs'),order_by('price','id')是多条件排序,按照price进行升序,price相同的数据,按照id进行升序
        
        
<6> reverse():              queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型
			# 只可以排序之后反转
  		    # query = models.Student.objects.all().order_by('id').reverse()
    		# print(query)

<7> count():       queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量。
  
<8> first():    queryset类型的数据来调用 ,返回第一条记录结果为model对象类型 Book值    
  
<9> last():                queryset类型的数据来调用,返回最后一条记录,结果为model对象类型
  
<10> exists():              queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False
                   空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits
                 例:all_books = models.Book.objects.all().exists() #翻译成的sql是SELECT (1) AS `a` FROM `app01_book` LIMIT 1,就是通过limit 1,取一条来看看是不是有数据

<11> values(*field):        用的比较多,queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
                            model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类型的其他的查找方法,其他方法也是一样的。 里面可以加子段显示
        
<12> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 里面可以加子段显示
 
<13> distinct():            values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录 去重,结果还是queryset 里面不可以传参 显示nanme等等可以用来去重
	query = models.Student.objects.all().values('age').distinct()
    print(query)
	


基于双下划线的模糊查询
Book.objects.filter(price__in=[100,200,300]) #price值等于这三个里面的任意一个的对象
Book.objects.filter(price__gt=100)  #大于,大于等于是price__gte=100,别写price>100,这种参数不支持
Book.objects.filter(price__lt=100) 小于
Book.objects.filter(price__range=[100,200])  #sql的between and,大于等于100,小于等于200
Book.objects.filter(title__contains="python")  #title值中包含python的
Book.objects.filter(title__icontains="python") #不区分大小写
Book.objects.filter(title__startswith="py") #以什么开头,istartswith  不区分大小写
Book.objects.filter(pub_date__year=2012) date类型


实列
# all_books = models.Book.objects.filter(pub_date__year=2012) #找2012年的所有书籍
    # all_books = models.Book.objects.filter(pub_date__year__gt=2012)#找大于2012年的所有书籍
    all_books = models.Book.objects.filter(pub_date__year=2019,pub_date__month=2)#找2019年月份的所有书籍,如果明明有结果,你却查不出结果,是因为mysql数据库的时区和咱们django的时区不同导致的,了解一下就行了,你需要做的就是将django中的settings配置文件里面的USE_TZ = True改为False,就可以查到结果了,以后这个值就改为False,而且就是因为咱们用的mysql数据库才会有这个问题,其他数据库没有这个问题。


删除表纪录

简单查询:filter()  -- 结果是queryset类型的数据里面是一个个的model对象,类似于列表
	models.UserInfo.objects.filter(id=7).delete()  #queryset对象调用
	models.UserInfo.objects.filter(id=7)[0].delete()  #model对象调用


修改表纪录

方式1:update
     models.UserInfo.objects.filter(id=2).update(
         name='篮子文',
         checked = 0,
    
     )
     #错误示例,model对象不能调用update方法
     models.UserInfo.objects.filter(id=2)[0].update(
         name='加篮子+2',
         # checked = 0,
     )
方式2 
    ret = models.UserInfo.objects.filter(id=2)[0]
    ret.name = '加篮子+2'
    ret.checked = 1
    ret.save()
    

更新时的auto_now参数
	# 更新记录时,自动更新时间,创建新纪录时也会帮你自动添加创建时的时间,但是在更新时只有使用save方法的方式2的形式更新才能自动更新时间,有缺陷,放弃
    now2 = models.DateTimeField(auto_now=True,null=True)


为什么要在类模型定义__str__方法?

为了便于观察 打印一个对象,让他显示一个能够看懂的值,__str__,当print()打印实例化对象时会调用__str__()

from django.db import models
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2,)
    pub_date = models.DateTimeField() #必须存这种格式"2012-12-12"
    publish = models.CharField(max_length=32)
    def __str__(self): #后添加这个str方法,也不需要重新执行同步数据库的指令
        return self.title #当我们打印这个类的对象的时候,显示title值


小练习 基于单表 增删改查

图书管理系统
实现对书籍的查看 新增 删除
内容和样式如下:
查看页面


解题思路

查看功能的实现
创建一张表 里面有 id 名称 价格 日期 出版社 字段
html
写页面记得 配置静态文件 利用orm .all获得查询集和 传给前端 前端 for 模板渲染
    STATIC_URL = '/static/'  #静态文件路径别名

    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, '项目下创建的文件夹名'),第2个参数创建的文件夹名
    ]
<link rel="stylesheet" href="/别名/bootstrap-3.3.7-dist/css/bootstrap.min.css">

def showbooks(request):
    if request.method == 'GET':
        books=models.Boo.objects.all()
        print(books)
        return render(request,'showbooks.html',{'books':books})

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>

<h1>查看书籍</h1>

<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
                <a href="/add_book/" class="btn btn-primary">添加书籍</a>
                <table class="table table-striped table-hover">
                <thead>
                <tr>
                    <th>编号</th>
                    <th>书籍名称</th>
                    <th>价格</th>
                    <th>出版日期</th>
                    <th>出版社</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% for book in books %}

                    <tr>
                        <td>{{ forloop.counter }}</td>
                        <td>{{ book.title }}</td>
                        <td>{{ book.price }}</td>
                        <td>{{ book.c_date|date:'Y-m-d' }}</td>
                        <td>{{ book.press }}</td>
                        <td>
                            <a href="" class="btn btn-warning">编辑</a>
                            <a href="" class="btn btn-danger">删除</a>
                        </td>
                    </tr>

                {% endfor %}

                </tbody>

            </table>


        </div>

    </div>

</div>

</body>
</html>



添加功能的实现
点击a标签 跳转页面(注意不用别名 要加/)
<a href="/add_book/" class="btn btn-primary">添加书籍</a>
input标签name值最好设置与表字段相同
返回查看页面 要用重定向 不然不走url分发 orm取不到数据库数据
 return render(request,'showbooks.html')

def add_book(request):
    if request.method == 'GET':
        return render(request,'add_book.html')
    else:
        ret = request.POST.dict()
        ret.pop('csrfmiddlewaretoken')
        print()
        models.Boo.objects.create(
            **(ret)
        )
        return render(request,'showbooks.html')

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>

<h1>添加书籍</h1>

<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <form action="" method="post">
                {% csrf_token %}
              <div class="form-group">
                <label for="title">书籍名称</label>{# "for" 属性的值设置为相关元素的 id 属性的值。#}
                <input type="text" class="form-control" id="title" placeholder="title" name="name">
              </div>
              <div class="form-group">
                <label for="price">价格</label>
                <input type="number" class="form-control" id="price" placeholder="price" name="price">
              </div>
               <div class="form-group">
                <label for="publish_date">出版日期</label>
                <input type="date" class="form-control" id="publish_date" placeholder="publish_date" name="c_date">
              </div>
               <div class="form-group">
                <label for="publish">出版社</label>
                <input type="text" class="form-control" id="publish" placeholder="publish" name="press">
              </div>

              <button type="submit" class="btn btn-success pull-right">提交</button>
            </form>


        </div>

    </div>



</div>


</body>
</html>


删除功能的实现

<a href="{% url 'delete_book' book.pk %}" class="btn btn-danger">删除</a>
对象.pk 就是独一无2的
models.Book.objects.filter(id=n).delete()


编辑功能的实现

点击编辑按钮pk传过去  获取到对象 对象传过去
修改values值 注意时间  {{ book_obj.publish_date|date:'Y-m-d' }}

<a href="{% url 'edit_book' book.pk %}" class="btn btn-warning">编辑</a>


def edit_book(request,n):
    book_obj = models.Book.objects.filter(pk=n)
    if request.method == 'GET':

        return render(request,'edit_book.html',{'book_obj':book_obj.first()})
    else:
        print(request.POST)
        # <QueryDict: {'title': ['金瓶7777'], 'price': ['27.00'], 'publish_date': ['2019-09-08'], 'publish': ['24期出版社xx']}>
        ret = request.POST.dict()

        book_obj.update(
            **ret
        )

        return redirect('showbooks')


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>

<h1>编辑书籍</h1>

<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">

            <form action="" method="post">
              <div class="form-group">
                <label for="title">书籍名称</label>
                <input type="text" class="form-control" id="title" placeholder="title" name="title" value="{{ book_obj.title }}">
              </div>
              <div class="form-group">
                <label for="price">价格</label>
                <input type="number" class="form-control" id="price" placeholder="price" name="price" value="{{ book_obj.price }}">
              </div>
               <div class="form-group">
                <label for="publish_date">出版日期</label>
                <input type="date" class="form-control" id="publish_date" placeholder="publish_date" name="publish_date" value="{{ book_obj.publish_date|date:'Y-m-d' }}">
              </div>
               <div class="form-group">
                <label for="publish">出版社</label>
                <input type="text" class="form-control" id="publish" placeholder="publish" name="publish" value="{{ book_obj.publish }}">
              </div>

              <button type="submit" class="btn btn-success pull-right">提交</button>
            </form>

        </div>

    </div>



</div>






</body>
</html>


多表操作

创建表代码

from django.db import models

class Author(models.Model):
    """
    作者表
    """
    name=models.CharField( max_length=32)
    age=models.IntegerField()
    # authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE)
    au=models.OneToOneField("AuthorDetail",on_delete=models.CASCADE)

    def __str__(self):
        return self.name

class AuthorDetail(models.Model):
    """
    作者详细信息表
    """
    birthday=models.DateField()
    telephone=models.CharField(max_length=11)
    addr=models.CharField(max_length=64)
    # class Meta:
        # db_table='authordetail' #指定表名
        # ordering = ['-id',]
    def __str__(self):
        return self.telephone + self.addr


class Publish(models.Model):
    """
    出版社表
    """
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    def __str__(self):
        return self.name

class Book(models.Model):
    """
    书籍表
    """
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publishs=models.ForeignKey(to="Publish",on_delete=models.CASCADE,)
    authors=models.ManyToManyField('Author',)

    def __str__(self):
        return self.title


多表增加
多表增加 有2种方式 一种是直接利用字段增加 一种是利用找到的mode对象增加
下面是 代码示例
一对一 和 一对多 方法相同


一对一 和 一对多

第一种方式 利用新生成的字段增加
models.Author.objects.create(
    name='海狗',
    age=59,
    au_id=4
)

第2种方式(与id为4的就是一对一关系)ghj
au_obj = models.AuthorDetail.objects.get(id=4)
第一种方式
models.Author.objects.create(
    name='海狗',
    age=59,
    au=au_obj
)

一对多
pub_obj = models.Publish.objects.get(id=3)
models.Book.objects.create(
    title='xx2',
    price=13,
    publishDate='2011-11-12',
    # publishs=pub_obj , #类属性作为关键字时,值为model对象
    publishs_id=3  # 如果关键字为数据库字段名称,那么值为关联数据的值
)



多对多

第一种
new_obj = models.Book.objects.create(
    title='海狗产后护理第二部',
    price=0.5,
    publishDate='2019-09-29',
    publishs_id=2,
)

new_obj.authors.add(3, 5)  # #*args  **kwargs
new_obj.authors.add(*[3, 5])  # 用的最多,


第2种
ziwen = models.Author.objects.get(id=3)
haigou = models.Author.objects.get(id=5)

new_obj = models.Book.objects.create(
    title='海狗产后护理第二部',
    price=0.5,
    publishDate='2019-09-29',
    publishs_id=2,
)

new_obj.authors.add(ziwen, haigou)


删除
一对一 一对多 删除都是找到mode对象delete()就行
多对多就比较特殊 要先找到对象 在点属性 进行修改


一对一
# models.AuthorDetail.objects.filter(id=3).delete()
一对多
models.Publish.objects.filter(id=3).delete()

# 多对多
book_obj = models.Book.objects.get(id=2)
book_obj.authors.clear()  # 清除
book_obj.authors.set(['1','5'])  # 先清除再添加,相当于修改


找到对象直接改
第一种
models.Book.objects.filter(id=5).update(
    title='华丽丽',
    publishs=ret,
    publishs_id=1,
)
第2种
ret = models.Publish.objects.get(id=2)
models.Book.objects.filter(id=5).update(
    title='华丽丽',
    publishs=ret,
)


跨表查询子查询
查询2种方式 (正向查询,反向查询)  关系属性写在表1,关联到表2,那么通过表1的数据去找表2的数据,叫做正向查询,返过来就是反向查询

正向查询用 属性
反向查询用 表名(小写的)
注意 
    多对多 反向查询用 表名_set
    只要含义多个的可能性都用all全部查询出来 在for循环 得到信息
下面是查询实列


查询实列

# 查询
# 一对一
# 关系属性写在表1,关联到表2,那么通过表1的数据去找表2的数据,叫做正向查询,返过来就是反向查询
# 查询一下王洋的电话号码

# 正向查询  对象.属性
# obj = models.Author.objects.filter(name='王洋').first()
# ph = obj.au.telephone
# print(ph)

# 查一下电话号码为120的作者姓名
# 反向查询  对象.小写的表名
# obj = models.AuthorDetail.objects.filter(telephone=120).first()
# ret = obj.author.name  #陈硕
# print(ret)

# 一对多
# 查询一下 海狗的怂逼人生这本书是哪个出版社出版的  正向查询
# obj = models.Book.objects.filter(title='海狗的怂逼人生').first()
# ret = obj.publishs.name
# print(ret)  #24期出版社
#  查询一下 24期出版社出版过哪些书
# obj = models.Publish.objects.filter(name='24期出版社').first()
#
# ret = obj.book_set.all() #<QuerySet [<Book: 母猪的产后护理>, <Book: 海狗的怂逼人生>]>
# for i in ret:
#     print(i.title)

# 多对多
# 海狗的怂逼人生 是哪些作者写的 -- 正向查询
# obj = models.Book.objects.filter(title='海狗的怂逼人生').first()
# ret = obj.authors.all()
#
# print(ret)  #<QuerySet [<Author: 王洋>, <Author: 海狗>]>
# for i in ret:
#     print(i.name)

# 查询一下海狗写了哪些书 -- 反向查询
# obj = models.Author.objects.filter(name='海狗').first()
# ret = obj.book_set.all()
# print(ret)
# for i in ret:
#     print(i.publishs.name)
#     print(i.title)
# return HttpResponse('ok')


模板渲染

模板渲染的语法?

模板渲染的官方文档 https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-for


变量相关的用{{}},逻辑相关的用{% %}。


模板渲染的取值(万能的点)?

我们可以利用 深度查询据点符(.)进行查询
<h2>{{ 变量 }}</h2>
<h2>{{ 字典.items }}</h2>items keys vlues
<h2>我是"{{ 列表.1 }}"</h2>
<h2>{{ 对象.对象方法 }}</h2>  #如果调用的方法需要传参,sorry用不了
代码示例如下



views.py视图函数中写法

from django.shortcuts import render
def home(request):
    import datetime
    current_time = datetime.datetime.now()
    name = '小白'
    num = 101
    d1 = {'xx':'oo','xx2':'oo2','xx3':[11,22,33]}
    d2 = ['11','22','33']
    d3 = []
    l1 = ['李业','陈硕','王洋','海狗']
    class Person:
        n = '类的属性'
        def p(self):
            return '类p的返回值'
    obj = Person()
    return render(request,'home.html',{'name':name,'d1':d1})


HTML页面写法

注意字典 列表 和对象中方法

<h2>{{ name }}</h2>
<h2>{{ d1.items }}</h2>items keys vlues
<h2>我是"{{ l1.1 }}"</h2>
<h2>{{ num }}</h2>
<h2>{{ obj.p }}</h2>  #如果调用的方法需要传参,sorry用不了


模板渲染用到的内置过滤器?

过滤器的语法: {{ value|filter_name:参数 }}
使用管道符"|"来应用过滤器。
注意事项:
    过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。
    过滤器可以接受参数,例如:{{ sss|truncatewords:30 }},这将显示sss的前30个词。
    过滤器参数包含空格的话,必须用引号包裹起来。比如使用逗号和空格去连接一个列表中的元素,如:{{ list|join:', ' }}
    '|'左右没有空格没有空格没有空格
参考博客:https://www.cnblogs.com/clschao/articles/10414811.html

代码示例


default,length,filesizeformat,slice-------等

default 如果一个变量是false或者为空,使用给定的默认值。 否则,使用变量的值。
{{ value|default:"nothing"}}
如果value没有传值或者值为空的话就显示nothing

length返回值的长度,作用于字符串和列表。
{{ value|length }}
返回value的长度,如 value=['a', 'b', 'c', 'd']的话,就显示4.

filesizeformat将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)。
{{ value|filesizeformat }}
如果 value 是 123456789,输出将会是 117.7 MB。

slice
切片,如果 value="hello world",还有其他可切片的数据类型
{{value|slice:"2:-1"}}

date格式化,
value=datetime.datetime.now()
{{ value|date:"Y-m-d H:i:s"}}
关于时间日期的可用的参数(除了Y,m,d等等)还有很多,有兴趣的可以去查查看看。

safe
{{ value|safe}}
如果value = "<a href='#'>点我</a>"   那么页面显示的是a标签

truncatechars如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
{{ value|truncatechars:9}} #注意:最后那三个省略号也是9个字符里面的,也就是这个9截断出来的是6个字符+3个省略号,有人会说,怎么展开啊,配合前端的点击事件就行啦

truncatewords在一定数量的字后截断字符串,是截多少个单词。
例如:‘hello girl hi baby yue ma’,
{{ value|truncatewords:3}}  #上面例子得到的结果是 'hello girl h1...'

cut移除value中所有的与给出的变量相同的字符串

{{ value|cut:' ' }}
    如果value为'i love you',那么将输出'iloveyou'.

join使用字符串连接列表,{{ list|join:', ' }},就像Python的str.join(list)
 


模板渲染中模板标签Tags(for if with 等)?

格式 如下
{% tag %}
标签 内容 ... 
{% endtag %}

forloop---模板变量
for标签----连续生成标签
for ... empty---为空在生成



for标签

生成几个p标签
{% for person in person_list %}
    <p>{{ person.name }}</p>  <!--凡是变量都要用两个大括号括起来-->
{% endfor %}


反向循环

{% for obj in list reversed %}


遍历一个字典:

{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}


forloop(模板变量)

forloop.counter            当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能
forloop.counter0           当前循环的索引值(从0开始)
forloop.revcounter         当前循环的倒序索引值(从1开始)
forloop.revcounter0        当前循环的倒序索引值(从0开始)
forloop.first              当前循环是不是第一次循环(布尔值)
forloop.last               当前循环是不是最后一次循环(布尔值)
forloop.parentloop         本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数等
实列
{% for i in d2 %}
    {% for k,v in d1.items %}

        <li>{{ forloop.counter }}-- {{ forloop.parentloop.counter }} === {{ k }} -- {{ v }}</li>

    {% endfor %}

{% endfor %}


for ... empty

for 标签带有一个可选的{% empty %} 从句,通过它我们可以定义当列表为空时的输出内容

{% for person in person_list %}
    <p>{{ person.name }}</p>
{% empty %}
    <p>你找的东西没有</p>
{% endfor %}


if 标签

 {% if %}会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。

{% if num > 100 or num < 0 %}
    <p>无效</p>  <!--不满足条件,不会生成这个标签-->
{% elif num > 80 and num < 100 %}
    <p>优秀</p>
{% else %}  <!--也是在if标签结构里面的-->
    <p>凑活吧</p>
{% endif %}

if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,注意条件两边都有空格。


with(起别名)

方法1
{% with total=business.employees.count %}  #注意等号两边不能有空格
    {{ total }} <!--只能在with语句体内用-->
{% endwith %}
方法2
{% with business.employees.count as total %}
    {{ total }}
{% endwith %}


模板继承

为什么要模板继承?

在写html页面中 发现有很多页面有雷同的样式 为了不必重复造轮子

Web框架需要一种很便利的方法用于动态生成HTML页面。 最常见的做法是使用模板。

模板包含所需HTML页面的静态部分,以及一些特殊的模版语法,用于将动态内容插入静态部分。

模板的设计实现了业务逻辑view与显示内容template的分离,一个视图可以使用任意一个模板,一个模板可以供多个视图使用。

模板继承怎么实现使用方法?

1. 创建一个base.html页面(作为母版,其他页面来继承它使用 名字随便取)
2. 在母版中定义block块(可以定义多个,整个页面任意位置)
	{% block content %}  <!-- 预留的钩子,共其他需要继承它的html,自定义自己的内容content -->
	{% endblock %}
3 其他页面继承写法
	{% extends 'base.html' %}  必须放在页面开头
4 页面中写和母版中名字相同的block块,从而来显示自定义的内容
    {% block content %}  <!-- 预留的钩子,共其他需要继承它的html,自定义自己的内容 -->
        {{ block.super }}  #这是显示继承的母版中的content这个快中的内容
        这是xx1
    {% endblock %}
以下是具体运行代码


第一步创建一个母模板

在templates文件夹 中创建一个muban.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板</title>
    <style>
        {% block css %}
        #mub{
            width: 100%;
            height: 50px;
            background-color: cornflowerblue;
        }
        {% endblock %}{#预留的钩子,共其他需要继承它的html,自定义自己的内容#}
    </style>
</head>
<body>
<div id="mub">我是模板</div>

{% block content %}  <!-- 预留的钩子,共其他需要继承它的html,自定义自己的内容 -->
    <h1>我是模板h1</h1>
{% endblock %}
{% block cs2 %}  <!-- 预留的钩子,共其他需要继承它的html,自定义自己的内容 -->
    <h1>我是测试2</h1>
{% endblock %}
</body>
</html>


第2步其他页面继承写法

在templates文件夹 中创建一个home.html 继承于muban.html

{% extends 'muban.html' %}

{% block css %}
    #mub{
        width: 100%;
        height: 50px;
        background-color: red;
    }
    #s6{
        width: 50%;
        height: 50px;
        background-color: red;
        float: right;
    }
    {% endblock %}




{% block content %}  <!-- 预留的钩子,共其他需要继承它的html,自定义自己的内容 -->
<h1>我改变了模板的内容</h1>
<div id="s6">我来测试</div>
{% endblock %}

{% block cs2 %}  <!-- 预留的钩子,共其他需要继承它的html,自定义自己的内容 -->
    {{ block.super }}
    我在测试2下面
{% endblock %}



组件

什么是组件?

组件就是类似于python中的模块 什么时候用 什么时候导入  比如常见的导航条,页尾信息等组件  我们一般 保存在单独的文件中, 组件封装了功能不可以改 模板可以修改


内置组件有那些?

分页器的使用
Form
modelForm
orm
cookie和session
中间件
信号
具体介绍 
https://www.cnblogs.com/tianshuai1/p/11042166.html


组件怎么创建 怎么使用?

1 在templates文件中创建html页面,里面写上自己封装的组件内容,xx.html
2 新的html页面使用这个组件
	{% include 'xx.html' %}#include固定写法 xx.html引用的组件名
以下是代码

注意  先加载组件后 渲染


创建组件

在templates文件夹 中创建一个组件 title.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .nav{
            background-color: pink;
            height: 40px;

        }

    </style>
</head>
<body>

<div class="nav">
    <span>个人中心</span>
    <span>首页</span>
    <span>注册</span>
    <span>登录</span>

</div>
</body>
</html>


引用组件

在templates文件夹 中创建一个home.html 引用组件 title.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>
<body>
{% include 'title.html' %}

<h1>这是新项目首页</h1>

</body>
</html>


自定义过滤器

为什么要有自定义标签和过滤器?


为什么要有自定义标签和过滤器?

因为在现实编程中可能内置的标签过滤器 不可以满足我们的需求 所有需要我们自己来创建

创建自定义标签和过滤器的流程

1 在应用下创建一个叫做templatetags的文件夹(名称不能改),在里面创建一个py文件,例如xx.py

2 在xx.py文件中引用django提供的template类,写法
	from django import template
	register = template.Library() #register变量名称不能改
	
	# 自定义标签 没有参数个数限制
    from django import template
    register = template.Library() #register变量名称不能改

    @register.filter   #参数至多两个  也就是管道后最多只有一个
    def guolv(v1,v2):
        """
        :param v1: 变量的值 管道前面的
        :param v2: 传的参数 管道后面的,如果不需要传参,就不要添加这个参数
        :return: 
        """
        return v1+v2

		使用:
		{% load xx %} xx是py文件 先在HTML页面引入
		{{ name|guolv:'oo' }} 使用过滤器 guolv是自己定义的过滤器函数名
	
下面是执行的代码


4.7.2.1在应用文件夹下 创建templatetags文件夹并在里面创建一个xx.文件

from django import template
register = template.Library() #register变量名称不能改

@register.filter   #参数至多两个
def guolv(v1,v2):
    """
    :param v1: 变量的值 管道前面的
    :param v2: 传的参数 管道后面的,如果不需要传参,就不要添加这个参数
    :return: 
    """
    return v1+v2


4.7.2.2视图views中代码

from django.shortcuts import render,HttpResponse
name='测试+'
def home(request):
    return render(request,'home.html',{'name':name})



4.7.2.3创建要引用自定义过滤器的html页面

{% load xx %}{#xx是templatetags文件夹下的xx.py文件#}
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
{{ name|guolv:'要传给n2的参数' }}

</body>
</html>



inclusion_tag

为什么要用inclusion_tag

inclusion_tag在使用的时候可以帮我们减少很多前端和后端重复的代码


inclusion_tag原理怎么使用 场景?

多用于返回html代码片段
原理先运行nue.html 调用xx.py 中res函数
res函数将返回值给@register.inclusion_tag对应的页面('result.html') 模板渲染后 以组件的形式给原来的nue.htm


**小列题 **

利用组件 利用后端传来的数据['c1','c2','c3']
要求组件 后端不可以修改 利用 inclusion_tag 来实现

实现流程
templatetags文件夹名 应用下创建 固定写法



home.html

{% load xx %}#xx是templatetags文件夹中自己定义的xx.py文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

{% inclusion li %}
#inclusion是xx.py文件中自己定义的函数
#li是参数 后端传过来的 列如传来的是['c1','c2','c3']

</body>

</html>



zujian.html组键

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .a{
            width: 40px;
            height: 40px;
        }
    </style>
</head>
<body>

<ul>
    {% for i in li %}
        <li class="a" >{{ i }}</li>
    {% endfor %}
</ul>
</body>

</html>



xx.py

# inclusion_tag 返回html片段的标签
from django import template

register = template.Library()


@register.inclusion_tag('zujian.html')  # 括号是组件
def inclusion(li):  # li 是后端传过来的['c1','c2','c3']
    l2 = []
    for i in li:
        l2.append(i + 'a')
    return {'li': l2}#返回一定是字典 键名要与组件有联系



静态文件相关

为什么要有静态文件 怎么配置使用?

js、css、img等都叫做静态文件,那么关于django中静态文件的配置,我们就需要在settings配置文件里面写上这写内容:

目录:别名也是一种安全机制,浏览器上通过调试台你能够看到的是别名的名字,这样别人就不能知道你静态文件夹的名字了,不然别人就能通过这个文件夹路径进行攻击。


1 项目目录下创建一个文件夹,例如名为 static_file,将所有静态文件放到这个文件夹中
2 settings 配置文件中进行下面的配置
	# 静态文件相关配置
    STATIC_URL = '/static/'  #静态文件路径别名

    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static_file'),第2个参数创建的文件夹名
    ]
    
    
3 html中引入
引入<link rel="stylesheet" href="/abc/css/index.css">


第2种
1 项目目录下创建一个文件夹,例如名为 static_file,将所有静态文件放到这个文件夹中
2 settings 配置文件中进行下面的配置
	# 静态文件相关配置
    STATIC_URL = '/static/'  #静态文件路径别名

    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static_file'),第2个参数创建的文件夹名
    ]
    
html文件开头加上
{% load static %} 固定写法

<link rel="stylesheet" href="{% static 'sw/sweetalert.css' %}">  static是固定写法


第一项目目录下创建一个文件夹,例如名为static_file,将所有静态文件放到这个文件夹中

1569493728264

第2找到settings 配置文件中进行下面的配置

STATIC_URL = '/abc/'  #静态文件路径别名 可以随便改
   #注意第2个参数一定对应创建的文件夹名 别名可以随便改
   STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static_file'),
   ]



第3引入的配置文件的html页面内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/abc/css/index.css">
    <link rel="stylesheet" href="/abc/plugins/bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>

<input type="text" class="form-control">
<h1>红浪漫spa会所</h1>
<img src="/abc/img/timg.jpg" alt="">
<a href="{% url 'home' %}">首页</a>

技师:
<ul>
    <li>小白</li>
    <li>小黑</li>
</ul>

</body>
</html>



别名和反向解析

为什么要有别名 怎么使用?


我们给url 起一个别名· 以后不管url怎么改 都可以实现原来的网址 就不会写死了

rurl文件写法
	url(r'^index2/', views.index,name='cs'),
反向解析
	后端views:
         from django.urls import reverse 可以实现反向解析
		 reverse('别名')  例如:reverse('cs') 反向解析为别名对应的地址 /index2/
         带参数的反向解析: reverse( 'index' ,args=(10,))--- /index2/10/
	html: {% url '别名' %} -- 例如:{% url 'cs' %} -- /index2/
下面是代码


实列
使其重定向至index别名对应的地址  /index2/10/
return redirect(everse( 'index' ,args=(10,)))



rurl文件写法

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^cs/', views.home,name='cs'),
    url(r'^cs1/', views.cs1,name='cs1'),
]



后端views.py

用于重定向 redirect
我们利用别名 以后不管 别名cs1 对应的网址这么变  我们通过反解析 ,都可以找到他,写活了他 
from django.shortcuts import render,HttpResponse,redirect
from django.urls import reverse

def home(request):
    return redirect(reverse('cs1'))
def cs1(request):
    return HttpResponse('测试')



url路由分发

各个应用间怎么进行路由分发?

创建应用python manage.py startapp 子应用名称

在每app下传建urls文件导入以下配置

from django.conf.urls import url
from django.contrib import admin

from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
]


主项目如下 配置 先导入include

from django.conf.urls import url,include
from django.contrib import admin

urlpatterns = [
    url(r'^app01/', include('app01.urls',namespace='app01')),
    url(r'^app02/', include('app02.urls',namespace='app02')),

]




url命名空间

为什么要有url命名空间 怎么使用?

在路由分发中可能会错乱为了 避免 设置命名空间名称namespace

from django.conf.urls import url,include
from django.contrib import admin
urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls',namespace='app01')),
    url(r'^app02/', include('app02.urls',namespace='app02')),
	
]


使用:
	后端:reverse('命名空间名称:别名') -- reverse('app01:home') 
	hmtl:{% url '命名空间名称:别名' %}  -- {% url 'app01:home' %}



ajax

ajax是什么?

AJAX是一种客户端Web开发技术
特点是异步请求,局部刷新;这一特点给用户的感受是在不知不觉中完成请求和响应过程


ajax怎么用?

先引入jquery
第一步绑定事件
第2步利用jquery获取用户内容
第3步 利用ajax 义字典形式数据提交到url对应的地址 进行路由匹配,执行相应的视图函数 返会逻辑结果
第4步ajax中success键对应的匿名函数 形参res接受到,之前视图函数的返回值 进行匿名函数逻辑判断 比如利用js添加内容等等


	{% csrf_token %}---必加安全验证
    用户名: <input type="text" id="username">
    密码: <input type="password" id="password">
    <button id="sub">提交</button>
    <span class="error"></span>

<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>    
    
   $('#sub').click(function () {

        var uname = $('#username').val();
        var pwd = $('#password').val();
        var csrf = $('[name=csrfmiddlewaretoken]').val(); ---固定写法获得随机值

        $.ajax({
            url:'{% url "login" %}',
            type:'post',
            data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端


            success:function (res) {---ret是后端给的
                if (res === '1'){
                    location.href = '/home/'; // 重定向http://127.0.0.1:8000/home/

                }else{
                    $('.error').text('用户名密码错误!');
                }

            }

        })

    })


AJAX常见应用情景

搜索引擎根据用户输入的关键字,自动提示检索关键字。
    还有一个很重要的应用场景就是注册时候的用户名的查重。



安全认证机制CSRF

安全认证机制CSRF是什么?
django中的安全认证机制CSRF?
如何解决django中CSRF?


安全认证机制CSRF是什么?

详述CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。


django中的安全认证机制CSRF?

django项目中默认对POST请求进行了csrf认证,只需要在模板提交数据的代码块中加入模板标签{{% vsrf_token %}}即可,(不需要注销seetings.py配置文件MIDDLEWARE列表中的'django.middleware.csrf.CsrfViewMiddleware',),模板页面在渲染时会自动在相应位置渲染成隐藏的input标签:<input type="hidden" name="csrfmiddlewaretoken" value="8J4z1wiUEXt0gJSN59dLMnktrXFW0hv7m4d40Mtl37D7vJZfrxLir9L3jSTDjtG8">每次都是随机的

表现情况
在之前我们提交数据必须`注了配置项MIDDLEWARE里面的django.middleware.csrf.CsrfViewMiddleware',否则403`为什么要有这个 安全认证机制csrftoken


如何解决django中CSRF?

两种情况
当form表单提交数据时
当ajax提交数据时
代码详解如下


当form表单提交数据时

form表单里面要有这个就行
<form action="" method="post">
    {% csrf_token %}
    用户名: <input type="text" name="username">
    密码: <input type="password" name="password">
    <input type="submit">
</form>


当ajax提交数据时

1570709516630

{% csrf_token %}会生成隐藏的input标签 name是csrfmiddlewaretoken固定的values值是随机的  为什么要这个input标签  官方源码解释
官方文档中说到,检验token时,只比较secret是否和 cookie中的secret值一样,而不是比较整个token。
token字符串的前32位是 salt(盐), 后面是加密后的 token(令牌), 通过salt能解密出唯一的secret。
django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法。
同样也不难解释,为什么ajax请求时,需要从cookie中拿取token添加到请求头中。

第一种方式
	{% csrf_token %}---必加
    用户名: <input type="text" id="username">
    密码: <input type="password" id="password">
    <button id="sub">提交</button>
    <span class="error"></span>

<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>    
    
   $('#sub').click(function () {

        var uname = $('#username').val();
        var pwd = $('#password').val();
        var csrf = $('[name=csrfmiddlewaretoken]').val(); ---固定写法获得随机值

        $.ajax({
            url:'{% url "login" %}',
            type:'post',
            data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端


            success:function (res) {---ret是后端给的
                if (res === '1'){
                    location.href = '/home/'; // http://127.0.0.1:8000/home/

                }else{
                    $('.error').text('用户名密码错误!');
                }

            }

        })

    })

第2种方式

	{% csrf_token %}---必加
    用户名: <input type="text" id="username">
    密码: <input type="password" id="password">
    <button id="sub">提交</button>
    <span class="error"></span>

<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>    
    
   $('#sub').click(function () {

        var uname = $('#username').val();
        var pwd = $('#password').val();
        var csrf = '{{ csrf_token }}'; ---第2种方式固定写法 csrf_token会获得csrf_token中键随机值

        $.ajax({
            url:'{% url "login" %}',
            type:'post',
            data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端


            success:function (res) {---ret是后端给的
                if (res === '1'){
                    location.href = '/home/'; // http://127.0.0.1:8000/home/

                }else{
                    $('.error').text('用户名密码错误!');
                }

            }

        })

    })

第3种
   注意:需要引入一个jquery.cookie.js插件。
<script src="{% static 'js/jquery.cookie.js' %}"></script>

	{% csrf_token %}---必加
    用户名: <input type="text" id="username">
    密码: <input type="password" id="password">
    <button id="sub">提交</button>
    <span class="error"></span>

<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>    
    
   $('#sub').click(function () {

        var uname = $('#username').val();
        var pwd = $('#password').val();
        var csrf = '{{ csrf_token }}'; ---第2种方式固定写法 csrf_token会获得csrf_token中键随机值

        $.ajax({
            url:'{% url "login" %}',
            type:'post',
            data:{username:uname,password:pwd},
			headers:{"X-CSRFToken":$.cookie('csrftoken')}--固定写法值是cookie
            success:function (res) {---ret是后端给的
                if (res === '1'){
                    location.href = '/home/'; // http://127.0.0.1:8000/home/

                }else{
                    $('.error').text('用户名密码错误!');
                }

            }

        })

    })


django文件上传

form表单上传文件
ajax上传文件 


form表单上传文件

理论
form表单提交的文件在request.FILES里面 如果不改消息格式则request.FILES里面为空 
修改消息格式 在form表单中加入 enctype="multipart/form-data"
获得文件对象(句柄)file_obj = request.FILES.get('file') 括号里面的file就是 后端<input type="file" name="file">标签对应的name值
获得文件名称 就用文件对象.name 及就是 file_obj.name
有了文件对象(句柄) 和原文件名字就可以写入文件了

步骤
第1步 修改消息格式 在form表单中加入 enctype="multipart/form-data"
第2步 后端代码获得文件对象 与名字就可以写人文件了

实现代码如下


前端代码

前端代码
<form action="" method="post" enctype="multipart/form-data">--到定义
    {% csrf_token %}
    用户名: <input type="text" name="username">
    密码: <input type="password" name="password">
    上传文件: <input type="file" name="file"> 

    <input type="submit">
    
</form>


后端代码
def upload(request):

    if request.method == 'GET':
        print(settings.BASE_DIR) #/static/

        return render(request,'upload.html')

    else:
        #print(request.POST)--获取的是对象 里面有文件名
        #print(request.FILES) --对象 文件对象 存在内存中 如果不改消息格式里面没东西
        uname = request.POST.get('username')
        pwd = request.POST.get('password')

        file_obj = request.FILES.get('file')---获取文件句柄对象

        print(file_obj.name) #开班典礼.pptx
        with open(file_obj.name,'wb') as f:
            # for i in file_obj: --怕文件中没有换行 会爆内存
            #     f.write(i)
            for chunk in file_obj.chunks():---一般用这种一次读取65536个字节
                f.write(chunk)
        return HttpResponse('ok')
    
扩展内容 
怎么修改chunks()默认读取子节?
配置文件修改chunks()


from django.conf import settings与from 项目名 import settings的区别?
from django.conf import settings是内置的另一个配置文件,外层settings没有的内层里面可能有
from 项目名 import settings是当前项目外层的配置文件 外层有的就覆盖了内层的
用法上from django.conf import settings更厉害 外层配配置文件没有的可能能在内层配置找到


ajax上传文件

第1步绑定点击事件 
第2步 点击 触发事件 执行匿名函数 函数里面逻辑利用js获得想要的结果
	var file = $('[type=file]')[0].files[0];--获取上传对象
第3步 创建FormData对象  借助他上传
第4步 将匿名函数或的的想要的结果append 键值对的形式加进去
第5步 ajax上传加入固定写法  叫django不要处理代码示例
    processData:false,--固定写法 叫django不要处理
    contentType:false,--固定写法 叫django不要处理

后端代码不变


多加一个参数multiple后端也要改

     {% csrf_token %}
    用户名: <input type="text" name="username"> 
    密码: <input type="password" name="password">
    上传文件: <input type="file" name="file" > 

    <input type="submit">
    
</form>
    $('#sub').click(function () {

        var formdata = new FormData();---创建FormData对象借助他上传
        var uname = $('#username').val();
        var pwd = $('#password').val();
        var file = $('[type=file]')[0].files[0];--获取对象

        formdata.append('username',uname); 封装到对象里面
        formdata.append('password',pwd);
        formdata.append('file',file);
        
    $.ajax({
                url:'{% url "upload" %}',
                type:'post',
                data:formdata,---发这个
                processData:false,--固定写法 叫django不要处理
                contentType:false,--固定写法 叫django不要处理
        
        success:function (res) {---ret是后端给的
                if (res === '1'){
                    location.href = '/home/'; // 重定向http://127.0.0.1:8000/home/

                }else{
                    $('.error').text('用户名密码错误!');
                }

            }

        })

    })
        
        
后端
def upload(request):

    if request.method == 'GET':
        print(settings.BASE_DIR) #/static/

        return render(request,'upload.html')

    else:
        #print(request.POST)--获取的是对象 里面有文件名
        #print(request.FILES) --对象 文件对象 存在内存中 如果不改消息格式里面没东西
        uname = request.POST.get('username')
        pwd = request.POST.get('password')

        file_obj = request.FILES.get('file')---获取文件句柄对象

        print(file_obj.name) #开班典礼.pptx
        with open(file_obj.name,'wb') as f:
            # for i in file_obj: --怕文件中没有换行 会爆内存
            #     f.write(i)
            for chunk in file_obj.chunks():---一般用这种一次读取65536个字节
                f.write(chunk)
        return HttpResponse('ok')
        


传输json字典数据

一般我们后端返回给前端的数据都是字典形式 所有要用到JSON 转化为特殊格式,便于解析回去
3种方式
第一种方式利用 json模块  low逼写法 后端要反序列化
import json
et_data_json = json.dumps(要转化的字典dic,ensure_ascii=False)
var res = JSON.parse(res);  -- 反序列化变成js自定义对象
第2种方式 高大尚写法
import json
ret_data_json = json.dumps(要转化的字典dic,ensure_ascii=False)
return HttpResponse(ret_data_json,content_type='application/json')
会自动转化为json数据 前端就不要反序列化了
第3种写法  最终版
from django.http import JsonResponse
return JsonResponse(字典)


--------------第一种
html页面
	{% csrf_token %}---必加
    用户名: <input type="text" id="username">
    密码: <input type="password" id="password">
    <button id="sub">提交</button>
    <span class="error"></span>

<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>    
    
   $('#sub').click(function () {

        var uname = $('#username').val();
        var pwd = $('#password').val();
        var csrf = $('[name=csrfmiddlewaretoken]').val(); ---固定写法获得随机值

        $.ajax({
            url:'{% url "login" %}',
            type:'post',
            data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端
    		
            success:function (res) {
                var res = JSON.parse(res);  -- 反序列化变成js自定义对象
                if (res.status === 1000){
                    // $('.error').text('登录成功');
                    location.href = '/home/'; // http://127.0.0.1:8000/home/

                }else{
                    $('.error').text(res.msg);
                }

            }

        })

    })
            
            
后端代码
def jsontest(request):
    if request.method == 'GET':
        return render(request,'jsontest.html')
    else:
        username = request.POST.get('username')
        pwd = request.POST.get('password')
        ret_data = {'status':None,'msg':None}

        if username == 'chao' and pwd == '123':
            ret_data['status'] = 1000  # 假设1000状态码登录成功自己设置的
            ret_data['msg'] = '登录成功'
        else:
            ret_data['status'] = 1001  # 假设1001状态码登录失败自己设置的
            ret_data['msg'] = '登录失败'
		et_data_json = json.dumps(ret_data,ensure_ascii=False)
        return HttpResponse(et_data_json)
    
    
    
--------------第2种方式
后端代码  


def jsontest(request):
    if request.method == 'GET':
        return render(request,'jsontest.html')
    else:
        username = request.POST.get('username')
        pwd = request.POST.get('password')
        ret_data = {'status':None,'msg':None}

        if username == 'chao' and pwd == '123':
            ret_data['status'] = 1000  # 假设1000状态码登录成功自己设置的
            ret_data['msg'] = '登录成功'
        else:
            ret_data['status'] = 1001  # 假设1001状态码登录失败自己设置的
            ret_data['msg'] = '登录失败'
            
        return HttpResponse(ret_data_json,content_type='application/json')
会自动转化为json数据 前端就不要反序列化了

html页面
	{% csrf_token %}---必加
    用户名: <input type="text" id="username">
    密码: <input type="password" id="password">
    <button id="sub">提交</button>
    <span class="error"></span>

<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>    
    
   $('#sub').click(function () {

        var uname = $('#username').val();
        var pwd = $('#password').val();
        var csrf = $('[name=csrfmiddlewaretoken]').val(); ---固定写法获得随机值

        $.ajax({
            url:'{% url "login" %}',
            type:'post',
            data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端
    		
            success:function (res) {
 //不要这个了     var res = JSON.parse(res);  -- 反序列化变成js自定义对象
                if (res.status === 1000){
                    // $('.error').text('登录成功');
                    location.href = '/home/'; // http://127.0.0.1:8000/home/

                }else{
                    $('.error').text(res.msg);
                }

            }

        })

    })


第3种方式
from django.http import JsonResponse
后端代码  

def jsontest(request):
    if request.method == 'GET':
        return render(request,'jsontest.html')
    else:
        username = request.POST.get('username')
        pwd = request.POST.get('password')
        ret_data = {'status':None,'msg':None}

        if username == 'chao' and pwd == '123':
            ret_data['status'] = 1000  # 假设1000状态码登录成功自己设置的
            ret_data['msg'] = '登录成功'
        else:
            ret_data['status'] = 1001  # 假设1001状态码登录失败自己设置的
            ret_data['msg'] = '登录失败'
            
        return JsonResponse(ret_data)
会自动转化为json数据 前端就不要反序列化了

html页面
	{% csrf_token %}---必加
    用户名: <input type="text" id="username">
    密码: <input type="password" id="password">
    <button id="sub">提交</button>
    <span class="error"></span>

<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'js/jquery.cookie.js' %}"></script>    
    
   $('#sub').click(function () {

        var uname = $('#username').val();
        var pwd = $('#password').val();
        var csrf = $('[name=csrfmiddlewaretoken]').val(); ---固定写法获得随机值

        $.ajax({
            url:'{% url "login" %}',
            type:'post',
            data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},--发送给后端
    		
            success:function (res) {
 //不要这个了     var res = JSON.parse(res);  -- 反序列化变成js自定义对象
                if (res.status === 1000){
                    // $('.error').text('登录成功');
                    location.href = '/home/'; // http://127.0.0.1:8000/home/

                }else{
                    $('.error').text(res.msg);
                }

            }

        })

    })


form组件

1 生成html标签
2 保留原来的数据
3 校验用户提交的数据
如何给生成的标签加样式


利用Form自动生成HTML标签

第1步 在views.py文件中 导入from django import forms模块 定义一个类 继承与forms.Form  名字随意  继承forms.Form
第2步 修改forms.后面的  变更不同属性
第3步 修改forms.widgets.后面的 变更不同标签
第4步 实列化这个类 传给后端
第5步 写后端代码 
	{{对象.字段.label }}就是自定义名字 
    {{ 对象.字段 }} 就会生成标签
    {{ 对象.字段.errors.0 }}就会生成错误信息 第1个


from django import forms    
#生成用户名输入框 name对应username vluse对应用户输入的值
class UserInfo(forms.Form):
	 username=forms.CharField(
        label='用户名:',
        initial="张三",#默认值
        widget=forms.widgets.TextInput,
)
#生成密码输入框 name对应password vluse对应用户输入的值
    password=forms.CharField(
        label='密码:',
        widget=forms.widgets.PasswordInput(attrs={'class':'c1'}),---attrs里面可以添加属性
    )
#生成单选框 默认下拉框 name对应 sex vluse对应用户选择的值
    sex = forms.ChoiceField(
        choices=((1,'女'),(2,'男'),),#元祖套列表或元祖套元祖
        # widget=forms.RadioSelect,----修改成这样变成小圆点选择框
        # widget=forms.widgets.Select,---这是下拉框 默认就是
    )
#生成多选框 默认下拉框 name对应 hobby vluse对应用户选择的值
    hobby = forms.MultipleChoiceField(
        choices= ((1,'喝酒'),(2,'抽烟'),(3,'烫头')),
        # widget=forms.SelectMultiple,---这是下拉框 默认就是
        widget=forms.CheckboxSelectMultiple,---修改成小方块选择框
    )
#生成 选中框 name对应 remember_me 
    remember_me = forms.ChoiceField(
        label='记住我',

        widget=forms.CheckboxInput,
    )
#生成 日期选择框 name对应 bday 
    bday = forms.DateField(
        label='出生日期',
        # 日期类型必须加上日期属性
        widget=forms.DateInput(attrs={'type':'date'}),  


    )
    
def index(request):

    if request.method == 'GET':
        u_obj = UserInfo()#实列化对象
        return render(request,'index.html',{'u_obj':u_obj})

    else:

        u_obj = UserInfo(request.POST)  #用户提交的数据给类 进行实例化 
        if u_obj.is_valid():#效验用户数据
            # {'username': 'a范德萨水电费', 'password': '1111'}
            print('正确数据',u_obj.cleaned_data)  #校验成功之后的数据cleaned_data

            return HttpResponse('ok')
        else:
            # print('错误信息',u_obj.errors)

            return render(request,'index.html',{'u_obj':u_obj})



    
前端模板写法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .c1{
            background-color: red;
        }
    </style>  
</head>
<body>

<h1>某某登录页面</h1>
{#{{ u_obj.errors }}#}全部报错信息

<form action="" method="post" novalidate>
    {% csrf_token %} 安全验证
{#    用户名: <input type="text" name="username">#}
{#    密码: <input type="password" name="password">#}

        {{ u_obj.username.label }} {{ u_obj.username }} {{ u_obj.username.errors.0 }}
    </div>
   <div>
        {{ u_obj.password.label }} {{ u_obj.password }} {{ u_obj.password.errors.0 }}
   </div>
    <div>
        {{ u_obj.r_password.label }} {{ u_obj.r_password }} {{ u_obj.r_password.errors.0 }}
   </div>


    <input type="submit">

</form>


利用form保留原来的数据

默认自动保留
password不会自动保留数据在widget里面添加render_value=True就可以强制留下
    r_password = forms.CharField(
        label='确认密码:',
        widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True),

    )
render_value=True强制留



利用form校验用户提交的数据

简单效验用户数据

u_obj.password.errors.0 获得第一个错误数据

常见属性 
    min_length设置用户输入长度最小值
    max_length设置用户输入长度最大值
    required=True,  #不允许输入为空,值为False就可以为空
    
自定义错误信息后就不会显示英文的了
error_messages={
            'required':'不能为空',
            'min_length':'太短了!',
            'max_length':'太长了!',
        },

注意浏览器会自动帮你效验 要想浏览器不效验 在前端代码设置(novalidate)提示浏览器不要多管闲事
 <form action="" method="post" novalidate>



下面的代码实列

    username=forms.CharField(
        label='用户名:',
        # initial='张三'  #默认值
        # min_length=6, #最小值为6位
        # required=True,  #不允许为空,值为False就可以为空
        # widget=forms.widgets.TextInput,
        # max_length=8,

        error_messages={
            'required':'不能为空',
            'min_length':'太短了!',
            'max_length':'太长了!',
        },
        validators=[RegexValidator(r'^a','必须以a开头!'),RegexValidator(r'b$','必须以b结尾!')]
        # validators=[mobile_validate,]  #a范德萨水电费

    )
    


正则校验器RegexValidator验证器

第一步引入模块from django.core.validators import RegexValidator
第2步定义规则  可以定义多个规则
validators=[RegexValidator(r'^a','必须以a开头!'),RegexValidator(r'b$','必须以b结尾!')] 第一个参数是正则 第2个参数是错误信息


下面是代码实列

    username=forms.CharField(
        label='用户名:',
        initial='张三'  #默认值
        min_length=6, #最小值为6位
        required=True,  #不允许为空,值为False就可以为空
        widget=forms.widgets.TextInput,
        max_length=8,

        error_messages={
            'required':'不能为空',
            'min_length':'太短了!',
            'max_length':'太长了!',
        },
        validators=[RegexValidator(r'^a','必须以a开头!'),RegexValidator(r'b$','必须以b结尾!')]


自定义验证规则

当正则效验 与基础效验 满足不了 我们就需要  自定义验证规则了( ) 
第1步引入模块
    from django.core.exceptions import ValidationError
    import re
第2步定义一个函数 
    函数里面value是形参 传要验证的字符串
    mobile_re是利用re模块编译出来的正则规则
    利用ValidationError抛出错误
    
 def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value): 
        raise ValidationError('手机号码格式错误')
第3步 在类里面 验证类里面加入·
	validators=[定义的函数名,]


代码实列

from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError

import re
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value): 
        raise ValidationError('手机号码格式错误') 
        
class UserInfo(forms.Form):

    username=forms.CharField(
        label='用户名:',
        # initial='张三'  #默认值
        # min_length=6, #最小值为6位
        # required=True,  #不允许为空,值为False就可以为空
        # widget=forms.widgets.TextInput,
        # max_length=8,

        error_messages={
            'required':'不能为空',
            'min_length':'太短了!',
            'max_length':'太长了!',
        },
        validators=[RegexValidator(r'^a','必须以a开头!'),RegexValidator(r'b$','必须以b结尾!')]
        validators=[mobile_validate,]  #a范德萨水电费

    )

    password=forms.CharField(
        label='密码:',
        widget=forms.widgets.PasswordInput(attrs={'class':'c1'},render_value=True),

    )

    r_password = forms.CharField(
        label='确认密码:',
        widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True),

    )

    # 局部钩子
    def clean_username(self):
        value = self.cleaned_data.get('username')
        if '666' in value:
            raise ValidationError('光喊666是不行的!')
        else:
            return value

    # 全局钩子
    def clean(self):
        password = self.cleaned_data.get('password')
        r_password = self.cleaned_data.get('r_password')

        if password == r_password:
            return self.cleaned_data
        else:
            # raise ValidationError('两次密码不一致!!!!')
            self.add_error('r_password','两次密码不一致~~~~') # 给单独的某个字段加错误信息



def index(request):
    if request.method == 'GET':
        u_obj = UserInfo()
        return render(request,'index.html',{'u_obj':u_obj})
    else:
效验
        u_obj = UserInfo(request.POST)  #{'username':'','password':'123'}
        print(u_obj.fields)
        # u_obj.is_valid()  #校验用户提交的数据,全部校验成功返回True,任意一个失败都返回False
        if u_obj.is_valid(): 
            # {'username': 'a范德萨水电费', 'password': '1111'}
            print('正确数据',u_obj.cleaned_data)  #校验成功之后的数据cleaned_data

            return HttpResponse('ok')
        else:
            # print('错误信息',u_obj.errors)

            return render(request,'index.html',{'u_obj':u_obj})






Hook钩子方法

局部钩子

第1步 我们在Fom类中定义 clean_字段() 方法,就能够实现对特定字段进行校验。
注意事项
	效验顺序是先效验字段里面的效验规则 然后在效验局部钩子 最后下一字段
	
实列写法(cleaned_data是效验以后得到的数据)
def clean_username(self):
        value = self.cleaned_data.get('username')
        if '666' in value:
            raise ValidationError('光喊666是不行的!')--错误信息
        else:
            return value--一定要返回不然会吧username对应的value剔除
钩子是写在类里面


代码实列

from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError

import re
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value): 
        raise ValidationError('手机号码格式错误') 
        
class UserInfo(forms.Form):

    username=forms.CharField(
        label='用户名:',
        # initial='张三'  #默认值
        # min_length=6, #最小值为6位
        # required=True,  #不允许为空,值为False就可以为空
        # widget=forms.widgets.TextInput,
        # max_length=8,

        error_messages={
            'required':'不能为空',
            'min_length':'太短了!',
            'max_length':'太长了!',
        },
        validators=[RegexValidator(r'^a','必须以a开头!'),RegexValidator(r'b$','必须以b结尾!')]
        validators=[mobile_validate,]  #a范德萨水电费

    )

    password=forms.CharField(
        label='密码:',
        widget=forms.widgets.PasswordInput(attrs={'class':'c1'},render_value=True),

    )

    r_password = forms.CharField(
        label='确认密码:',
        widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True),

    )

    # 局部钩子
    def clean_username(self):
        value = self.cleaned_data.get('username')
        if '666' in value:
            raise ValidationError('光喊666是不行的!')
        else:
            return value

    # 全局钩子
    def clean(self):
        password = self.cleaned_data.get('password')
        r_password = self.cleaned_data.get('r_password')

        if password == r_password:
            return self.cleaned_data
        else:
            # raise ValidationError('两次密码不一致!!!!')
            self.add_error('r_password','两次密码不一致~~~~') # 给单独的某个字段加错误信息



def index(request):
    if request.method == 'GET':
        u_obj = UserInfo()
        return render(request,'index.html',{'u_obj':u_obj})
    else:
效验
        u_obj = UserInfo(request.POST)  #{'username':'','password':'123'}
        print(u_obj.fields)
        # u_obj.is_valid()  #校验用户提交的数据,全部校验成功返回True,任意一个失败都返回False
        if u_obj.is_valid(): 
            # {'username': 'a范德萨水电费', 'password': '1111'}
            print('正确数据',u_obj.cleaned_data)  #校验成功之后的数据cleaned_data

            return HttpResponse('ok')
        else:
            # print('错误信息',u_obj.errors)

            return render(request,'index.html',{'u_obj':u_obj})






全局钩子

句部钩子全部指行完 在执行全局  比如多个字段对比效验, 比如确认密码
函数名固定写法 
def clean(self):
    password = self.cleaned_data.get('password')
    r_password = self.cleaned_data.get('r_password')

    if password == r_password:#效验成功了L
        return self.cleaned_data---固定写法 cleaned_data源码里有
    else:
        # raise ValidationError('两次密码不一致!!!!')不用因为给全局了
        self.add_error('r_password','两次密码不一致~~~~') # 给单独的某个字段加错误信息


代码实列

from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError

import re
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value): 
        raise ValidationError('手机号码格式错误') 
        
class UserInfo(forms.Form):

    username=forms.CharField(
        label='用户名:',
        # initial='张三'  #默认值
        # min_length=6, #最小值为6位
        # required=True,  #不允许为空,值为False就可以为空
        # widget=forms.widgets.TextInput,
        # max_length=8,

        error_messages={
            'required':'不能为空',
            'min_length':'太短了!',
            'max_length':'太长了!',
        },
        validators=[RegexValidator(r'^a','必须以a开头!'),RegexValidator(r'b$','必须以b结尾!')]
        validators=[mobile_validate,]  #a范德萨水电费

    )

    password=forms.CharField(
        label='密码:',
        widget=forms.widgets.PasswordInput(attrs={'class':'c1'},render_value=True),

    )

    r_password = forms.CharField(
        label='确认密码:',
        widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True),

    )

    # 局部钩子
    def clean_username(self):
        value = self.cleaned_data.get('username')
        if '666' in value:
            raise ValidationError('光喊666是不行的!')
        else:
            return value

    # 全局钩子
    def clean(self):
        password = self.cleaned_data.get('password')
        r_password = self.cleaned_data.get('r_password')

        if password == r_password:
            return self.cleaned_data
        else:
            # raise ValidationError('两次密码不一致!!!!')
            self.add_error('r_password','两次密码不一致~~~~') # 给单独的某个字段加错误信息



def index(request):
    if request.method == 'GET':
        u_obj = UserInfo()
        return render(request,'index.html',{'u_obj':u_obj})
    else:
效验
        u_obj = UserInfo(request.POST)  #{'username':'','password':'123'}
        print(u_obj.fields)
        # u_obj.is_valid()  #校验用户提交的数据,全部校验成功返回True,任意一个失败都返回False
        if u_obj.is_valid(): 
            # {'username': 'a范德萨水电费', 'password': '1111'}
            print('正确数据',u_obj.cleaned_data)  #校验成功之后的数据cleaned_data

            return HttpResponse('ok')
        else:
            # print('错误信息',u_obj.errors)

            return render(request,'index.html',{'u_obj':u_obj})


利用from实现页面

   # 批量添加属性样式
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        for field_name,field in self.fields.items(): #orderdict(('username',charfield对象))

            field.widget.attrs.update({'class':'form-control'})



modelform组件

class BookModelForm(forms.ModelForm):
    # 优先级高
    # title = forms.CharField(
    #     label='书名2',
    # )

    class Meta:
        model = models.Book
        #fields='__all__'
	    fields=['title','publishs']	
        labels={
            'title':'书名',
            'price':'价格',
            'publishDate':'出版日期',
            'publishs':'出版社',
            'authors':'作者',
        }

        widgets={
            'publishDate':forms.TextInput(attrs={'type':'date'}),
            # 'publishDate2':forms.TextInput(attrs={'type':'date'}),
        }

        error_messages={
            'title':{'required':'书名不能为空!',},
            'price':{'required':'不能为空!',},
            'publishDate':{'required':'不能为空!',},
            'publishs':{'required':'不能为空!',},
            'authors':{'required':'不能为空!',},
        }


    #
    # def clean_title(self):
    #     pass
    # def clean(self):
    #     pass

    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        for field_name,field in self.fields.items():
            field.widget.attrs.update({'class':'form-control'})


为什么要有modelform组件

 举个例子,你也许会有个Book 模型,并且你还想创建一个form表单用来添加和编辑书籍信息到这个模型中。 在这种情况下,在form表单中定义字段将是冗余的,因为我们已经在模型中定义了那些字段。
  基于这个原因,Django 提供一个辅助类来让我们可以从Django 的模型创建Form,这就是ModelForm。
form与model的终极结合,会根据你model中的字段转换成对应的form字段,并且并你生成标签等操作。


modelform组件怎么用

第一步定义一个类 名字随便取 最好有含义 列入BookModelForm
类里面定义
    class Meta:
            model = models.Book
            #fields='__all__' 拿到所有字段 生成from字段




class BookModelForm(forms.ModelForm):#类目随便取
    # 优先级高
    # title = forms.CharField(
    #     label='书名2',
    # )

    class Meta:
        model = models.Book#找到book表
        fields='__all__' #拿到所有字段 生成from字段
	    #fields=['title','publishs']	
        labels={
            'title':'书名',
            'price':'价格',
            'publishDate':'出版日期',
            'publishs':'出版社',
            'authors':'作者',
        }

        
        
        widgets={
            'publishDate':forms.TextInput(attrs={'type':'date'}),
            # 'publishDate2':forms.TextInput(attrs={'type':'date'}),
        }

        error_messages={
            'title':{'required':'书名不能为空!',},
            'price':{'required':'不能为空!',},
            'publishDate':{'required':'不能为空!',},
            'publishs':{'required':'不能为空!',},
            'authors':{'required':'不能为空!',},
        }


    #
    # def clean_title(self): 钩子
    #     pass
    # def clean(self):
    #     pass

    def __init__(self,*args,**kwargs):#还是和from一样给所有字段添加样式
        super().__init__(*args,**kwargs)
        for field_name,field in self.fields.items():
            field.widget.attrs.update({'class':'form-control'})


admin

admin添加用户

python manage.py createsuperuser
输入用户名:wuchao
邮箱不用输 直接回车
输入密码:必须超过8位,并且别太简单

admin注册

from django.contrib import admin

# Register your models here.

from app01 import models

admin.site.register(models.Author)
admin.site.register(models.AuthorDetail)
admin.site.register(models.Publish)
admin.site.register(models.Book)

admin添加用户

python manage.py createsuperuser
输入用户名:wuchao
邮箱不用输 直接回车
输入密码:必须超过8位,并且别太简单


admin注册

from django.contrib import admin

# Register your models here.

from app01 import models

admin.site.register(models.Author)
admin.site.register(models.AuthorDetail)
admin.site.register(models.Publish)
admin.site.register(models.Book)


什么是cookie及 应用场景
cookie的运用方法?


什么是cookie?

定义:cookie是服务器让浏览器保存在浏览器本地的键值对
		原因:http是无状态,每次请求之间没有任何关系,无法保存状态。使用cookie来保存一些状态。
实列 用户登录
登录成功向其客户端发送一个令牌 (保存在用户器上的键值对)  用于下次确认身份


cookie的运用方法

1.通过js设置cookie
js查看cookie 
	document. cookie
js设置cookie 注意路径问题path  默认为空  有则覆盖 无则增加
	document. cookie = ' k1=wy222 ;path=/ '

注意: path不同会导致设置不同.
     path的作用
    /,当前网站中所有的URL都能读取到此值.
    ",只能在当前页面访问的到此数据
    /index/ ,只能在/index/xxx的网页中查看.
    
通过jquery设置     有则覆盖 无则增加
$. cookie('k1', 'wy222' ,{path:'/'})


通过py文件设置
# 设置cookie
	data.set_cookie('a1','1',path='/')
    data.set_cookie('a2','2',path='')
    data.set_cookie('a3','3',path='/test/')

# 读取cookie
request.COOKIES.get('cookie键') #判断有没这个值
在下面3种情况都可以设置cookie
return HttpResponse('...')
return render('...')
return redirect('...')


# 用户登录成功
        result = redirect('/index/')
        result.set_cookie('xxxxxxxx',user)
        return result
        
用户输入index网址 后台进行判断 
def index(request):
    """
    博客后台首页
    :param request:
    :return:
    """
    user = request.COOKIES.get('xxxxxxxx')#判断有没这个值
    if not user:
        return redirect('/login/')
    return render(request,'index.html',{'user':user})    
在下面3种情况都可以设置cookie
return HttpResponse('...')
return render('...')
return redirect('...')
 
必须背会一下三个参数:
key 键, value='' 值, max_age=None 秒 多少秒后失效 path 路径



菜鸟教程
https://www.runoob.com/js/js-cookies.html


![1570527138385](file:///C:/Users/86131/AppData/Roaming/Typora/typora-user-images/1570527138385.png)

session

依赖cookie 是一种存储数据的方式,依赖于cookie,
实现本质: 用户向服务端发送请求,服务端做两件事:第一步为此用户开辟一个独立的空间来存放当前用户独有的值.第2步生成随机字符串发给客户端 他保存在游览器中 

在空间中如何想要设置值:
request.session['x1'] = 123
request.session['x2'] = 456 

在空间中取值: 
request.session['x2'] 
request.session.get('x2') 推荐
视图函数中的业务操作处理完毕,给用户响应,在响应时会 将随机字符串存储到用户浏览器的cookie中.

数据 django默认存在数据库中


装饰器


![1570528618057](file:///C:/Users/86131/AppData/Roaming/Typora/typora-user-images/1570528618057.png)

![1570528714941](file:///C:/Users/86131/AppData/Roaming/Typora/typora-user-images/1570528714941.png)

cookie和session的区别?

答: 
cookie是存储在客户端浏览器上的键值对,发送请求时浏 览器会自动携带. 

session是一种存储数据方式,基于cookie 实现,将数据存储在服务端(django默认存储到数据库).
其本质是:用户向服务端发送请求,服务端做两件事:生成随机字符串;
为此用户开辟一个独立的空间来存放当前用户独有的值. 在空间中如何想要设置值: 
request.session['x1'] = 123 request.session['x2'] = 456 
在空间中取值: 
request.session['x2'] 
request.session.get('x2') 
视图函数中的业务操作处理完毕,给用户响应,在响应时会 将随机字符串存储到用户浏览器的cookie中.


session相关的配置

SESSION_COOKIE_NAME = "sessionid"  # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串

SESSION_COOKIE_DOMAIN = None  #那个域名可以用 读取的到 none代表全部子域名


SESSION_COOKIE_PATH = "/"  # Session的cookie保存的路径 /默认所有的

SESSION_COOKIE_HTTPONLY = True  # 是否Session的cookie只支持http传输 True只可以读不可以改

SESSION_COOKIE_AGE = 1209600  #单位秒 Session的cookie失效日期(2周)

SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 是否关闭浏览器使得Session过期

SESSION_SAVE_EVERY_REQUEST = False  # 是否每次请求都保存Session,默认false定时过期时间 如果改为true刷新一次过期时间程序计算


session如何设置过期时间?

SESSION_COOKIE_AGE = 1209600 # Session的 cookie失效日期(2周)


session默认存储可以放在其他地方吗?

小系统:默认放在数据库即可. 大系统:缓存(redis)

在settings文件  里面加入里面的代码
# 文件
# SESSION_ENGINE = 'django.contrib.sessions.backends.file'
# SESSION_FILE_PATH = '/sssss/'

# 缓存(内存)
# SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
# SESSION_CACHE_ALIAS = 'default'
#
# CACHES = {
#     'default': {
#         'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
#         'LOCATION': 'unique-snowflake',
#     }
# }


# 缓存(redis)
# SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
# SESSION_CACHE_ALIAS = 'default'
# CACHES = {
#     "default": {
#         "BACKEND": "django_redis.cache.RedisCache",
#         "LOCATION": "redis://127.0.0.1:6379",
#         "OPTIONS": {
#             "CLIENT_CLASS": "django_redis.client.DefaultClient",
#             "CONNECTION_POOL_KWARGS": {"max_connections": 100}
#             # "PASSWORD": "密码",
#         }
#     }
# }


操作session

# 设置(添加&修改) request.session['x1'] = 123 request.session['x2'] = 456 
# 读取 request.session['xx'] request.session.get('xx') 
# 删除 del request.session['xx'] 其实是修改了过期时间
内容总结
1. 装饰器要加入functools.wrap装饰
2. orm字段中的verbose_name
3. 路由系统中记得加入终止符 $
4. 用户名和密码检测
5. 模板查找顺序
request.session.keys() request.session.values() request.session.items() request.session.set_expiry(value) request.session.session_key


通过js设置cookie

document.cookie = 'k1=wy222;path=/'
$.cookie('k1','wy222',{path:'/'})
注意:path不同会导致设置不同.


path的作用

/ , 当前网站中所有的URL都能读取到此值.
"",只能在当前页面访问的到此数据.
/index/ ,只能在/index/xxx 的网页中查看.


functools

为什么要有functools
因为如果多个函数被两个装饰器装饰时就报错,因为两个函数名一样,第二个函数再去装饰的话就报错.   而增加@functools.wraps(f), 可以保持当前装饰器去装饰的函数的 name 的值不变 所以需要他

理论不懂 看代码实列


当没有 functools时

def wrapper(f):
    def inner(*args,**kwargs):
        '''我是内层函数inner'''
        ret = f(*args,**kwargs)
        return ret
    return inner


#@wrapper等效与index=wrapper(index)
@wrapper
def index(a1,a2):
    """我是原函数index"""
    return a1+a2

index(1,2)

print(index.__name__) #查看此处index函数名  打印结果是--inner
print(index.__doc__)#查看此处index函数的注释打 结果是 ---我是内层函数inner


当有functools时

import functools
def wrapper(f):
    @functools.wraps(f)
    def inner(*args,**kwargs):
        '''我是内层函数inner'''
        ret = f(*args,**kwargs)
        return ret
    return inner


#@wrapper等效与index=wrapper(index)
@wrapper
def index(a1,a2):
    """我是原函数index"""
    return a1+a2

index(1,2)

print(index.__name__) #查看此处index函数名  打印结果是--index
print(index.__doc__)#查看此处index函数的注释打 结果是 ---我是原函数index


对比发现functools可以还原本质 解决了 多个函数被两个装饰器装饰时就报错

posted @ 2020-01-10 17:30  一起奥利给  阅读(117)  评论(0编辑  收藏  举报