目录:1.关于web框架  2.wsgiref模块  3.自己写一个web框架  4.http协议  5.django
 
关于web这一部分的内容还有待进一步整理补充
 
关于web框架:
         web应用程序是一种可以通过web访问的应用程序
                   有两种模式C/S和B/S,一般是B/S模式
                  
                   Google浏览器检查的时候network里面:
                            preview是显示浏览器渲染后的预览效果
                            response是浏览器得到的响应的具体内容,就是代码样式的
                  
         web框架是一种开发框架,用来支持动态网站、网络应用和网络服务的开发
 
wsgiref模块就是python基于wsgi协议开发的服务模块,本质上是一个web框架
         1.按照http请求协议解析数据
         2.这部分是我们自己写的,上面的跟下面的都用wsgiref模块来是实现
         3.按照http响应协议封装数据
        
         实例:
                   from wsgiref.simple_server import make_server
 
                   def application(environ, start_response):
                            # 按照http协议解析的数据就是environ
                            # 按照http协议组装的数据的格式就是start_response
                            path = environ.get('PATH_INFO')  # 这个environ是字典格式
                            print(path)
                            start_response('200 OK', [('Content-Type', 'text/html')])  # 这里第二个元素这个列表可以为空,但是必须要有这个列
                            data = b''
                            if path == '/login':
                                     with open('login.html', 'rb') as f:
                                               data = f.read()
                            elif path == '/index':
                                     with open('index.html', 'rb') as f:
                                               data = f.read()
                            return [data]  # 这里必须用列表的格式
 
                   # 封装socket
                   http = make_server('127.0.0.1', 8060, application)
                   # 等待用户连接 conn,addr = sock.accept()
                   http.serve_forever()
 
自己写一个web框架:
         在pycharm -----> day49-web -----> 03文件夹
        
         自己写的这个03就是一个web框架
         1.main.py是主函数入口,启动文件,封装了socket
         2.urls.py:路径与视图函数的映射关系------url控制器
         3.views.py是整个视图函数部分,固定有一个形参environ-----视图函数
         4.图片、html文件等放在templates里面-------模板
         5.models.py在项目启动前,在数据库中创建表结构,单独执行的-------与数据库相关
 
http协议
         hyper text transfer protocol 超文本传输协议
                   用于万维网(world wide web)服务器与本地浏览器之间传输超文本的传送协议
        
         是一个属于应用层的面向对象的协议
 
         特点:
                   基于TCP/IP协议
                   基于请求-响应模式
                            请求是从客户端发出的,服务端响应该请求并返回,也就是说先从客户端建立通信
                   无状态保存
                            http协议本身不对请求和响应之间的通信状态进行保存
                   无连接
                            限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后就断开连接
 
         http的请求协议与响应协议
                   http协议包含 由浏览器发送数据到服务器需要遵循的请求协议
                                                和 服务器发送数据到浏览器需要遵循的响应协议
                  
                   用于http协议交互的信被称为http报文,请求报文和响应报文
                           
                   请求协议:
                            请求格式
                                     请求首行:请求方法+URL+协议版本(HTTP/1.1),用\r\n与请求头分割
                                     请求头,有多个,用\r\n分割,一直到遇到\r\n\r\n,这后面的内容就是请求体了
                                     请求体,只有post请求才有请求体
                                    
                            请求方法
                                     get  提交的数据会被放在url之后,以?分割url和传输数据,参数之间用&连接
                                                提交的数据有大小限制
                                                有安全问题
                                                没有请求体
                                                常用于做查询操作的时候
                                                默认的,包括a标签也是get方法
                                                
                                     post 把提交的数据放在http包的body中
                                                提交数据没有大小限制
                                                常用于数据库更改的时候
                  
                   响应协议:
                            响应格式
                                     响应首行:协议版本(HTTP/1.1)+状态码+OK(状态码的原因短语)
                                     响应头
                                     响应体
                                    
                            响应状态码
                                     1XX  Informational(信息性状态码)     接收的请求正在处理
                                     2XX  Success(成功状态码)             请求正常处理完毕
                                     3XX  Redirection(重定向状态码)      需要进行附加操作以完成请求,服务器发出的,
                                                                                                                          举个例子就是访问的网站换域名了,访问原来的网址自动跳转到新的
                                     4XX  Client Error(客户端错误状态码)  服务器无法处理请求
                                     5XX  Server Error(服务器错误状态码)          服务器处理请求出错
 
django
         是一种web框架
         MTV模型的
                   M代表model模型
                   T代表Template模板
                   V代表view视图
        
         一个django project
        
                   django-admin startproject mysite
                 
                   下面包含
                            manage.py  与django进行交互的
                            mysite/
                                     __init__.py
                                     settings.py  配置文件
                                     urls.py  控制器/路由
                                     wsgi.py
                   在mysite目录下创建应用
                            python manage.py startapp blog
                            下面包含
                                     blog:
                                               __init__.py
                                               admin.py
                                               apps.py
                                               migrations
                                                        __init__.py
                                               models.py
                                               tests.py
                                               views.py
                  
                   启动django项目
                            python manage.py runserver 8080
 
