django基础和进阶操作
Django框架
- 
python三大主流web框架 - 
django - 
特点:大而全,自带功能十分的多 
- 
缺点:过于笨重。 
 
- 
- 
flask: - 
特点:小而精,自带功能特别少,但是第三方模块比较多。 
- 
缺点:比较依赖第三方的开发者。 
 
- 
- 
tornado - 
特点:异步非阻塞,支持高并发,甚至可以开发游戏服务器 
- 
缺点:我不会。 
 
- 
 
- 
注意事项
# 如何然你的计算机能够正常的启动django项目
1.计算机的名称不能有中文
2.一个pycharm窗口只开一个项目
3.项目里面所有的文件尽量不要出现中文
4.python解释器尽量使用3.4-3.6之间的版本
5.如果项目报错那就去源码把逗号删掉
# django版本问题
pip install django==1.11.11
Django基本操作
命令行操作
- 
创建django项目(django-admin startproject 项目名) - 
django-admin startproject web_server 
 
- 
- 
启动django项目(python manage.py runserver),这条指令后面可以跟ip和端口。如果不跟就默认是127.0.0.1:8000 - 
python manage.py runserver 
- 
注意在启动manage.py之前需要去到他的文件夹下面去 
- 
使用cd web_server,可以使用ls查看文件夹下面的文件。 
 
- 
- 
创建应用(python manage.py startapp 名称) - 
python manage.py startapp app01 
- 
创建完应用以后需要去配置文件中注册 
 
- 
# 在settings文件中的INSTALLED_APPS中写"app01.apps.App01Config",也可以简写app01
- 
应用:django框架是一款专门开发app的web框架 
- 
app名称应该见名知意 
- 
修改端口号以及创建server 
使用pycharm创建django项目
- 
newproject选择左侧第二个django 
django项目主要文件介绍
- 
web_server文件夹 - 
wen_server文件夹 - 
settings.py(项目配置文件) 
- 
urls.py(是路由与视图函数对应关系) 
- 
wsgi.py (wsgiref模块) 
 
- 
- 
manage.py (是django的入口文件) 
- 
db.sqlite3 (django自带的sqlite3数据库,小型数据库功能不是很多,还有很多bug) 
- 
app01文件夹 - 
admin.py(django的后台管理) 
- 
apps.py (注册使用) 
- 
migrations文件夹 (数据库迁移记录) 
- 
models.py(数据库相关的 模型类(orm)) 
- 
tests.py(测试文件) 
- 
views.py(是一个 试图函数) 
 
- 
 
- 
命令行和pycharm创建的区别
- 
使用命令行创建不会自动成templates文件夹,需要自己手动创建,但是pycharm会自动帮你创建并且还会自动在配置文件中配置对应的路径。 
- 
使用命令行创建出来的文件里面没有自动配置路径,使用pycharm创建出来的文件会自动创建路径。 
# 命令行创建出来的文件
'DIRS': [],
# pycharm创建出来的文件
'DIRS': [BASE_DIR, '/templates']
django小白必会三板斧
"""
HttpResponse:使用来返回字符串
    # return HttpResponse("你好,我是django妹纸")
    
render:返回html页面
    # return render(request, "01.html")
redirect:
    # return redirect("https://www.bilibili.com/")
"""
静态文件配置
"""
我们将html文件默认都放在templates文件夹下面
我们将网站使用的静态文件都默认放在static文件夹下面
静态文件
    前端已经写好了的,能过直接使用的文件。
    网站写好的js文件、css文件、网站使用到的图片第三方前端框架等等。
"""
# django默认是不会自动帮你创建static文件夹,需要自己手动创建
"""
一般情况下我们会在static文件夹下面还会进一步的划分处理
static
    js
    css
    img
    其他第三方文件
"""
"""
在浏览器中输入url能够看到 对应的资源是因为后端提前开设了该资源的接口
反过来说如果后端没有开设这个接口,那么就会访问不到资源
静态文件如果没有配置,也会访问不到。
"""
# django项目问题
"""
当你写django项目的时候可能会出现后端修改代码,但是前端页面没变化的情况
    1.你开了好几个django项目,一直再跑的是第一个django项目
    这个是因为端口号占用
    
    2.浏览器缓存问题
        settings
            network
                disable cache勾选上,这样就会清除缓存
"""
# 静态文件配置
    STATIC_URL = '/static/'   # 类似于访问静态文件的令牌
    """
    如果想要访问静态文件就需要以static开头
    """
    # 静态文件配置
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static')
    ]
    """静态文件查找顺序是从上往下查找,如果有相同的文件可能会出现替换的情况,静态文件可以配置多个"""
    
