Django篇---三

昨日内容回顾
小白必会三板斧
HttpResponse # 返回字符串

    render  # 返回html页面 并且可以给该html传值
    
    redirect  # 重定向
        # 既可以是我们自己的路径也可以是网上的路径
    
    django返回的都是HttpResponse对象

以登录功能为例

静态文件配置
    用户能够在浏览器中输入网址访问到相应的资源
    前提是后端暴露了该资源接口
    在django中如果你想让用户访问到对应的资源,我们只需要
    在urls.py中设置对应关系
    反过来如果我没有在urls.py中开设资源 用户就永远就访问不到对应的资源
    
    返回给浏览器的html页面上所有的静态资源 也需要请求后端加载获取

    通常我们将网站所用到的html文件全部存放在templates文件夹下
    网站用到的静态资源全部存放到static文件夹下
        静态文件
            网站自己写的
                js
                css
                font
                第三方的代码 bootstrap elementUi layui

    STATIC_URL = '/static/'  # 是访问静态资源的接口前缀
"""只要你想访问静态资源 你就必须以static开头"""
# 手动配置静态文件访问资源
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static'),
    # os.path.join(BASE_DIR,'static1'),
    # os.path.join(BASE_DIR,'static2'),
]

静态文件路径动态解析
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
<script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>

request对象
request.method # 获取前端的请求方式 并且是大写的字符串形式

  请求方式
        get
        post
        两者都能够携带数据,但是get请求携带的数据是直接拼接在url后面 不安全并且数据大小有限制
  request.GET  # 你就把它当成一个大字典 里面放的是get请求携带过来的数据
    request.POST  # 你就把它当成一个大字典 里面放的是post请求携带过来的数据
    """上面的大字典 所有的value都是一个列表"""
    
    request.GET.get('key')  # 默认取的是列表的最后一个元素 并不是直接将列表取出
    request.GET.getlist('key')  # 直接将value的列表取出
 request.POST.get('key')  # 默认取的是列表的最后一个元素 并不是直接将列表取出
    request.POST.getlist('key')  # 直接将value的列表取出
 form表单action参数
        1.不写 默认往当前地址提交
        2.写后缀 /index  朝着本网站的index路径提交数据
        3.写全路径  http://www.xiaohuar.com
    
    form表单默认的提交数据方式是get请求  你可以通过method参数修改提交方式
        前端获取用户输入的信息 会被存放在input/option/...标签的value属性中
pycharm连接数据库(pycharm充当数据库的客户端)
django连接数据库配置
    django默认自带的一个小型的sqlite数据库   该数据库功能不是很强大 尤其是对日期格式的数据 不是很兼容
    
    django连接MySQL有两步配置
        1.settings文件中配置
            DATABASES = {
                'default': {
                    'ENGINE': 'django.db.backends.mysql',  # 指定数据库 MySQL postgreSQL
                    'NAME': 'day57',  # 到底使用哪个库
                    'USER':'root',
                    'PASSWORD':'root',
                    'HOST':'127.0.0.1',
                    'PORT':3306,
                    'CHARSET':'utf8'
                }
            }
        2.django默认使用的是mysqldb模块连接数据库  但是该模块不兼容 不推荐使用
        告诉django使用pymysql模块连接数据库
            在项目名的文件下的__init__文件或者应用名下的__init__文件中都可以指定
            import pymysql
            pymysql.install_as_MySQLdb()

django orm简介

    强调 django orm并不会自动帮你创建数据库 需要你手动指定 
        一个django项目用一个数据库(******)

    ORM对象关系映射
	表创建
        在应用名下的models.py中书写模型类
        from django.db import models

        # Create your models here.
        class Userinfo(models.Model):
            # 设置id字段为userinfo表的主键  id int primary key auto_increment
            id = models.AutoField(primary_key=True)  # 在django中 你可以不指定主键字段 django orm会自动给你当前表新建一个名为id的主键字段
            # 设置username字段  username varchar(64)  CharField必须要指i定max_length参数
            username = models.CharField(max_length=32)  # 在django orm中 没有char字段  但是django 暴露给用户 可以自定义char字段
            # 设置password字段  password int
            password = models.IntegerField()
            # phone = models.BigIntegerField(default=110)  # 新增的字段 可以提前设置默认值
            # addr = models.CharField(max_length=64,null=True)  # 新增的字段 可以设置为空
        
    *****************************************数据库迁移命令************************************************
    1.python manage.py makemigrations  # 不会真正操作数据 只是将记录记录到小本本上(对应应用名下面的migrations文件)
    2.python manage.py migrate  # 真正的将记录同步到数据库中 
        # 当你第一次执行上面两条命令的时候 django会自动创建很多张表  这些表都是django默认需要用到的表
        # 你自己写的模型类所对应的表 表名有固定格式
            应用名_表名