第一个简单实例在新项目django_01里面,settings里面TEMPLATES的DIRS加一句os.path.join(BASE_DIR, 'templates')
静态文件配置在新项目django_02里面,重点在于
        先加一个static包,盛放jquery文件,这里static就固定用这个名字,虽然可以改,但是不要改了
        再在settings里面加了一个
            STATICFILES_DIRS = [
                os.path.join(BASE_DIR, 'static')
            ]
        这样在客户端访问static/这个地址的时候才能访问的到
        这样配置完了以后写html的时候就可以用jquery文件了,<script src="/static/jquery-3.3.1.js"></script>
        然后,控制html的css文件跟js文件也可以单独拿出来放到static里面,同一个功能的可以放到同一个包里
        html里面<link rel="stylesheet" href="/static/app01/timer.css">
                <script src="/static/app01/timer.js"></script>引入
                要注意引入时候的先后顺序
 
                                    
路由层(URLconf) django_03
         from django.urls import path,re_path
         path是django2版本的语法,re_path是django1版本的语法,2版本两种都支持
        
         URL配置就像django所支撑网站的目录,本质是url与要被该url调用的视图函数之间的映射表
         就是通过这个来告诉django,对于客户端发来的某个url调用哪一段逻辑代码对应执行
         说白了就是哪一个路径由哪一个视图函数来处理
        
         注意:
                   若要从URL 中捕获一个值,只需要在它周围放置一对圆括号,括号是分组的意思,会默认传值,写视图函数的时候要多写一个形参,一个括号多写一个
                   不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
                   每个正则表达式前面的'r' 是可选的但是建议加上。它告诉Python 这个字符串是“原始的”,字符串中任何字符都不应该转义
                  
         有名分组:
                   使用命名的正则表达式组来捕获URL中的值并以关键字参数传递给视图
                   (?P<name>pattern) name是组的名称,也就是关键字参数时候关键字key  pattern是正则匹配规则
                  
         分发:
                   把app01的功能单独出来,放到app01-->urls里面,就是在app01里面再写一个urls.py
                   然后在主urls里面先引入include,再像下面这样写
                   re_path(r"app01/", include("app01.urls")),
                  
         登录验证的实例在django_03
                   def login(request):
                            if request.method == 'GET':
                                     return render(request, 'login.html')
                            else:
                                     user = request.POST.get('user')  # request.POST request.GET拿到的都是字典格式,必须大写
                                     pwd = request.POST.get('pwd')
                                     if user == 'yuan' and pwd == '123':
                                               return HttpResponse('登录成功')  # 必须要有返回
                                     else:
                                              return HttpResponse('用户名或密码错误')
        
         反向解析
                   在使用django项目时,一个常见的需求是获得URL的最终形式,以用于嵌入到生成的内容中或用于处理服务器端的导航
                   硬编码或者设计专门的url生成机制会容易产生过期的url
                  
                   所以在需要url的地方,对于不同层级,django提供不同的工具用于url反查:
                            在模板中,使用url模板标签
                                     path('login/', views.login, name="Log")  # 使用name=给url起别名
                                     <form action="{% url 'Log' %}" method="post">  # 使用反向解析的固定格式,与render有关
                            在python代码中,使用from django.urls import reverse()函数
                                     也是先在url里面起别名
                                               re_path(r'^articles/2003/$', views.special_case_2003, name="s_c_2003"),
                                     再在views.py里面引入reverse
                                               url = reverse("s_c_2003")
                  
                   命名url模式时,需要确保使用的名称不会与其他应用中的名称冲突
                  
         名称空间namespace
                   名称空间是表示标识符的可见范围
                   一个标识符可在多个名称空间中定义,它在不同名称空间中的含义互不相干
                  
                   由于别名name没有作用域,django在反解url时,会在项目全局顺序搜索,当查到第一个name指定url时就返回
                  
                   可以在分发的时候设定namespace是“app01”,放在include里面,限定名称空间,格式是include(("app01.urls","app01"))
                            在使用反解的时候reverse(app01:index)
                  
         django2.0版本的path
                   特有的功能,规则如下:
                            使用尖括号<>从url中捕获值,捕获到的值字符串格式
                            捕获值中可以包含一个转化器类型,比如使用<int:name>捕获一个整数变量
                                     如果没有转化器,将匹配任何字符串,当然也包含了/字符
                            无需添加前导斜杠
                           
                           
                            django默认支持以下5个path转化器(Path Converters)
                                     str   匹配除了路径分隔符/之外的非空字符串,默认值
                                     int   匹配正整数,包含0
                                     slug  匹配字母、数字、横杠和下划线组成的字符串
                                     uuid  匹配格式化的uuid
                                     path  匹配任何非空字符串,包含路径分隔符,?不行,因为?是路径分隔符
                  
                            注册自定义转化器
                                     三点要求:
                                               regex 类属性,字符串类型
                                               to_python(self, value)方法,value是由雷属性regex所匹配到的字符串,返回具体的python变量值
                                                        以供django传递到对应的视图函数中
                                               to_url(self, value)方法,和to_python相反,value是一个具体的python变量值,返回其字符串,通常用于url反向引用
                                    
                                     实例:
                                               class FourDigitYearConverter: 
                                                        regex = '[0-9]{4}' 
                                                        def to_python(self, value): 
                                                                 return int(value) 
                                                        def to_url(self, value): 
                                                                 return '%04d' % value 
                                               使用register_converter 将其注册到URL配置中:
                                               from django.urls import register_converter, path 
                                               from . import converters, views 
                                               register_converter(converters.FourDigitYearConverter, 'yyyy') 
                                               urlpatterns = [ 
                                                        path('articles/2003/', views.special_case_2003), 
                                                        path('articles/<yyyy:year>/', views.year_archive), 
                                                        ... 
                                               ]
 
                                    