# 动态获取静态文件令牌
{% load static %}  # 将static模块导进来
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    
"""
from表单action参数
    不写就是默认向当前的url提交数据
    全写就会向写的地址提交参数
    只写后缀
"""
# 在前期我们使用django提交post请求的时候,需要去配置文件中注释掉一行代码
    """
    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',
]
    """
request方法
# request.method获取当前请求方式
# request.POST 获取用户提交的post数据(不包含文件)
    """
    request.POST.get() 会获取列表里面最后一个元素
    request.POST.getlist()会将一个列表完整的拿出来
    """
# request.GET获取用户提交的get数据
    """
    request.GET.get()
    request.GET.getlist()
    和post请求的用法一模一样
    """
def login(request):
    """
    get请求和post请求应该有不同的处理机制
    :param request:
    :return:
    """
    print(request.method)   # 获取当前的请求方式,返回的是全大写的字符串形式的数据
    return render(request, 'login.html')
def login(request):
    if request.method =="POST":
        return HttpResponse("收到了 宝贝")
    elif request.method =="GET":
        return render(request, "login.html")
def login(request):
    if request.method == "POST":
        return HttpResponse("收到了 宝贝")
    return render(reuqest, "login.html")
request method
request.method
request.POST
request.GET
request.FILES
request.path  # only get url
request.path_info # only get url
request.get_full_path()  # can get url and url behind parameter(参数)
request.body # 获取原生的浏览器发过来的数据
django连接数据库(mysql)
# django默认是连接sqlite3
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
# django连接mysql数据库
1.先要配置文件中的配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'web_serve',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'CHARSET': 'utf8'
    }
}
2.代码声明
    """
    django默认的使用mysqldb连接mysql数据库
    但是,mysqldb的兼容性不好,所以需要手动改成使用pymysql连接
    所以我们需要告诉django不要使用mysqldb来连接数据库们要改成pymsql来连接数据库
    """
    # 在项目名下面的init或者任意名下的init文件中书写以下代码都可以。
    import pymysql
    pymysql.install_as_MySQLdb()
    
"""
如果使用pycharm连接不了sqllite3,那么你需要下载sqllite3的驱动。
"""
djangoROM
"""
ORM对象关系映射
作用:是让不会sql语句的小白也能使用python代码快捷的操作数据库
不足之处:封装程度太高,有时候sql语句的效率太低,需要自己写sql语句
    类                    表
    对象                  记录
    对象属性               记录某个字段对应的值
"""
# 先去models.py中写一个类
class User(models.Model):
    # id int primary_key auto_increment
    id = models.AutoField(primary_key=True)
    # username varchar(32)
    username = models.CharField(max_length=32)
    # password int
    password = models.IntegerField()
    
#数据库迁移命令
    # 将操作记录记录在migrations文件夹中
    python manage.py makemigrations
    # 将操作同步到数据库中
    python manage.py migrate
    # 只要修改了models.py中和数据库相关的代码就需要重新执行上诉两条命令。
利用ROM实现数据的增删改查
字段的增删改查
# 字段的增加
    """
    1.可以在终端内直接给出默认值
    2.null=True字段可以为空
    info = models.CharField(max_length=50,verbose_name='个人简介', null=True)
    3.default="默认值"
    hobby = models.CharField(max_length=20,verbose_name="兴趣爱好",default="study") 
    """
数据的增删改查
# 查找数据
username = request.POST.get("username")
password = request.POST.get("password")
# print(password, username)
# select * from app01_user where username="egen";
info = models.User.objects.filter(username=username)
"""
filter里面可以携带多个参数,如果写多个参数那么参数与参数之间就会默认以and的形式连接
可以把filter看成是sql语句中的where。
"""
# <QuerySet [<User: User object>]> 拿到的是列表套数据对象
print(info) # 可以索引取值,但是不支持负数索引取值
info.first() # 取里面第一个元素 
print(info[0].username)
print(info[0].password)
    
# 增加数据
第一种方式
    data = models.User.objects.create(username=username, password=password)
第二种方式
    data = models.User(username=username, password=password)
    data.save()  # 保存数据
    