数据操作
        表字段的增删改查
            新增的字段
                1.直接提供默认值 default
                2.设置改字段可以为空 null=True
            注意的是 不要轻易的注释models.py中任何跟数据库相关的代码
            主要是跟数据库相关的代码 你在处理的时候一定要小心谨慎
 数据的增删改查
            数据的查
                get() 
                    1.条件存在的情况下 获取的直接是数据对象本身
                    2.条件不存在的情况下 会直接报错  所以不推荐你使用get方法查询数据
                
                filter()
                    1.条件存在的情况下 获取到的是一个可以看成列表的数据 列表里面放的才是一个个数据对象本身
                    2.条件不存在的情况下  并不会报错 返回的是一个可以看成空列表的数据
                    3.filter括号内可以写多个参数逗号隔开  这多个参数在查询的时候 是and关系
                    4.filter的结果支持索引取值  但是不支持负数  并且不推荐你使用索引  推荐你使用它封装好的方法 first取第一个数据对象
            数据的增
                1.create()
                    1.括号内些关键字参数的形式 创建数据
                    2.该方法会有一个返回值 返回值就是当前对象本身
                2.利用对象点方法的方式
                    user_obj = User(username='jason')
                    user_obj.save()  # 将当前对象保存到数据库中
1.数据的编辑与删除
#数据的编辑,展示用户所有信息
def userlist(request):
    #获取用户表中的所有数据
    user_queryset = models.Userinfo.objects.all()
    # print(user_queryset)  #<QuerySet [<Userinfo: Userinfo object>, <Userinfo: Userinfo object>, <Userinfo: Userinfo object>]>
    # print(user_queryset.query)  #只有queryset对象才能点query查询内部所对应的sql语句;SELECT `app01_userinfo`.`id`, `app01_userinfo`.`username`, `app01_userinfo`.`password`, `app01_userinfo`.`phone`, `app01_userinfo`.`addr` FROM `app01_userinfo`
    # filter 和 all 拿到的都是 queryset 对象
    # 将数据传递给前端页面展示给用户看
    return render(request, 'userlist.html', locals())

def edit_user(request):
    # 1.如何获取用户想要编辑的数据
    edit_id = request.GET.get('edit_id')
    if request.method == 'POST':
        # 将用户新修改的所有的数据
        username = request.POST.get('username')
        password = request.POST.get('password')
        """去数据库中修改对应的数据"""
        #方式一:
        models.Userinfo.objects.filter(pk=edit_id).update(username=username, password=password)  #批量更新
        """update方法会将filter查询出来的queryset对象中所有数据对象全部更新"""

        #方式二: 获取当前数据对象,然后利用点属性的方式,先修改数据,然后调用对象方法保存
        #不推荐使用第二种方式,效率低,因为会挨个重新一遍
        # edit_obj = models.Userinfo.objects.filter(pk=edit_id).first()  #pk能够帮你查询出当前表的主键字段名
        # edit_obj.username = username
        # edit_obj.password = password
        # edit_obj.save()
    #2.根据主键值去数据库中查询出当前对象,展示给客户看
    edit_obj = models.Userinfo.objects.filter(pk=edit_id).first()
    # 3.将查询出来的数据对象传递给前端页面,展示给用户看
    return render(request, 'edit_user.html', locals())

def delete_user(request):
    # 1.获取想要删除的数据id,直接删除
    delete_id = request.GET.get('delete_id')
    models.Userinfo.objects.filter(pk=delete_id).delete() #批量删除
    return redirect('/userlist')


#####userlist.py里拼接客户端用户id############ get请求 /?.....
<a href="/edit_user/?edit_id={{ user_obj.id }}" class="btn btn-primary btn-sm">编辑</a>
                                <a href="/delete_user/?delete_id={{ user_obj.id }}" class="btn btn-danger btn-sm">删除</a>