视图层
         在django_04
         一个视图函数,简称视图,是一个简单的python函数,它接受web请求并返回web响应
         响应可以是任何东西,但是必须有
         我们约定俗成的将视图放在项目或应用程序目录中名为views.py的文件中
         也约定俗成的将request作为第一个参数传进去
        
         url:协议://IP:port/路径path?请求数据
                  
         请求对象HttpRequest
                   通常称之为request
                   属性:
                            request.method  拿到的就是get或post
                            request.GET     拿到的是字典,可以用get拿数据
                            request.POST  拿到的是字典,可以用get拿数据
                            request.path   拿到的是/index/这一部分,只拿路径,不拿数据部分
                           
                   常用方法:
                            HttpRequest.get_full_path()    返回path,这个会连同数据一起拿到,而上面的path只拿路径
                            HttpRequest.is_ajax()   
        
         响应对象HttpResponse
                   主要由三种形式
                            1.HttpResponse()    括号内直接跟一个具体的字符串作为相应体,可以渲染标签
                           
                            2.render()        
                                     render(request, template_name,[context])
                                     request用于生成响应的请求对象
                                     template_name是要使用的模板的完整名称,就是那个页面html文件
                                    
                                     可选参数context是提交到模板上下文中的一个字典,默认是一个空字典,如果字典中的某个值是可调用的,
                                               视图将会在渲染模板之前调用它,模板语法{'ctime':ctime},在html里面{{ ctime }}
                                              
                            3.redirect()
                                     传递要重定向的一个硬编码的url
                                               return redirect('/some/url')
                                     也可以传递一个完成的url
                                               return redirect('http://example.com')
 
                                              
模板层
         在django_05
         模板语法只有两个:{{}}  0.渲染变量
                                                                 1.深度查询,使用句点符,就是用.,一层层都是用点来查
                                                                 2.过滤器
                                                 {%%}  0.渲染标签
         在django模板中遍历复杂数据结构的关键是句点字符,语法:{{var_name}}
        
                   return render(request, 'index.html', locals())  # locals()用于局部变量比较多的时候,也是按照键值对字典的方式
                  
                   对于复杂数据结构,用.来查询
                   <p>{{ l.1 }}</p>
                   <p>{{ info.name }}</p>
                   <p>{{ alex.name }}</p>
                   <p>{{ alex.age }}</p>
                   <p>{{ person_list.1.age }}</p>
        
         过滤器
                   语法:{{obj|filter_name:param}}
                  
                   default
                            {{value|default:"nothing"}}
                            如果一个变量是false或者为空,显示给定的nothing,否则,显示变量的值
                  
                   length
                            {{value|length}}
                            返回值的长度,它对字符串和列表都起作用
                            如果value是['a','b','c','d'],那么输出4
                  
                   filesizeformat
                            {{value|filesizeformat}}
                            将值格式化为一个人类可读的文件尺寸
                            如果value是123456789,输入将会是117.7MB
                  
                   date
                            {{value|data:"Y-m-d"}}
                            如果value=datetime.datetime.now()
                  
                   slice切片,顾头不顾尾
                            {{value|slice:"2:-1"}}
                            如果value="hello world"
                  
                   truncatechars
                   truncatewords
                            {{value|truncatechars:9}}
                            如果字符串字符多于指定的字符数量,那么将会被截断,截断的字符串将会以可翻译的省略号序列("...")结尾
                            参数是要截断的字符数,注意后面的三个...会占空
                  
                   safe
                            告诉django这段代码不必转义
                            value="<a href="">点击</a>"
                            {{value|safe}}
                  
                   add
                            {{ l.1|add:100 }}加法
                  
         标签
                   看起来是这样的{% tag %}
                  
                   for标签
                            遍历每一个元素
                            {% for i in l  %}
                                     <p>{{ i }}</p>
                            {% endfor %}
 
                            遍历每一个字典
                            {% for i, v in info.items %}
                                     <p>{{ forloop.counter }} {{ i }}:{{ v }}</p>  # 使用forloop加序号,作用类似于enumerate,但是从1开始,counter0就从0开始
                            {% endfor %}
                  
                   for...empty
                            for标签带有一个可选的{% empty %}从句,以便在给出的组是空或者没找到时可以有所操作
                            {% for person in person_list %}
                                     <p>{{ person.name }}</p>
 
                            {% empty %}
                                     <p>sorry,no person here</p>
                            {% endfor %}
                  
                   if标签
                            {% if %}会对一个变量求值,如果它的值是True(存在、不为空且不是boolean类型的false值),输出对应的内容
                            {% if user %}
                                     <p><a href="#">hi{{ user }}</a></p>
                            {% else %}
                                     <p><a href="#">登录</a></p>
                            {% endif %}
                           
                   with标签
                            使用一个简单的名字缓存一个复杂的变量,当你需要使用一个昂贵的方法(比如访问数据库)很多次时非常有用
                            {% with person_list2.1.name as n %}
                                     {{ n }}
                                     {{ n }}
                                     {{ n }}
                                     {{ n }}
                            {% endwith %}
                           
                   csrf_token
                            用于跨站请求伪造保护,加在html的form表单里面
                            <form action="" method="post">
                                     {% csrf_token %}
                                     <input type="text" name="user">
                                     <input type="submit">
                            </form>
                           
         自定义标签和过滤器
                   1.在settings中INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag
                   2.在app中创建templatetags模块,模块名只能是templatetags,是一个包
                   3.创建任意.py文件,如my_tags.py
                            from django import template
                            from django.utils.safestring import mark_safe
                             
                            register = template.Library()   #register的名字是固定的,不可改变
                             
                             
                            @register.filter  自定义的过滤器最多只能传两个值进去
                            def filter_multi(v1,v2):
                                     return  v1 * v2
                            <br>
                            @register.simple_tag  自定义标签 没有传参的限制
                            def simple_tag_multi(v1,v2):
                                     return  v1 * v2
                            <br>
                            @register.simple_tag
                            def my_input(id,arg):
                                     result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
                                     return mark_safe(result)
                   4.在使用自定义simple_tag和filter的html文件中导入之前创建的my_tags.py
                            {% load my_tags %}
                   5.使用simple_tag和filter,filter可以用在if等语句之后,simple_tag不可以
                            {% if i|multi_filter:20 > 300 %}
                                     <p> >300 </p>
                            {% else %}
                                     <p> {{ i|multi_filter:20 }} </p>
                            {% endif %}
                           
         继承
                   django_05的base.html,include也在里面,include是把部分内容单独拿出来,用include引入
                            {% include 'advertise.html' %}
                   base是一个html模板,在其他html中用{% extends 'base.html' %}继承,这一句必须写在首行
                            这样新的html就拥有相同的样式,
                            然后再base中放空盒子用来给新html添加内容,空盒子如下:
                                {% block con %}
                                     {% endblock %} 
                                     盒子可以放多个,con可以随便写,title也可以这样写,block盒子越多越好
                                     endblock也可以起名字,这样更可读
                            在新html中,把要添加的内容放到盒子中间
 