数据的删改查
# 将数据库的数据展示到前端,然后给每一个数据两个按钮,一个编辑,一个删除。
# 编辑功能
    # 点击编辑按钮,然后从后端发送编辑数据的请求。
    """
    如何告诉后端用户想要编辑那条数据?
        将编辑按钮所在的那一行主键值发送给后端
        利用url问号后面的携带参数的方式将主键值传给后端
    """
def user_lis(request):
    # 取出全部数据
    # user_list = models.User.objects.filter()
    # 取出全部数据
    user_list = models.User.objects.all()
    print(user_list)
    return render(request, "userlis.html", locals())
# models.User.objects.filter(id=user_id).update(username=username, password=password)
def edit_user(request):
    user_id = request.GET.get("user_id")
    if request.method == "GET":
        return render(request, "edit_user.html")
    # 方式1:
    username = request.POST.get("username")
    password = request.POST.get("password")
    models.User.objects.filter(id=user_id).update(username=username, password=password)
    """批量操作,速度比较快"""
    # 方式2:
    # data = models.User.objects.filter(id=user_id).first()
    # data.username = username
    # data.password = password
    # data.save()
    """速度比较慢,会将查询出来的数据全部改一遍"""
    return redirect("/userlis/")
# data = models.User.objects.filter(id=user_id).delete()
def remove(request):
    user_id = request.GET.get("user_id")
    data = models.User.objects.filter(id=user_id).delete()
    return redirect("/userlis/")
djangoORM创建表关系
"""
表和表之间的关系
一对一
多对一
多对多
"""
# 创建表关系 先将基表创建出来,然后在添加外键字段字段
class Book(models.Model):
    title = models.CharField(max_length=32)
    # max_digits=8表示最大长度8位,decimal_places=2表示小数点后面占两位。
    price = models.DecimalField(max_digits=8, decimal_places=2)
    # 表示将publish设置成为外键,和Publish表做关联,如果不写to_fields就会默认和主键字段做关联
    publish = models.ForeignKey(to="Publish")
    """如果字段对应的是foreign key 那么ORM会自动的在字段后面加上_id
    就算你在外键上面加上_id ORM依然会在后面加上_id。一对多的话外键字段建在多的一方"""
    # 图书和作者是多对多的关系,外键字段建在任意一方即可,但是推荐建在查询频率比较高的表
    authors = models.ManyToManyField(to="Author")
    """
    authors是一个虚拟字段,主要是告诉ORM书籍表和作者表的多对多的关系
    让ORM帮你创建第三张表
    """
class Publish(models.Model):
    name = models.CharField(max_length=32, verbose_name="出版社名称")
    addr = models.CharField(max_length=64, verbose_name="出版社地址")
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者名字")
    age = models.IntegerField(verbose_name="作者年龄")
    # 一对一关系创建外键字段可以建在任意一方,但是推荐建立在查询频率比较高的表中
    author_detail = models.OneToOneField(to="AuthorDetail")
    """也会在后面给你加_id"""
class AuthorDetail(models.Model):
    addr = models.CharField(max_length=64, verbose_name="作者地址")
    phone = models.BigIntegerField()  # 直接写字符串类型也行
"""
在django1.X中外键都是级联更新级联删除的。
在django2.x和3.x中就需要设置级联更新和级联删除
on_delete=models.CASCADE
"""
django生命周期请求流程图
# 扩展知识点
"""
缓存数据库
    提前已经将你想要的 数据准备好了 来了就可以直接拿
    这样就可以提高效率和响应时间
"""
路由层
# 路由匹配
url(r"test",views.test),
url(r"testadd",views.testadd),
"""
url第一个参数是正则表达式,
    只要第一个参数正则表达式能过匹配到内容,那么就会立刻停止往下匹配,
    直接执行对应的视图函数。
    
你在输入url的时候会自动的加上/,是因为django自动的帮你做了重新向,,匹配一次不行那就加上/在匹配一次
    
    取消自动加/
    APPEND_SLASH = False
"""
无名分组和有名分组
# 无名分组
url(r'^test/(\d+)/', views.test),
def test(request, aa):
    print(aa)
    return HttpResponse("test")
"""
无名分组就是将括号内正则表达式匹配到的位置参数当作位置参数传递给后面的视图函数
"""
# 有名分组
url(r'^testadd/(?P<year>\d+)/', views.testadd),
def testadd(request, year):
    print(year)
    return HttpResponse("testadd")