2. orm 表关系创建

from django.db import models

# Create your models here.

# orm 表关系创建

class Book(models.Model):
    title = models.CharField(max_length=32)
    #总共八位,小数占两位
    price = models.DecimalField(max_digits=8, decimal_places=2)

    # 书和出版社是一对多的关系,外键字段在多的一方
    # to指定跟谁是外键关联 默认关联的是表的主键字段
    publish_id = models.ForeignKey(to='Publish')
    """ForeignKey字段  django orm 在创建表的时候,会自动给该字段添加 _id 后缀"""

    # 书和作者是多对多的关系,外键建在任何一方都可以,但是推荐建在查询频率比较高的一方
    authors = models.ManyToManyField(to='Author')
    '''
    authors字段仅仅是一个虚拟字段,不会再表中展现出来,仅仅是用来告诉 django orm 书籍和作者表示多对多的关系
    自动创建第三种表
    '''
class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=255)


class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.BigIntegerField()

    # 一对一字段,表关系建在哪张表都可以,但是推荐建在使用频率比较高的那张表
    author_detail = models.OneToOneField(to='AuthorDetail') ## to指定跟谁是外键关联 默认关联的是表的主键字段
    '''
    OneToOneField字段  django orm 在创建表的时候,会自动给该字段添加 _id后缀
    '''
class AuthorDetail(models.Model):
    addr = models.CharField(max_length=255)
    age = models.IntegerField()
  1. django 请求生命周期流程图

django请求流程MTV

5. 路由匹配即--->路由匹配
url()方法 第一个参数就是一个正则表达式,一旦前面的正则匹配到了内容,就不会继续放下匹配,而是直接执行对应的视图函数
django在理由匹配的时候,当你在浏览器中没有敲最后的斜杠,首先 django 会拿个没有敲斜杠的结果去匹配,如果没有匹配上,会让浏览器在末尾加斜杠再发一次请求,再去匹配一次,如果还是匹配不上才会报错

如果你想取消该机制,不想做二次判断可以在 settings 配置文件中设置:
APPEND_SLASH = False #该参数默认是True

简单路由配置
url(r'^index/$', views.index)
	第一个参数是正则表达式,第二个参数是视图函数(跳转到哪个视图函数)
	每个正则表达式前面的 'r' 是可选的, 但是建议加上,取消转义

url(r'index/', views.index) 
如果不加上^和$,由于路由匹配是从上到下的执行,若是输入其他路径中包含index,也会被匹配到的

无名分组

url(r'^index/(\d+)/', views.index) #一定要记得加括号

def index(request,xxx) 或者 def index(request, *args)
	return ...

客户端输入: /index/12....(数字)
	
按位置传参
()表示分组, 路由匹配的时候,会将()正则表达式匹配到的内容当做位置参数传递给视图函数
views视图函数必须要位置参数接收分组出来的数据,这个参数名可以随便取(这里用xxx接收(\d+))
当分组出来的数据很多时,也可以用*args用一个元组接收所有数据

有名分组

url(r'^index/(?P<year>\d+)/', views.index) #大写P

def index(request, year)
	return ...

客户端输入: /index/12....(数字)

按关键字传参
路由匹配的时候,会将括号内的正则表达式匹配到的内容,当做关键字参数传递给视图函数

注意事项:views视图函数接收的参数名必须要和url传递的参数名一致,不然就会报错。(year接收url传递的数字)

注意:有名分组和无名分组不能混合使用,但是在同一种分组下,是可以使用多个的

无名分组支持多个
url(r'^test/(\d+)/(\d+)/', views.test),

有名分组支持多个
url(r'^test/(?P<year>\d+)/(?P<xxx>\d+)/', views.test)

反向解析

本质:其实就是给你返回一个能够访问对应url的地址

1.先给url 和 视图函数对应关系起别名(name就是起别名)

url(r'text/', views.text, name='aaa')

2.反向解析

​ 后端反向解析 视图层

​ 后端可以在任意位置通过 reverse 反向解析出对应的 url(就是通过起别名name得出对应的url)

from django.shortcuts import render, HttpResponse, redirect, reverse

#print(reverse('aaa'))  # aaa是别名

def test(request):
    print(reverse('aaa'))  #testadd
    return render(request, 'login.html')

前端反向解析  模板层
<a href="{% url 'aaa' %}">1</a>