模型层
         ORM是对象-关系-映射的简称,实现了数据模型与数据库的解耦
                   即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库
                  
                   只能对表进行操作,不能对数据库进行操作
                  
         单表操作django_06
                   创建表
                            1.在app01的models.py里面,固定格式如下:
                           
                                     from django.db import models
                                     # Create your models here.
                                     class Book(models.Model):
                                               id = models.AutoField(primary_key=True)
                                               title = models.CharField(max_length=32)
                                               pub_date = models.DateField()
                                               price = models.DecimalField(max_digits=8, decimal_places=2)  # 这个是最大是八个数,有两位是小数
                                               publish = models.CharField(max_length=32)
                  
                            2.配置settings
                                     DATABASES = {
                                               'default': {
                                                        'ENGINE': 'django.db.backends.mysql',
                                                        'NAME':'bms',           # 要连接的数据库,连接前需要创建好
                                                        'USER':'root',        # 连接数据库的用户名
                                                        'PASSWORD':'',        # 连接数据库的密码
                                                        'HOST':'127.0.0.1',       # 连接主机,默认本级
                                                        'PORT':3306            #  端口 默认3306
                                               }
                                     }
                           
                            3.在项目名称下的__init__.py里面加上,而不是app的__init__.py里面
                                     import pymysql
                                     pymysql.install_as_MySQLdb()
                            上面这两步2 3是为了在本机生成一个表,如果不进行上面这两步,那么就在项目中生成mysql表
                           
                            4.还要确保配置文件settings中的INSTALLED_APPS中写入了app的名称
                           
                            5.最后通过两条数据库迁移命令即可在指定的数据库中创建表 :
                                     python manage.py makemigrations
                                     python manage.py migrate
                           
                            1 4 5必写,2 3是为了在本地生成表
                           
                   添加表记录
                            # 添加表记录
                            # 方式一
                            book_obj = Book(id=1, title='python红宝书', price=100, pub_date='2012-12-12', publish='人民出版社')
                            book_obj.save()
                            # 方式二,返回值是当前生成的记录的对象,可以操作对象取值,推荐
                            book_obj2 = Book.objects.create(title='php', price=100, pub_date='2013-12-12', publish='人民出版社')
                  
                   查询表记录
                            <1> all():                  查询所有结果
                                                        book_list = Book.objects.all()
                                                        print(book_list)  # 返回QuerySet数据类型,django独有的,有点像列表,可以进行for循环、索引、切片等
                                                        for i in book_list:
                                                                 print(i.title, i.price)
          
                            <2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象,条件可以加多个,用,分割
                                             # filter() 调用者是objects管理器,返回值是queryset对象,该对象可以使用first last方法
                                                        book_list = Book.objects.filter(price=100)
                                                       
                             
                            <3> get(**kwargs):          返回与所给筛选条件相匹配的对象,返回结果有且只有一个,
                                                                                             如果符合筛选条件的对象超过一个或者没有都会抛出错误。
                                                        book_obj = Book.objects.get(title='php')
                             
                            <4> exclude(**kwargs):      它包含了与所给筛选条件不匹配的对象,排除
                                                        调用者是objects管理器,返回值是queryset对象,该对象可以使用first last方法
                                                        book_obj = Book.objects.exclude(title='go')
                             
                            <5> order_by(*field):       对查询结果排序
                                                        调用者是queryset对象,返回值是queryset对象
                                                        ret = Book.objects.all().order_by('id')
                                                        按照id升序排列,加一个-就是'-id'就是降序排列
                             
                            <6> reverse():              对查询结果反向排序
                             
                            <8> count():                返回数据库中匹配查询(QuerySet)的对象数量。
                                                        ret = Book.objects.all().count()
                                                        调用者是queryset对象,返回值是int类型
                             
                            <9> first():                返回第一条记录              
                            <10> last():                返回最后一条记录
                                                        # first() last() 调用者是queryset对象,返回值是model对象
                                                        theFirst = Book.objects.all().first()
                                                        theLast = Book.objects.all().last()
                                      
                            <11> exists():              如果QuerySet包含数据,就返回True,否则返回False
                                                        ret = Book.objects.all().exists()  只要有就行
                                                        调用者是queryset对象,返回值是布尔值
                             
                            <12> values(*field):        返回一个ValueQuerySet  一个特殊的QuerySet,运行后得到的并不是一系列
                                                                                             model的实例化对象,而是一个可迭代的字典序列
                                                                                             调用者是queryset对象
                                                        ret = Book.objects.all().values('price')
                                                        取值是ret[0].get('price')
                                                       
                                                                
                            <13> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
                                                                                             调用者是queryset对象
                                                        ret = Book.objects.all().values_list('price')
                             
                            <14> distinct():            从返回结果中剔除重复纪录,配合value或value_list使用
                                                        ret = Book.objects.all().values('price').distinct()
                                                                                             调用者是queryset对象,返回值看是value还是value_list
                  
                   基于双下划线的模糊查询
                            Book.objects.filter(price__in=[100,200,300])
                            Book.objects.filter(price__gt=100)  # 大于
                            Book.objects.filter(price__lt=100)  # 小于
                            Book.objects.filter(price__range=[100,200])
                            Book.objects.filter(title__contains="python")  # 有python就行,在哪不重要,区分大小写
                            Book.objects.filter(title__icontains="python")  # 有python就行,在哪不重要,不区分大小写
                            Book.objects.filter(title__startswith="py")  以py开头的
                            Book.objects.filter(pub_date__year=2012)
                  
                   删除表记录,通用于单表和多表
                            删除方法就是 delete()。它运行时立即删除对象,返回任何值是删除的记录数。可以一次删除多个对象
                            调用者是queryset对象,model对象也可以调用delete
                           
                   修改表记录,通用于单表和多表
                            Book.objects.filter(title__startswith="py").update(price=120),返回值是修改的记录数
                            调用者只能是queryset对象
                  
                   练习:
                            1 查询老男孩出版社出版过的价格大于200的书籍
                                     Book.objects.filter(publish='老男孩出版社', price__gt=200)
 
                            2 查询2017年8月出版的所有以py开头的书籍名称
                                     Book.objects.filter(title__startswith='py', pub_date__year=2017, pub_date__month=8).values('title')
                             
                            3 查询价格为50,100或者150的所有书籍名称及其出版社名称
                                     Book.objects.filter(price__in=[50,100,150]).values('title', 'publish')
                             
                            4 查询价格在100到200之间的所有书籍名称及其价格
                                     Book.objects.filter(price__range=[100,200]).values('title', 'price')
                             
                            5 查询所有人民出版社出版的书籍的价格(从高到低排序,去重)
                                     Book.objects.filter(publish='人民出版社').values('price').distinct().order_by('-price')
        