"""
又名分组就是将括号内正则表达式匹配到的内容当作关键字参数传给后面的视图函数
"""
"""
无名分组和有名分组能不能混合使用?
    不能混用,但是同一个分组可以使用N多次。
"""
反向解析
"""
反向解析的本质:
    通过一些方法得到一个结果,该结果可以访问到对应的url从而触发视图函数的运行
"""
# 最简单的情况url第一个参数里面没有正则符号
url(r'^func/', views.func, name='ooo')
# 两种解析的情况
    """
    前端
        {% url 'ooo'%}
        
    后端
        需要reverse方法
        from django.shortcuts import
        print(reverse('ooo'))
        别名不能冲突
        
    """
# 后端
reverse("ooo", args(1,))
# 前端
{% url"ooo" 1 %}
路由分发
"""
django的每一个应用都可以有自己的templates文件夹urls.py和static文件夹。
正是基于上诉的特点,django能够很好的做到分组开发(每个人只写自己的app)
当一个django项目里面的app特别的多的时候,总路由urls.py代码非常不好维护,这时候也可以利用路由分发来减轻总路由的压力。
利用路由分发以后,总路由不再干路由与视图的直接对应关系,而是做一个分发处理,识别当前的url是哪一个app下的,在分发给对应的app去做处理
"""
# 总路由
    from django.conf.urls import url, include
from django.contrib import admin
# from ..app02 import urls as app02_urls
# from ..app01 import urls as app01_urls
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 路由分发
    # url('^app01/', include(app01_urls)),  # 只要url前缀是app01的都交给app01_urls处理
    # url('^app02/', include(app02_urls)),  # 只要url前缀是app01的都交给app01_urls处理
    # 路由分发终极写法
    url(r'^app01/', include('app01.urls')),
    url(r'^app02/', include('app02.urls')),
]
# 子路由
    from django.conf.urls import url
import views
urlpatterns = [
]
名称空间
"""
当多个应用出现了相同的别名,在正常情况下,反向解析是不会自动识别前缀的,因此在不同的应用里面出现相同的名字时,逆向解析是无法解析出相对应的url的。
在面对这种情况时,我们可是使用名称kon'jian
"""
# namespace 创建名称空间
    url(r'^app01/', include('app01.urls', namespace='app01')),
    url(r'^app02/', include('app02.urls', namespace='app02')),
reverse('app02:reg')
{% url 'app02:reg' %}
# 一般情况下,多个app之间在取别名的时候,会加上app的前缀,
# 这样的话会保证app之间的别名不会起冲突。
伪静态页面
"""
静态页面
    静态页面就是写死的万年不变的
伪静态
    将一个动态页面伪装成为静态页面
为什么要将一个动态页面伪装成为一个静态页面呢?
    伪装的目的在于增大本网站的seo查询力度
    并且增加搜索引擎搜索本网站的概率(如果网页是一个静态页面,那么搜索引擎就会优先把网页收藏起来,等用户访问的时候就可以优先将页面发送过去。)
    伪装静态页面只需要在url后面加上html后缀就行
    url(r'^index.html', views.index),       
    
搜索引擎本质上就是一个巨大的爬虫程序
总结:
    无论你怎么优化,怎么处理,都干不过RMB玩家
"""
虚拟环境
"""
在正常开发中,我们会给每一个项目配备一个独有的开发环境,
在这个环境里面只有该项目使用的模块,其他的模块都不装
虚拟环境
    虚拟环境就类似于一个单独的python解释器环境(你每创建一个虚拟环境就类似于重新下载一个纯净的python解释器)
    但是虚拟环境不要创建太多,是需要消耗内存空间的。
    
拓展:
    每一个项目都需要用到很多模块,并且每一个模块的版本还不一样。
    那我们应该如何安装?
    在开发中我们会给每一个项目配备一个requirements.txt文件
    里面书写该项目需要使用到的模块及版本就可以使用命令一键安装。
    
"""
django版本区别
"""
django1版本路由层使用的是url方法
django2和django3版本使用的是path方法
    url()方法第一个参数是支持正则的
    path()方法第一个参数是不支持正则的,写什么就匹配什么。
    
    如果你不习惯使用path方法django给你提供了re_path方法。
    from django.urls import path, re_path
    想使用url方法就导入from django.conf.urls import url
    
    2版本和3版本里面的re_path就等价于url
虽然path不支持正则,但是他的内部支持五种转换器。
    str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
    int,匹配正整数,包含0。
    slug,匹配字母、数字以及横杠、下划线组成的字符串。
    uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
    path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
"""
# path除了有五个转换器之外还支持自定义转换器(了解)。
class MonthConverter:
    regex = '\d{2}' # 属性名必须是regex
    def to_python(self, value):
        return int(value) # 匹配的regex是两个数字,那么返回的结果也必须是两个数字
    
    def to_url(self, value):
        return value