无名分组反向解析(无名分组+反向解析)

也是先起一个别名 name

url(r'^index/(\d+)/$', views.index, name='kkk')

后端反向解析 视图层

reverse('kkk', args=(1,))  # 后面的数字通常是数据的id值

前端反向解析 模板层

{% url 'kkk' 1 %} #后面的数字通常是数据的id值

有名分组的反向解析

跟我命分组反向解析一样的用法

先起别名

url(r'^index/?P<year>\d+)/', views.index, name='kkk')

后端反向解析

方法一
print(reverse('kkk', args=(1,))) #推荐使用这种方法
方法二
print(reverse('kkk', kwargs={'year': 1}))

前端反向解析

方式一
<a href="{% url 'kkk' 1 %}">1</a>  #推荐使用这种
方式二
<a href="{% url 'kkk' year=1 %}">1</a>

注意事项:在同一应用下,别名千万不能重复(*****)

路由分发 include(针对存在多个app)

当你的 django 项目特别庞大的时候,路由与视图函数对应关系特别多,那么你的总路由urls.py代码太过冗余,不易维护

所以:每一个应用都可以有自己的 urls.py, static文件夹, templates文件夹(**)

正是基于上述条件,可以实现多人分组开发,等多人开发完成后,只需要创建一个空的 django香蜜。然后将多人开发的 app 全部注册进来,在总路由实现一个路由分发,而不再做路由匹配(来了之后,我只给你分发到对应的app中)

from django.conf.urls import url, include
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls')),
    url(r'^app02/', include('app02.urls')),
]
总路由中,以及路由后面千万不要加$符号
名称空间(了解)

多个app起了相同的别名,这个时候用反向解析,并不会自动识别应用前缀

有两种方式:

方式1:

  总路由

url(r'^app01/',include('app01.urls',namespace='app01'))
url(r'^app02/',include('app02.urls',namespace='app02'))
后端解析的时候

reverse('app01:index')  #名称空间:别名
reverse('app02:index')
前端解析的时候

{% url 'app01:index' %}  #名称空间:别名
{% url 'app02:index' %}
方式2:

起别名的时候不要冲突,一般情况下在起别名的时候通常建议以应用名作为前缀

  name ='app01_index'

伪静态

复制代码
静态网页:数据是写死的 万年不变
        
伪静态网页的设计是为了增加百度等搜索引擎seo查询力度
        
所有的搜索引擎其实都是一个巨大的爬虫程序
        
网站优化相关  通过伪静态确实可以提高你的网站被查询出来的概率
但是再怎么优化也抵不过RMB玩家
复制代码
#路由,用html结尾
url(r'^book.html/',views.book)
虚拟环境

一般情况下 我们会给每一个项目 配备该项目所需要的模块 不需要的一概不装
虚拟环境 就类似于为每个项目量身定做的解释器环境
        
        
如何创建虚拟环境
  每创建一个虚拟环境 就类似于你又下载了一个全新的python解释器
django版本的区别

复制代码
django1.X跟django2.X版本区别
     路由层1.X用的是url
     而2.X用的是path
            
     2.X中的path第一个参数不再是正则表达式,而是写什么就匹配什么 是精准匹配
            
     当你使用2.X不习惯的时候  2.X还有一个叫re_path
     2.x中的re_path就是你1.X的url
复制代码
复制代码
虽然2.X中path不支持正则表达式  但是它提供了五种默认的转换器
        
            1.0版本的url和2.0版本的re_path分组出来的数据都是字符串类型
            默认有五个转换器,感兴趣的自己可以课下去试一下
            str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
            int,匹配正整数,包含0。
            slug,匹配字母、数字以及横杠、下划线组成的字符串。
            uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
            path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
  path('index/<int:id>/',index)  # 会将id匹配到的内容自动转换成整型
复制代码
复制代码
还支持自定义转换器
            class FourDigitYearConverter:  
            regex = '[0-9]{4}'  
            def to_python(self, value):  
                return int(value)  
            def to_url(self, value):  
                return '%04d' % value  占四位,不够用0填满,超了则就按超了的位数来!
            register_converter(FourDigitYearConverter, 'yyyy')  
            
            urlpatterns = [  
                    path('articles/2003/', views.special_case_2003),  
                    path('articles/<yyyy:year>/', views.year_archive),  
                    ...  ]