图书管理系统的作业    
         实现功能:book单表的增删改查,写在django_07
        
         多表操作django_08
                   一对多
                            一旦确定表关系是一对多,创建关联字段,关联字段必须在多的表中
                   多对多
                            一旦确定表关系是多对多,创建第三张关系表,三个字段,id和另外两张表的关联字段
                   一对一
                            其实就是把本来的一张表,其中几个字段拿出来单独写一张表,就变成一对一的关系了
                            在两张表的任意一张表中建立关联字段加上unique
                  
                   建关联字段是为了进行查询,加外键约束条件是为了保护数据安全,避免有关联的内容被删除导致其他数据查不到
                  
                   创建表
                   实例:用ORM生成关联的表模型
                            from django.db import models
 
                            class Author(models.Model):
                                     nid = models.AutoField(primary_key=True)
                                     name=models.CharField( max_length=32)
                                     age=models.IntegerField()
 
                                     # 与AuthorDetail建立一对一的关系
                                     authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
 
                            class AuthorDetail(models.Model):
                                     nid = models.AutoField(primary_key=True)
                                     birthday=models.DateField()
                                     telephone=models.BigIntegerField()
                                     addr=models.CharField( max_length=64)
 
                            class Publish(models.Model):
                                     nid = models.AutoField(primary_key=True)
                                     name=models.CharField( max_length=32)
                                     city=models.CharField( max_length=32)
                                     email=models.EmailField()
 
                            class Book(models.Model):
                                     nid = models.AutoField(primary_key=True)
                                     title = models.CharField( max_length=32)
                                     publishDate=models.DateField()
                                     price=models.DecimalField(max_digits=5,decimal_places=2)
 
                                     # 与Publish建立一对多的关系,外键字段建立在多的一方,生成字段
                                     publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
                                     # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
                                     authors=models.ManyToManyField(to='Author',)          
                                              
                            注意:
                                     表的名字是自动生成的
                                     id字段是自动添加的
                                     对于外键字段,django会在字段名上添加_id来创建数据库表中的列名
                                     一对一和一对多一定要加上on_delete=models.CASCADE,只要是django2.0版本
                                     外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None
        
                   添加表记录
                            一对多:
                                     方式1:
                                        publish_obj=Publish.objects.get(nid=1)
                                        book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish=publish_obj)
                                      
                                     方式2:
                                        book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish_id=1)  
                                    
                                     book_obj.publish是与这本书关联的出版社对象
                                     book_obj.publish_id是与这本书关联的出版社的id
                           
                            多对多:
                                     # 当前生成的书籍对象
                                     book_obj=Book.objects.create(title="追风筝的人",price=200,publishDate="2012-11-12",publish_id=1)
                                     # 为书籍绑定的做作者对象
                                     yuan=Author.objects.filter(name="yuan").first() # 在Author表中主键为2的纪录
                                     egon=Author.objects.filter(name="alex").first() # 在Author表中主键为1的纪录
 
                                     # 绑定多对多关系,即向关系表book_authors中添加纪录
                                     book_obj.authors.add(yuan,egon)    #将某些特定的 model 对象添加到被关联对象集合中  book_obj.authors.add(*[])
                                                                                    可以直接放1,2
                                                                                    可以放*[1,2]
                                    
                                     多对多关系其它常用API:
                                               book_obj.authors.remove()      #将某个特定的对象从被关联对象集合中去除。book_obj.authors.remove(*[])
                                               book_obj.authors.clear()       #清空被关联对象集合
                                               book_obj.authors.set()         #先清空再设置 
                                    
                                     book.authors.all() 是于这本书关联的所有作者对象的集合 
                  
                   跨表查询:
                            1.基于对象的查询
                            2.基于双下划线的查询
                            3.聚合与分组查询
                            4.F查询与Q查询
        
                   基于对象的跨表查询,就是mysql里的子查询
                            关联属性在A表中,通过A找B就是正向查询,通过B找A就是反向查询
                           
                           
                            实例:
                                     一对多:正向查询按字段查,反向查询按表名查,表名小写_set
                                               # 查询麦田这本书出版社的名字,一对多的正向查询,按字段
                                               book_obj = Book.objects.filter(title='麦田').first()
                                               print(book_obj.publish.name)
                                               # 查询人民出版社出版的书籍名称,一对多的反向查询,按表名,小写表名_set
                                               publish = Publish.objects.filter(name='人民出版社').first()
                                               book_list = publish.book_set.all()
                                               print(book_list)
                                    
                                     多对多:正向查询按字段查,反向查询按表名查,表名小写_set
                                               # 查询麦田的所有作者名称,多对多的正向查询
                                               book_obj = Book.objects.filter(title='麦田').first()
                                               author_list = book_obj.authors.all()
                                               for author in author_list:
                                                        print(author.name)
                                               # 查询alex出版过的所有书籍名称,多对多的反向查询
                                               alex = Author.objects.filter(name='alex').first()
                                               book_list = alex.book_set.all()
                                               for book in book_list:
                                                        print(book.title)
                                    
                                     一对一:正向查询按字段,反向查询按表名
                                               # 查询alex的手机号,一对一的正向查询
                                               alex = Author.objects.filter(name='alex').first()
                                               print(alex.authorDetail.telephone)
                                               # 查询手机号为110的作者的名字跟年龄,一对一的反向查询
                                               ad = AuthorDetail.objects.filter(telephone=110).first()
                                               print(ad.author.name)
                                               print(ad.author.age)
                           
                           
                           
                           
                   基于双下划线的跨表查询,就是mysql里的join,相对用的较多
                            正向查询按字段,反向查询按表名小写用来告诉ORM引擎join哪张表
                           
                            实例:
                                     # 一对多
                                     # 一对多查询的正向查询:查询麦田这本书出版社的名字
                                     # ret = Book.objects.filter(title='麦田').values('publish__name')
                                     # print(ret)
                                     # 一对多的反向查询:查询麦田这本书出版社的名字
                                     # ret = Publish.objects.filter(book__title='麦田').values('name')
                                     # print(ret)
 
                                     # 多对多
                                     # 多对多的正向查询:查询麦田的所有作者名称
                                     # ret = Book.objects.filter(title='麦田').values('authors__name')
                                     # print(ret)
                                     # 多对多的反向查询:查询麦田的所有作者名称
                                     # ret = Author.objects.filter(book__title='麦田').values('name')
                                     # print(ret)
 
                                     # 一对一
                                     # 一对一的正向查询:查询alex的手机号
                                     # ret = Author.objects.filter(name='alex').values('authorDetail__telephone')
                                     # print(ret)
                                     # 一对一的反向查询:查询alex的手机号
                                     ret = AuthorDetail.objects.filter(author__name='alex').values('telephone')
                                     print(ret)
                                    
                                     # 进阶练习 连续跨表查询
                                     # 查询手机号以110开头的作者出版过的所有书籍名称以及书籍出版社的名称
                                     # 方式一:
                                     # ret = Book.objects.filter(authors__authordetail__telephone__startswith=110).values('title', 'publish__name')
                                     # print(ret)
                                     # 方式二:
                                     ret = Author.objects.filter(authorDetail__telephone__startswith=110).values('book__title', 'book__publish__name')
                                     print(ret)
        
                   聚合查询
                            # 聚合查询 aggregate返回值是一个字典,而不是queryset格式的数据,键的名称可以自定义
                            #                   可以放多个参数                                                               ,默认是price__avg
                            # 查询所有书籍的平均价格
                            from django.db.models import Avg, Max, Min, Count
                            ret = Book.objects.all().aggregate(Avg('price'))
                            print(ret)
                  
                   分组查询
                            单表分组查询
                                     单表模型.objects.values('group by的字段').annotate(聚合函数(‘统计字段))
                                     annotate函数的返回值是queryset数据类型
                                    
                                     # 查询每一个部门的名称以及员工的平均薪水
                                     from django.db.models import Avg, Max, Min, Count
                                     ret = Emp.objects.values('dep').annotate(Avg('salary'))
                                     print(ret)
                                    
                                     Emp.objects.all()翻译成sql语句就是select * from Emp
                                    
                            多表/跨表分组查询
                                     每一个后的表模型.objects.values('主键').annotate(聚合函数(关联表__统计字段))
                                     每一个后的表模型.objects.annotate(聚合函数(关联表__统计字段)),不加values的话会默认有一个all()
                                     最好用主键分组,下面的例子有的不是用主键分组的,不太好
                                    
                                     # 查询每一个出版社名称及出版的书籍的个数
                                     from django.db.models import Avg, Max, Min, Count
                                     ret = Publish.objects.values('name').annotate(c=Count('book__title'))
                                     print(ret)
                                     # 查询每一个作者的名字以及出版过的书籍的最高价格
                                     from django.db.models import Avg, Max, Min, Count
                                     ret = Author.objects.values('name').annotate(m=Max('book__price'))
                                     print(ret)
                                     # 查询每一个书籍的名称以及对应的作者个数
                                     from django.db.models import Avg, Max, Min, Count
                                     ret = Book.objects.values('title').annotate(Count('authors__name'))
                                     print(ret)
                                     # 统计每一本以‘红’开头的书籍的作者个数
                                     from django.db.models import Avg, Max, Min, Count
                                     # ret = Book.objects.filter(title__startswith='红').values('pk').annotate(Count('authors__name'))
                                     # print(ret)
                                     # 统计不止一个作者的书籍
                                     ret = Book.objects.values('pk').annotate(c=Count('authors__name')).filter(c__gt=1).values('title', 'c')
                                     print(ret)
                  
                   F查询与Q查询
                            F查询:
                                # 查询评论数大于收藏数的书籍
                                from django.db.models import F
                                Book.objects.filter(commnetNum__lt=F('keepNum'))
                                     #将每一本书的价格提高30元:
                                     Book.objects.all().update(price=F("price")+30) 
                                     # Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
                                     # 查询评论数大于收藏数2倍的书籍
                                     Book.objects.filter(commnetNum__lt=F('keepNum')*2)
                           
                            Q查询:
                                     from django.db.models import Q
                                     Q对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象
                                     bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))
                                     Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询
                                     bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")
                                     查询函数可以混合使用Q 对象和关键字参数,但是,Q对象,它必须位于所有关键字参数的前面
                                     bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
                              title__icontains="python")
                           