from django.urls import path, register_converter
from app01.path_converts import MonthConverter
# 先注册转换器
register_converter(MonthConverter,'mon')
from app01 import views
urlpatterns = [
    
]
"""
在1版本里面外键都是级联跟新级联删除的,但是到了2版本和3版本中需要自己手动pei
"""
视图层
"""
HttpResponse
    返回字符串类型
f
    返回html页面 并且在返回html页面之前可以给html页面传值
redirect
    重定向
"""
# 试图函数必须要返回一个HttpResponse对象
# render简单的内部原理
jsonresponse对象
# jsonresponse默认只能序列化字典,如果想要序列化其他数据类型就需要加safe参数
form表单上传文件
"""
form表单上传文件类型数据
    1.method必须指定成post
    2.enctype必须换成formdata
"""
request.POST # 不可以拿到文件数据只能拿到普通的键值对数据
request.FILES # 专门拿文件数据
request.FILES.get('file')  # get file object
FBV与CBV
# 视图函数既可以是函数也可以是类
# FBV
def func(request):
    return HttpResponse('index')
# CBV
    # 路由
    re_path(r'^login/', views.Login.as_view())
    # class类写法
    from django.views import View
    class Login(View):
        def get(self, request):
            return render(request, 'register.html')
        def post(self, request):
            return HttpResponse('post')
"""
FBV和CBV各有千秋
    CBV特点:
        能够根据请求的方式不同直接匹配到对应的方法执行
    内部到底怎么实现的?
    
"""
CBV源码剖析
django模板语法
"""
django模板语法可以传python里面的任何数据类型,甚至可以传递函数名,帮你加括号在后端调用函数,并且将函数的返回值展现到前端页面。但是django模板语法里面的函数不能传递参数。
django模板语法在传值给前端的时候,会自动的判断能不能加括号调用。如果加括号能够调用的话那么就会自动调用。并且把返回值返回给前端。
"""
# 过滤器语法
"""
过滤器就类似于模板语法内置的方法 内置方法
django里面有60多种过滤器
过滤器基本语法
    {{数据|过滤器:参数}}
    例如:
        s = "你有一个好朋友"
        {{s|length}}
"""
# 常见的过滤器
{{s|length}}
b = True
{{b|default:啥也不是}}
default如果b是True那么就取True,如果b是False那么就取啥也不是
# 文件大小
filesizeformat会自动的帮你计算文件的大小
# 日期格式化
import datetime
current_time = datetime.datetime.now()
{{current_time|date:'Y-m-d H:i:s'}}
# 列表切片操作
{{l|slice:'0:4:2'}}
# 切取字符串
{{ info:truncatechars:9 }} # 切取9个字符,包括后面的三个点
# 切取英文单词
{{info:truncatewords:9}} # 切取9个英文单词是不包含后面的三个点的。
# 移除特特定的字符
{{info|cut:' '}}  # 移除空字符
# 拼接操作
{{info|join:'$'}}  # 使用$将列表里面的字符串拼接起来
# add拼接操作(如果是数字和数字的拼接那么就会将两个数字相加,如果是字符串,那么就会将两个字符串拼接起来)
{{info|add:10}}
{{info|add:'你好'}}
# 前端转义
h1 = '<h1>我要转义</h1>'
{{h1|safe}}
# 后端转义传给前端
from django.utils.safestring import make_safe
res = make_safe(h1)
{{res}}
"""
在学了转义以后,你的前端代码不一定要写在前端页面里面,还可以将js代码写在后端里面。
"""
forloop参数
{% for foo in l %}   
    <p>{{ forloop }}</p> 
{% endfor %}
"""
{'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 3, 'revcounter0': 2, 'first': True, 'last': False}
{'parentloop': {}, 'counter0': 1, 'counter': 2, 'revcounter': 2, 'revcounter0': 1, 'first': False, 'last': False}
{'parentloop': {}, 'counter0': 2, 'counter': 3, 'revcounter': 1, 'revcounter0': 0, 'first': False, 'last': True}
"""
if判断语句
b = False
{% if b %}
<p>boby</p>
{% elif True %}
    <p>old boby</p>
{% else %}
    <