基于多表的图书管理在django_09
 
 
像服务器发送请求的途径:
         1.浏览器的地址栏,默认是get请求
         2.form表单:
                            get请求
                            post请求
         3.a标签,默认get请求
         1.2.3都是同步请求
         4.ajax:
                   特点:
                            1.异步请求
                            2.局部刷新
                   请求方式:
                            get请求
                            post请求
 
Ajax django_10
         AJAX(Asynchronous Javascript And XML)即异步Javascript和XML
         使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)
         特点:
                   1.异步请求
                   2.局部刷新
        
         同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
         异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
        
         登录验证的时候用到了json
                   在html中
        var ret =JSON.parse(data);  // js的反序列化json字符串方法
                if (ret.user){
                    location.href = 'http://www.baidu.com'  // 跳转页面
                }else{
                    $('.error').html(ret.msg).css({'color':'red', 'margin-left':'10px'})
                }
        
         ContentType请求头
                   指定请求体的编码类型
                   1.application/x-www-form-urlencoded,只要是post类型,无论是原生form还是ajax的form都默认是这种类型
                   2.multipart/form-data,使用表单上传文件时,必须设置enctype=multipart/form-data
                   3.application/json 用来告诉服务端消息主体是序列化后的 JSON 字符串
        
         基于form表单上传文件
         基于ajax上传文件
         都在file_put里面
        
分页器 在django_11
         from django.core.paginator import Paginator
        
forms组件 在django_12
         如果所有的字段都校验成功的话,那么form.cleaned_data里面有所有的校验成功的键值对,是一个字典
         如果传进来的值有检验函数未定义的部分,那么没有影响,只要规定的部分正确就行
         如果传进来的值缺少校验函数定义的部分,那么就当做空值,那就是false,即可多不可少
         form.errors也是一个字典,里面是匹配错误的,键是匹配的键,值是错误信息
        
         form表单的name属性值一定要与forms组件字段名称保持一致才能进行校验
        
         forms组件的渲染标签功能
                  
         forms组件渲染错误信息<form action="" method="post" novalidate>
        
         forms组件参数设置
                   class UserForm(forms.Form):
                            name = forms.CharField(min_length=4, label='用户名', error_messages={'required': '该字段不能为空'},
                                                                              widget=widgets.TextInput(attrs={'class': 'form-control'}))
                            pwd = forms.CharField(min_length=4, label='密码',
                                                                             widget=widgets.PasswordInput(attrs={'class': 'form-control'}),  # 设置成密文
                                                                             error_messages={'required': '该字段不能为空'})
                            r_pwd = forms.CharField(min_length=4, label='确认密码', error_messages={'required': '该字段不能为空'},
                                                                                    widget=widgets.TextInput(attrs={'class': 'form-control'}))
                            email = forms.EmailField(label='邮箱', error_messages={'required': '该字段不能为空','invalid': '格式错误'},
                                                                                     widget=widgets.TextInput(attrs={'class': 'form-control'}))
                            tel = forms.CharField(label='手机号', error_messages={'required': '该字段不能为空'},
                                                                             widget=widgets.TextInput(attrs={'class': 'form-control'}))
                           
会话跟踪技术         django_13                 
         cookie
                   具体一个浏览器针对一个服务器存储键值对
                   一般默认有效时间是两周
                   Cookie大小上限为4KB;
                    一个服务器最多在客户端浏览器上保存20个Cookie;
                    一个浏览器最多保存300个Cookie;
                   response.set_cookie(key,value)  设置
                   request.COOKIE.get(key)                           读取
                    
                   path='/', 设置Cookie生效的路径,不设置的话任何路径都能拿到cookie,设置后只有这个路径可以
                  
                   删除cookie:
                            response.delete_cookie("cookie_key",path="/",domain=name)
                           
                            在浏览器内ctrl+shift+delete就能清除cookie
 
         session
                   流程:
                            1.浏览器向服务器发送第一次请求
                            2.服务器设置session,生成随机字符串,并将随机字符串与设置的session对应,并将随机字符串作为cookie返回给浏览器
                            3.浏览器携带cookie向服务器发送第二次请求,服务器根据cookie中的随机字符串去找到对应的session值
                            1.生成随机字符串作为session-key
                            2.设置cookie
                            3.在django_session表中创建一条记录,以session-key随机字符串为键,以数据的字典格式为值
                           
                           
                   语法:
                            1、设置Sessions值
                                                 request.session['session_name'] ="admin"
                            2、获取Sessions值
                                                 session_name = request.session["session_name"]
                            3、删除Sessions值
                                                 del request.session["session_name"]
                            4、flush()
                                     request.session.flush()
                                      删除当前的会话数据并删除会话的Cookie。
                                      这用于确保前面的会话数据不可以再次被用户的浏览器访问
                  
                   session的数据,然后之前有该浏览器的数据,那就是会更新,如果没有那就创建
                           
                   session的配置
                            Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
                                    
                            配置 settings.py
           
                            SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
                              
                            SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
                            SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
                            SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
                            SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
                            SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
                            SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
                            SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
                            SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
 
用户认证组件 django_14
         逻辑更严谨
         用户认证推荐用这套组件,功能是用session记录登录验证状态
         使用前提:要有一张用户表,是django自带的auth-user表
         创建超级用户:
         auth模块
                   from django.contrib import auth
                   auth模块三个常用方法:
                            authenticate()
                                     用户认证,一般需要username password两个关键字参数
                                     如果认证消息有效,会返回一个user对象
                            login()
                                     接收request 和 一个认证了的user对象两个参数
                                     返回一个request.user对象,给已通过认证的用户附上session id等信息
                            logout()
                                     注销用户
                                     接收一个request参数,无返回值
                                     调用该函数,当前请求的全部session信息会全部被清除
         user对象
                   属性:username password,password是用哈希算法保存到auth-user表中的
                   方法:
                            is_authenticated
                                     用于检查用户是否通过了认证,如果是真正的user对象,返回值为True
                                     request.user.is_authenticated
                                    
                                     from django.contrib.auth.decorators import login_required
                                     这是django设计好的一个装饰器,用于处理一种情况:
                                               用户登录后才能访问某些页面
                                               如果没有登录就访问该页面的话会跳转到登陆页面
                                               在登录页面中登录后再在动跳转到之前要访问的地址
                                     若用户没有登录,则会跳转到django默认的登录URL '/accounts/login/ '
                                     (这个值可以在settings文件中通过LOGIN_URL进行修改)。
                                     并传递当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。
        
                            创建用户
                                      使用create_user这个辅助函数来创建用户
                                               from django.contrib.auth.models import User
                                               user = User.objects.create_user(username='',password='',email='')
                                    
                            check_password()
                                     用户要修改密码的时候,让用户先输入原来的密码,通过检查后返回True
                           
                            修改密码set_password()
                                     user = User.objects.get(username='')
                                     user.set_password(password='')
                                     user.save
        
         匿名用户对象
                   id永远为None
                   username永远为空字符串
                   get_username() 永远返回空字符串
                   is_staff 和 is_superuser永远为false
                   is_active 永远为false
                   groups和user_permissions永远为空
                   is_anonymous()返回True而不是False
                   is_authenticated() 返回False而不是True
                   set_password() check_password() save() 和delete() 引发NotImplementedError
                  
         总结
                   如果未登陆 request.user就是AnonymousUser
                   如果登陆了就是登陆对象
                   request.user是一个全局变量
 
中间件
         浏览器发请求给服务器,先到wsgiref,wsgiref处理成request发送到中间件,七个中间件依次处理process_request
         然后交给路由控制,分发到对应的视图函数处理,可能就要去与数据库和模板进行交互了,拿到处理结果交给中间件,交给process_response处理
         处理完了交给wsgiref进行数据封装,最后把封装好的数据交给浏览器,浏览器渲染显示在页面上
 
         介于request与response之间的一道处理过程,可以在全局上改变django的输入输出
        
         中间件的四个方法:
                   process_request,不能有返回值
 
                   process_view可以用来调用视图函数,process_request--->路由控制--->process_view--->视图函数
                            如果有返回值,会越过其他的process_view以及视图函数,但是所有的process_response都还会执行。
 
                   process_exception 如果视图函数出错,这个方法才会执行,也是像response一样倒着执行
 
                   process_response,必须有返回值
        
         应用案例
                   做IP访问频率限制
                   URL访问过滤