Django框架

注意事项

如何让你的计算机能够正常的启动django项目

        1. 计算机的名称不能有中文

        2. 一个pycharm窗口只开一个项目

        3. 项目里面所有的文件也尽量不要出现中文

        4. python解释器尽量使用3.4.-3.6之间的版本

        如果项目报错 点击最后一个报错信息 去源码中把逗号删掉

django版本问题

        1.X 2.X 3.X(直接忽略)

        1.X 和2.X 本身差距也不大 

django安装

        pip 3 install django == 1.11.11

验证是否安装成功的方式

        终端输入django-admin看看有没有反应

django基本操作

命令行操作

        1. 创建django项目

                先切换到对应的盘 然后再创建

                d:

                

                django-admin startproject mysite (项目名)

                        mysite文件夹

                                manage.py

                                mysite 文件夹

                                        __init__.py

                                        settings.py

                                        urls.py

                                        wsgi.py

        2. 启动django项目

                一定要先切换到项目目录下

                python manage.py runserver

                http://127.0.0.1:8000/

        3. 创建应用

                python manage.py start app01

                应用名应该做到见名知意

                        user

                        order

                        web

                        ..

        有很多文件

                

        

pycharm操作

        1. new project 选择左侧第2个django即可

        2. 启动

                1. 还是用命令行启动

                2. 点击绿色小箭头即可

        3. 创建应用

                 1. pycharm 提供的终端输入完整命令

                 2. pycharm tools -> run manage.py task提)

        4. 修改端口号以及创建server

                edit configuration 

应用

django是一款专门用来开发app的web框架

django框架就类似于是一所大学(空壳子)

app就类似于大学里面的各个学院(具体功能的app)

        比如开发淘宝

                订单相关        

                用户相关

                投诉相关

                创建不同的app对应不同的功能

        选课系统

                学生功能

                老师功能

一个app就是一个独立的功能模块

主要文件介绍

-mysite 项目文件夹

        -- mysite 文件夹 

                ---settings.py 项目配置文件

                -- urls.py        路由与视图函数对应关系(路由层)

                -- wsgi.py       wsgiref模块(不考虑)

        -- manage.py         django的入口文件

        -- dbsqlite3        django自带的sqlite3数据库(小型数据库 功能不是很多还有bug)

        -- app01文件夹

                --- admin.py        django后台管理

                --- apps.py          注册使用

                --- migrations文件夹         数据库迁移记录

                --- models.py        数据库相关的 模型类(orm)

                --- tests.py        测试文件

                --- views.py        视图函数 (视图层)

命令行与pycharm创建的区别

1 命令行创建不会自动有template文件夹 需要你自己手动创建而pycharm会自动帮你创建,并且还会自动在配置文件中配置对应的路径

2. pycharm创建

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates']
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

命令行创建

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

静态文件配置

登录功能

我们将html文件默认都放在templates文件夹下

我们将网站所使用的静态文件都放在static文件夹下 

静态文件

        前端已经写好了的 能够直接调用使用的文件

                网站写好的js文件

                网站写好的css文件

                网站用到的图片文件

                第三方前端框架

                ...

                拿来就可以直接使用的

django默认是不会自动创建static文件夹 需要手动创建

一般情况下我们在static文件夹内还会做进一步的划分处理

        -static

                --js

                --css

                --img

                其他第三方文件

"""

在浏览器中输入url能够看到对应的资源

是因为后端提前开设了该资源的接口

如果访问不到资源 说明后端没有开设该资源的接口

"""

静态文件配置

"""

在写django项目的时候 可能会出现后端对吗修改了但是前端没有变化的情况

        1. 在同一个端口开了好几个django项目

                一直在跑的其实是第一个django项目

        2. 浏览器缓存的问题

                settings

                        network

                                disable cache 勾选上

"""

STATIC_URL = '/xxx/' #类似于访问静态文件的令牌
"""
如果你想要访问静态文件 你就必须以static开头
"""

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
import os
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static')
]

静态文件动态解析

        {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.js' %}"></script>

form 表单默认是get请求提交数据

http://127.0.0.1:8000/login/?username=jeffrey&password=1234567

form表单action参数

        1. 不写 默认朝当前所在的url提交数据

        2. 全写 指名道姓

        3. 只写后缀 /login/

在前期使用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方法  返回请求方式 并且是大写的字符串形式  <class 'str'>

request.POST  获取用户POST请求提交的普通数据不包含文件

        request.POST.get()  只获取列表最后一个元素

        request.POST.getlist()  直接将列表取出

request.GET 获取用户提交的GET请求数据

        request.GET.get() 只获取列表最后一个元素

        request.GET.getlist() 直接将列表取出

get请求携带的数据是有大小限制的 大概好像只有4KB左右

而post请求则没有限制

def login(request):
    """
    get请求和post请求应该有不同的处理机制
    :param request: 请求相关的数据对象里面 有很多简易的方法
    :return:
    """
    # 返回一个登录界面
    # print(type(request.method)) # 返回请求方式 并且是全大写的字符串像是 <class 'str'>
    # print('来了 老弟')
    # print(request)
    # if request.method == 'GET':
    #     return render(request,'login.html')
    # elif request.method == 'POST':
    #     return HttpResponse('收到了 宝宝')
    if request.method =='POST':
        return HttpResponse('收到了 宝宝')
    return render(request,'login.html')

pycharm连接数据库(MySQL)

三个位置查找数据库相关

        右侧上方database

        左下方database

        配置里面的plugins插件搜索安装

        

        再没有卸载pycharm重新装

pycharm可以充当很多款数据库软件的客户端 

需要提前创建好库

django连接数据库

默认用的是sqlite3

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

django连接mysql

        1. 第一步配置文件中配置

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'day60',
        'USER':'root',
        'PASSWORD':'123456',
        'HOST':'127.0.0.1',
        'PORT':3306,
        'CHARSET':'utf8'
    }
}

      2. 代码声明

        django默认用的是mysqldb模块连接mysql

        但是该模块的兼容性不好 需要手动改为用pymysql连接

        需要告诉django不要用默认的mysqldb而是用pymysql

        在项目名下的init或者任意的应用名下的init文件中书写以下代码都可以

django ORM

orm 对象关系映射

作用: 能够让一个不会sql语句的小白也能够通过python面向对象对象的代码简单快捷的操作数据库

不足之处: 封装程度太高 有时候sql语句的效率偏低 需要自己写sql语句

类                        表

对象                    记录

对象属性             记录某个字段对应的值

应用下面的models.py文件

1. 先去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()
    

2. 数据库迁移命令

python manage.py makemigrations 将操作记录记录到小本本上(migrations文件夹)
python manage.py migrate 将操作真正的同步到数据库中

只要修改了models.py中跟数据库相关的代码

class User(models.Model):
    # id int primary_key auto_increment
    id = models.AutoField(primary_key=True,verbose_name='主键')
    # username varchar(32)
    username = models.CharField(max_length=32,verbose_name='用户名')
    """
    charField必须要指定 max_length参数 不指定会直接报错
    verbose_name 该参数是所有字段都有的 就是用来对字段的解释
    """
    # password int
    password = models.IntegerField(verbose_name='密码')

class Author(models.Model):
    # 由于一张表中必须要有一个主键字段 并且一般情况下都叫id字段
    # 所以orm当你不定义主键id字段的时候,orm会自动帮你创建一个名为id的主键字段
    # 也就意味着 后续我们在创建模型表的时候如果主键字段名没有额外的叫法 那么主键字段可以省略不写
    username = models.CharField(max_length=32)
    password = models.IntegerField()

字段的增删改查

 字段的添加

        1. 当表中已经有数据 可以在终端内直接给出默认值

        2. 该字段可以为空

                

   # 该字段可以为空
    info = models.CharField(max_length=32, verbose_name='个人简介',null=True)
    

        3. 直接给字段设置默认值

       

# 直接给字段设置默认值
    hobby = models.CharField(max_length=32, verbose_name='兴趣爱好',default='study')

字段的修改

        直接修改代码然后执行数据库迁移的两条命令即可

字段的删除

        直接注释对应的字段然后执行数据库迁移的两条命令即可

        执行完毕之后字段对应的数据也都没有了

在操作models.py的时候一定要细心

        千万不要注释一些字段

        执行迁移命令之前最好先检查一些代码

数据的增删改查 

res = models.User.objects.filter(username=username)

返回值先看成是列表套数据对象的格式

它也支持索引取值 切片操作 但是不支持负数索引

它也不推荐你使用索引的方式取值

user_obj = models.User.objects.filter(username=username).first()

filter括号内可以携带多个参数 参数与参数之间默认是and关系

可以把filter联想成where记忆

 第一种增加

res = models.User.objects.create(username=username,password=password)
# 返回值就是当前被创建的对象本身
print(res.username,res.password)

第二种增加

user_obj = models.User(username=username,password=password)
user_obj.save()

先将数据库中的数据全部展示到前端 然后给每一个数据两个按钮 一个编辑一个删除 

查看

def userlist(request):
    # 查询出用户表中所有的数据
    # 方式1
    # data = models.User.objects.filter()
    # print(data)
    # 方式2
    user_queryset = models.User.objects.all()
    # print(data)
    # return render(request,'userlist.html',{'user_queryset':user_queryset})
    return render(request,'userlist.html',locals())

编辑功能

        点击编辑按钮朝后端发送编辑数据的请求

                如何告诉后端用户想要编辑哪条数据?

                        将编辑按钮所在的那一行数据的主键值发送给后端

                        利用ur问号后面携带参数的方式

                    <tbody>
                        {% for user_obj in user_queryset %}
                            <tr>
                                <td>{{ user_obj.id }}</td>
                                <td>{{ user_obj.username }}</td>
                                <td>{{ user_obj.password }}</td>
                                <td>
                                    <a href="/edit_user/?user_id={{ user_obj.id }}" class="btn btn-primary btn-xs">编辑</a>
                                    <a href="" class="btn btn-danger btn-xs">删除</a>
                                </td>
                            </tr>
                        {% endfor %}
                    </tbody>

        后端查询出用户想要编辑的数据对象 展示到前端页面供用户查看和编辑

                        

def edit_user(request):
    print(request.method)
    # 获取url问号后面的参数
    edit_id = request.GET.get('user_id')
    print(edit_id)
    edit_obj = models.User.objects.filter(id=edit_id).first()

    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        print(username)
        print(password)
        # 去数据库中修改对应的数据内容
        # 修改数据方式1

        """
        将filter查询出来的列表中所有的对象全部更新 批量更新操作
        只修改被修改的字段
        """
        # models.User.objects.filter(id= edit_id).update(username=username,password=password)
        # 修改数据方式2
        edit_obj.username = username
        edit_obj.password = password
        edit_obj.save()
        """
            上述方法当字段特别多的时候效率会非常的低
            从头到尾将数据的所有字段全部更新一遍 无论该字段是否被修改
        """
        # 跳转到数据的展示页面
        return redirect('/userlist/')

    # 查询当前用户想要编辑的数据对象
    edit_obj = models.User.objects.filter(id=edit_id).first()
    # 将数据对象展示到页面上
    return render(request,'edit_user.html',locals())

删除功能

def delete_user(request):
    # 获取用户想要删除的数据id值
    delete_id = request.GET.get('user_id')
    # 直接去数据库中找到对应的数据删除即可
    models.User.objects.filter(id=delete_id).delete()
    """
    批量删除
    """
    # 跳转到展示页面
    return  redirect('/userlist/')

真正的删除功能应该二次确认,后面增加

删除数据内部其实并不是真正的删除 给数据添加一个标识字段用来表示当前字段是否被删除了,如果数据被删了仅仅只是将字段修改一个状态

username  password  is_delete

jason             123        1

egon              123        1

django orm如何创建表关系

表与表之间的关系

        一对多

        多对多

        一对一

判断表关系的方法: 换位思考

图书表

出版社表

作者表

作者详情表

图书和出版社是一对多的关系 外键字段建在多的那一方 book

图书和作者是多对多的关系 需要创建第三张表来专门存储

作者与作者详情表是一对一

from django.db import models

# Create your models here.

# 创建表关系 先将基表创建出来 然后再添加外键字段
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    # 小数总共8位 小数点后面占2位


    """
    图书和出版社是一对多 并且书是多的一方 所以外键字段放在书表里面
    """
    publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)  # to_field 不写,默认就是与出版社表的主键字段关联
    """
    如果字段对应的是ForeignKey, 那么orm会自动在字段的后面加_id
    如果你自作聪明的加了_id那么orm还是会在后面继续加_id
    后面在定义 ForeignKey的时候就不要自己加_id了
    """
    """
    图书和作者是多对多的关系 外键字段建在任意一方均可 但是推荐你建在查询频率较高的一方
    """
    authors = models.ManyToManyField(to='Author')
    """
    authors是一个虚拟字段 主要是用来告诉orm书籍表和作者表是多对多的关系
    让orm自动帮你创建第三张关系表
    """


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    """
    作者与作者详情是一对一的关系 外键字段建在任意一方都可以 但是推荐你建在查询频率较高的表中
    """
    author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)
    """
    OneToOneField也会自动给字段加_id后缀
    所以也不要自作聪明的自己加_id
    """

class AuthorDetail(models.Model):
    phone = models.BigIntegerField()  # 或者直接用字符类型
    addr = models.CharField(max_length=32)

orm中如何定义三种关系

publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)  # to_field 不写,默认就是与出版社表的主键字段关联
authors = models.ManyToManyField(to='Author')
author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)

        ForeignKey

        OenToOneField

                会自动在字段后面加-id后缀

在django1.x版本中外键默认都是级联更新级联删除的

多对多的表关系可以有好几种创建方式 这里暂且先介绍一种

针对外键字段里面的其他参数 暂时不考虑

django请求声明周期流程图 

扩展:

        缓存数据库 

                提前已经将你想要的数据准备好了 直接拿就可以

                提高效率和响应时间

        修改数据的时候 数据并不是立刻修改完成的

        而是需要经过一段时间才会修改

                博客园

路由层

路由匹配

url(r'test',views.test),
url(r'testadd',views.testadd)

url方法第一个参数是正则表达式

        只要第一个参数正则表达式能够匹配到内容 那么就会立刻停止往下匹配

        直接执行对应的视图函数

在输入url的时候会默认加斜杠

        django内部自动做的重定向

        一次匹配不行

        url后面加斜杠再来一次

取消自动加斜杠

APPEND_SLASH = False
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    # 首页
    url(r'^$', views.home),
    # 路由匹配
    url(r'^test/$',views.test),
    url(r'^testadd/$',views.testadd),
    # path('test',views.test),
    # path('testadd',views.testadd)
    # 尾页 (了解)
    url(r'',views.error),
]

无名分组 

分组:就是给某一段正则表达式用小括号括起来

    url(r'^test/(\d+)/$',views.test),

def test(request,xx):
    print(xx)
    return HttpResponse('test')

无名分组就是将括号内正则表达式匹配到的内容当做位置参数传递给后面的视图函数

 有名分组

可以给正则表达式起一个别名

url(r'^testadd/(?P<year>\d+)/$', views.testadd),

def testadd(request,year):
    print(year)
    return HttpResponse('testadd')

有名分组就是将括号内正则表达式匹配到的内容当做关键字参数传递给后面的视图函数

无名有名是否可以混合使用 

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

不能混用

单个的分组可以使用多次

url(r'^index/(\d+)/(\d+)/(\d+)/$',views.index),
url(r'^index/(?P<year>\d+)/(?P<age>\d+)/(?P<month>\d+)/$',views.index),

反向解析

通过一些方法得到一个结果 该结果可以直接访问对应的url触发视图函数

先给路由与视图函数起一个别名

url(r'^func_kddd/',views.func,name='ooo'),

反向解析

        后端反向解析

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

reverse('ooo')

        前端反向解析

<a href="{% url 'ooo' %}">111</a>

 无名有名分组反向解析

无名分组反向解析

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

前端

{% url 'xxx' 123 %}

后端

reverse('xxx',args=(1,))

这个数字写代码的时候应该放什么

        数字一般情况下放的是数据的主键值 数据的编辑和删除

url(r'^edit/(\d+)',views.edit,name='xxx')

def edit(request,edit_id):
    reverse('xxx',args=(edit_id,))
{% for user_obj in user_queryset%}
    <a href='{% url "xxx" user_obj.id%}'>编辑 </a>
{% endfor %}

利用无名有名反向解析完成数据的增删改查

有名分组反向解析

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

前端


<a href="{% url 'ooo' year=123 %}">222</a> #了解
<a href="{% url 'xxx' 123 %}">111</a> #记忆

后端

print(reverse('ooo',kwargs={'year':123}))
#简便的写法
print(reverse('ooo',args=(111,)))

路由分发

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

正是基于上述的特点,django能够非常好的做到分组开发(每个人只写自己的app)

作为组长 只需要将组员书写的app全部拷贝到一个新的django项目中 然后再配置文件里面注册所有的app

再利用路由分发的特点将所有的app整合起来

当一个django项目中的url特别多的时候 总路由urls.py代码非常冗余不好维护

这个时候也可以利用路由分发来减轻总路由的压力

利用路由分发之后 总路由不再干路由与视图函数的直接对应关系

而是做一个分发处理

        识别当前url是属于哪个应用下的 直接分发给对应的应用去处理

总路由

# from app01 import urls as app01_urls
# from app02 import urls as app02_urls

urlpatterns = [
    path('admin/', admin.site.urls),
    # 首页
    # url(r'^$',views.home),
    # # 无名分组反向解析
    # url(r'^index/(\d+)',views.index,name = 'xxx'),
    # # 有名分组反向解析
    # url(r'^func/(?P<year>\d+)',views.func,name='ooo')
    # 路由分发
    # re_path(r'^app01/',include(app01_urls)), #只要url前缀是app01打头,全部交给app01处理
    # re_path(r'^app02/',include(app02_urls)), #只要url前缀是app02打头,全部交给app01处理
    # 终极写法 推荐使用
    re_path(r'^app01/',include('app01.urls')),
    re_path(r'^app02/',include('app02.urls'))
    # 注意事项: 总路由里面的url千万不能加$结尾
]

子路由

# app01 urls.py
from django.urls import re_path
from app01 import views

urlpatterns = [
    re_path('^reg/',views.reg),
]

# app02 urls.py

from app02 import views
from django.urls import re_path

urlpatterns = [
    re_path('^reg/',views.reg)
]

名称空间 (了解)

当多个应用出现了相同的别名 我们研究反向解析会不会自动识别应用前缀

正常情况下的反向解析是没有办法自动识别前缀的

名称空间

        总路由

    re_path(r'^app01/',include(('app01.urls','app01'),namespace='app01')),
    re_path(r'^app02/',include(('app02.urls','app02'),namespace='app02')),

        子路由

urlpatterns = [
    re_path('^reg/',views.reg,name = 'reg'),
]

        解析的时候

    print(reverse('app01:reg'))
{% url 'app01:reg' %}
{% url 'app02:reg' %}

其实只要保证名字不冲突就没有必要使用名称空间

一般情况下有多个app的时候我们在起别名的时候会加上app的前缀

这样的话就能够确保多个app之间名字不冲突的问题

urlpatterns = [
    re_path('^reg/',views.reg,name = 'app01_reg'),
]
urlpatterns = [
    re_path('^reg/',views.reg,name = 'app02_reg'),
]

伪静态(了解)

 静态网页

        数据是写死的 万年不变

伪静态

        将一个动态网页伪装成静态网页

        为什么要伪装呢?

                伪装的目的在于增大本网站的seo查询力度

                并且增加搜索引擎收藏本网站的概率

       

        搜索引擎本质上就是一个巨大的爬虫程序

       

        总结:

                  无论你怎么优化        怎么处理

                  始终还是干不过RMB玩家        

虚拟环境(了解)

在正常开发中 我们会给每一个项目配备一个该项目独有的解释器环境

该环境内只有该项目用到的模块 用不到的一概不装

linux: 缺什么装什么

虚拟环境

        你每创建一个虚拟环境就类似于重新下载了一个纯净的python解释器

        但是虚拟环境不要创建太多,是需要消耗硬盘空间的

扩展:

        每一个项目都需要用到很多模块 并且每个模块版本可能还不一样

        那我该如何安装呢?

        

        开发当中,我们会给每一个项目配备一个requirements.txt文件

        里面书写了该项目所有的模块及版本

        你只需要直接输入一条命令即可一键安装所有模块及版本

django版本区别

django 1.x路由层使用的是url方法

而在django2.x 和3.x版本中路由层使用的是path方法

url()第一个参数支持正则

path() 第一个参数是不支持正则的 写什么就匹配什么

re_path(r'^app01/', include('app01.urls' )),
re_path(r'^app02/', include('app02.urls' )),

2.x和3.x里面的re_path就等价于1.x里面的url

虽然path不支持正则,但是它的内部支持5种转换器

    path('index/<int:id>/',index)
def index(request,id):
    print(id,type(id))
    return HttpResponse('INDEX')

str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式

int,匹配正整数,包含0。

slug,匹配字母、数字以及横杠、下划线组成的字符串。

uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。

path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)

除了有默认的5个转换器之外 还支持自定义转换器(了解)

class MonthConverter:
    regex='\d{2}' # 属性名必须为regex

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
# 先注册转换器

from django.urls import path,register_converter
from app01.path_converts import MonthConverter

register_converter(MonthConverter,'mon')

from app01 import views


urlpatterns = [
    path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),

]

模型侧里面1.x外键默认都是级联更新级联删除的

但是到了2.x和3.x中需要你自己手动配置参数

视图层

三板斧

HttpResponse

        返回字符串类型

render

        返回html页面 并且在返回浏览器之前还可以给html文件传值

redirect

        重定向

视图函数必须要返回一个HttpResonse对象 正确 研究

render内部原理

JsonResponse对象

json格式的数据有什么用?

        前后端数据交互需要使用到json作为过渡 实现跨语言传输数据

前端序列化

        JSON.stringfy()        json.dumps

        JSON.parse()          json.loads

def ab_json(request):
    user_dict = {'username':'jeffrey好帅啊,我好喜欢','password':'123','hobby':'girl'}
    l =  [111,222,333,444,555]
    # 先转成json格式字符串
    # json_str = json.dumps(user_dict,ensure_ascii=False)
    # 将该字符串返回
    # 读源码掌握用法
    # return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})
    return JsonResponse(l,safe=False)

默认只能序列化字典 序列化其他需要增加safe参数

form表单上传文件及后端如何获取

form表单上传文件类型的数据

        1.method必须指定成post

        2. enctype必须换成form-data

def ab_file(request):
    if request.method == 'POST':
        print(request.POST) #只能获取普通的键值对数据 文件不行
        print(request.FILES) #获取文件数据
        file_obj = request.FILES.get('file') # 文件对象
        print(file_obj.name)
        with open(file_obj.name,'wb') as f:
            for line in file_obj:  # 一行一行读取文件内容
                f.write(line)
    return render(request,'form.html')

request对象方法 

request.method

request.POST

request.GET

request.Files

request.path

request.path_info

request.get_full_path() 能够获取完整的url及问号后面的参数

FBV与CBV 

视图函数既可以是函数也可以是类

def index(request):
        return HttpResponse('index')
from django.views import View

class MyLogin(View):
    def get(self,request):
        return render(request,'form.html')
    def post(self,Request):
        return HttpResponse('post方法')
    re_path(r'^login/',views.MyLogin.as_view())

FBV和CBV各有千秋

CBV的特点

        能够直接根据请求方式的不同直接匹配到对应的方法执行

CBV源码剖析 

突破口在 urls.py

re_path(r'^login/',views.MyLogin.as_view())

模板语法传值 

{{ }}: 变量相关

{% %}: 逻辑相关

    <p>{{ n }}</p>
    <p>{{ f }}</p>
    <p>{{ s }}</p>
    <p>{{ b }}</p>
    <p>{{ l }}</p>
    <p>{{ d }}</p>
    <p>{{ t }}</p>
    <p>{{ se }}</p>
    <p>传递函数名会自动加括号调用 倒是模板语法不支持给函数传额外的参数{{ func }}</p>
    <p>传类名的时候也会自动加括号调用(实例化){{ Myclass }}</p>
    <p>内部能够自动判断出当前的变量名是否可以加括号调用 如果可以就会自动执行 针对的是函数名和类型</p>
    <p>{{ obj }}</p>
    <p>self:{{ obj.get_self }}</p>
    <p>{{ obj.get_func }}</p>
    <p>{{ obj.get_class }}</p>

django模板语法的取值,是固定的格式只能采用"句点符"

过滤器

过滤器就类似于是模板语法内置的 内置方法

django内置有60多个过滤器 了解10个左右

基本语法

{{数据|过滤器:参数}}

转义

前端

    <p>过滤器</p>
    <p>统计长度:{{ s | length }}</p>
    <p>默认值(第一个参数是True就展示第一个参数的值否则展示冒号后面的值): {{ b|default:'啥也不是'}}</p>
    <p>文件大小:{{ file_size|filesizeformat }}</p>
    <p>日期格式化:{{ current_time|date:'Y-m-d H:i:s' }}</p>
    <P>切片操作 {{ l|slice:'0:4:2' }}</P>
    <p>切取字符(包含3个点,3个点算1个字符):{{ info|truncatechars:3 }}</p>
    <p>切取单词(不包含3个点 按照空格切):{{ egl|truncatewords:9 }}</p>
    <p>切取单词(不包含3个点 按照空格切):{{ info|truncatewords:9 }}</p>
    <p>移出特定的字符:{{ msg|cut:' ' }}</p>
    <p>拼接操作:{{ l|join:'$' }}</p>
    <p>拼接操作(加法): {{ n|add:10 }}</p>
    <p>拼接操作(加法): {{ s|add:msg }}</p>
    <p>取消转义{{ hhh|safe }}</p>
    <p>{{ sss|safe }}</p>
    <p>{{ res }}</p>

后端

from django.utils.safestring import mark_safe
res = mark_safe('<h1>欣欣</h1>')

以后在写全栈项目的时候 前端代码不一定非要在前端页面书写

也可以现在后端写好 然后传递给前端页面

标签

for循环

    {% for baby in l %} <!-- for i in range[1,2,3,4,5]-->
        <p>{{ forloop }}</p>
        <p>{{ baby }}</p>
    {% endfor %}

{'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 6, 'revcounter0': 5, 'first': True, 'last': False}

小红

{'parentloop': {}, 'counter0': 1, 'counter': 2, 'revcounter': 5, 'revcounter0': 4, 'first': False, 'last': False}

珊珊

{'parentloop': {}, 'counter0': 2, 'counter': 3, 'revcounter': 4, 'revcounter0': 3, 'first': False, 'last': False}

花花

{'parentloop': {}, 'counter0': 3, 'counter': 4, 'revcounter': 3, 'revcounter0': 2, 'first': False, 'last': False}

茹茹

{'parentloop': {}, 'counter0': 4, 'counter': 5, 'revcounter': 2, 'revcounter0': 1, 'first': False, 'last': False}

敏敏

{'parentloop': {}, 'counter0': 5, 'counter': 6, 'revcounter': 1, 'revcounter0': 0, 'first': False, 'last': True}

欣欣

if 判断

{% if b %}
    <p>baby</p>
{% elif s %}
    <p>都来吧</p>
{% else %}
    <p>老baby</p>

处理字典

{% for foo in d.keys %}
    <p>{{ foo }}</p>
{% endfor %}
<br>
{% for foo in d.values %}
<p>{{ foo }}</p>
{% endfor %}
<br>

{% for foo in d.items %}
    <p>{{ foo }}</p>

{% endfor %}

with起别名

{% with d.hobby.3.info as nb  %}
    <p>{{ nb }}</p>
    在with语法内就可以通过as后面的别名快读的使用前面非常复杂获取数据的方式
{% endwith %}

自定义过滤器,标签,inclusion_tag (过滤器最多只能有2个参数)

先三步走

        1. 在应用下创建一个名字必须叫templatetags的文件夹

        2. 在该文件夹内创建任意名称的py文件  eg: mytag.py

        3. 在该py文件内“必须”先书写下面两句话

                

from django import template
register = template.Library()

自定义过滤器        

@register.filter(name='baby')
def my_sum(v1,v2):
    return v1+v2

使用

{% load mytag %}
<p>{{ n|baby:'111,222,333,444' }}</p>

自定义标签 类似自定义函数

# 自定义标签 (参数可以有多个)
@register.simple_tag(name='plus')
def index(a,b,c,d):
    return '%s-%s-%s-%s'%(a,b,c,d)

使用

{#标签多个参数彼此之间空格隔开#}
<p>{% plus 'jason' 123 123 123 %}</p>

自定义inclusion_tag

内部原理

        先定义一个方法

        在页面上调用该方法 并且可以传值

        该方法会生成一些数据然后传递给一个html页面

        之后将渲染好的结果放到调用的位置

#自定义inclusion_tag
@register.inclusion_tag('left_menu.html')
def left(n):
    data = ['第{}项'.format(i) for i in range(n)]
    # 第一种
    # return {'data':data}
    # 第二种
    return locals() # 将data传递给left_menu.html

left_menu.html

<ul>
    {% for foo in data %}
        <li>{{ foo }}</li>
    {% endfor %}
    
</ul>

index.html 

{% left 5 %}

总结:当html页面某一个地方的页面需要传参数才能够动态的渲染出来,并且在多个页面上都需要使用到该局部 那么就需要将该局部页面做成inclusion_tag形式

模板的继承 

"""
同一个HTML页面想重复的使用大部分样式 只是局部修改
"""

# 继承
{% extends '模板页面名'%}

# 局部修改
	# 1. 你需要先去模板页面中划定可以被修改的区域
	{% block '名字'%}
	模板内容 (666)
	{% endblock %} 
	# 2. 子页面中继承了模板页面之后 就可以根据名字修改
	{% block '名字'%}
	子版内容
	
	子版页面除了可以自己写自己的之外 还可以继续使用模板的内容
	
	{{ block.super }} 666
	{{ block.super }} 666
	{{ block.super }} 666
	{{ block.super }} 666
	{% endblock%}

# 规律: 一般情况下模板页面内至少应该有三块区域
	css
	html
	js
	所有的子页面中都可以有自己独立的css html 和js

# 利用模板的继承能够让你的页面更加的好维护

模板的导入 

{% include '模板文件名'}

单表操作

 django自带的sqlite3数据库对日期格式不是很敏感 处理的时候容易出错
 增

res = models.User.objects.create(name='jason',age=18,register_time='2002-1-21')
print(res)
import datetime
ctime = datetime.datetime.now()
user_obj = models.User(name='egon',age=84,register_time=ctime)
user_obj.save()


 

res = models.User.objects.filter(pk=2).delete()
print(res)

pk会自动查找到当前表的主键字段 指代的就是当前表的主键字段
    用了pk之后 你就不需要指代当前表的主键字段到底叫什么了
        uid
        pid
        sid

user_obj = models.User.objects.filter(pk=1).first()
user_obj.delete()

修改

models.User.objects.filter(pk=4).update(name='egonDSB')

user_obj = models.User.objects.get(pk=4)
user_obj = models.User.objects.filter(pk=6)

get方法返回的直接就是当前数据对象
但是该方法不推荐使用
        一旦数据不存在该方法会直接报错
        而filter则不会
            所以我们还是用filter

user_obj.name = 'egonPPP'
user_obj.save()

  必知必会13条

 # 1.all()  查询所有数据

    # 2.filter()     带有过滤条件的查询
    # 3.get()        直接拿数据对象 但是条件不存在直接报错
    # 4.first()      拿queryset里面第一个元素
    # res = models.User.objects.all().first()
    # print(res)
    # 5.last()
    # res = models.User.objects.all().last()
    # print(res)

    # 6.values()  可以指定获取的数据字段  select name,age from ...     列表套字典
    # res = models.User.objects.values('name','age')  # <QuerySet [{'name': 'jason', 'age': 18}, {'name': 'egonPPP', 'age': 84}]>
    # print(res)
    # 7.values_list()  列表套元祖
    # res = models.User.objects.values_list('name','age')  # <QuerySet [('jason', 18), ('egonPPP', 84)]>
    # print(res)
    # """
    #  # 查看内部封装的sql语句
    #  上述查看sql语句的方式  只能用于queryset对象
    #  只有queryset对象才能够点击query查看内部的sql语句
    #
    # """
    # 8.distinct()  去重
    # res = models.User.objects.values('name','age').distinct()
    # print(res)
    """
    去重一定要是一模一样的数据
    如果带有主键那么肯定不一样 你在往后的查询中一定不要忽略主键
    
    """
    # 9.order_by()
    # res = models.User.objects.order_by('age')  # 默认升序
    # res = models.User.objects.order_by('-age')  # 降序
    #
    # print(res)
    # 10.reverse()  反转的前提是 数据已经排过序了  order_by()
    # res = models.User.objects.all()
    # res1 = models.User.objects.order_by('age').reverse()
    # print(res,res1)

    # 11.count()  统计当前数据的个数
    # res = models.User.objects.count()
    # print(res)
    # 12.exclude()  排除在外
    # res = models.User.objects.exclude(name='jason')
    # print(res)

    # 13.exists()  基本用不到因为数据本身就自带布尔值  返回的是布尔值
    # res = models.User.objects.filter(pk=10).exists()
    # print(res)

测试脚本

当你只是想测试django中的某一个py文件内容 那么你可以不用书写前后端交互的形式
而是直接写一个测试脚本即可脚本代码无论是写在应用下的tests.py还是自己单独开设py文件都可以

 测试环境的准备 去manage.py中拷贝前四行代码 然后自己写两行
 

import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
    import django
    django.setup()
    # 在这个代码块的下面就可以测试django里面的单个py文件了

查看内部sql语句的方式

# 方式1
res = models.User.objects.values_list('name','age')  # <QuerySet [('jason', 18), ('egonPPP', 84)]>
print(res.query)
queryset对象才能够点击query查看内部的sql语句

# 方式2:所有的sql语句都能查看
# 去配置文件中配置一下即可
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

神奇的双下划线查询

# 神奇的双下划线查询
    # 1 年龄大于35岁的数据
    # res = models.User.objects.filter(age__gt=35)
    # print(res)
    # 2 年龄小于35岁的数据
    # res = models.User.objects.filter(age__lt=35)
    # print(res)
    # 大于等于 小于等于
    # res = models.User.objects.filter(age__gte=32)
    # print(res)
    # res = models.User.objects.filter(age__lte=32)
    # print(res)

    # 年龄是18 或者 32 或者40
    # res = models.User.objects.filter(age__in=[18,32,40])
    # print(res)

    # 年龄在18到40岁之间的  首尾都要
    # res = models.User.objects.filter(age__range=[18,40])
    # print(res)

    # 查询出名字里面含有s的数据  模糊查询
    # res = models.User.objects.filter(name__contains='s')
    # print(res)
    #
    # 是否区分大小写  查询出名字里面含有p的数据  区分大小写
    # res = models.User.objects.filter(name__contains='p')
    # print(res)
    # 忽略大小写
    # res = models.User.objects.filter(name__icontains='p')
    # print(res)

    # res = models.User.objects.filter(name__startswith='j')
    # res1 = models.User.objects.filter(name__endswith='j')
    #
    # print(res,res1)


    # 查询出注册时间是 2020 1月
    # res = models.User.objects.filter(register_time__month='1')
    # res = models.User.objects.filter(register_time__year='2020')

一对多外键增删改查 

    # 增
    # 1 直接写实际字段 id
    # models.Book.objects.create(title='论语',price=899.23,publish_id=1)
    # models.Book.objects.create(title='聊斋',price=444.23,publish_id=2)
    # models.Book.objects.create(title='老子',price=333.66,publish_id=1)
    # 2 虚拟字段 对象
    # publish_obj = models.Publish.objects.filter(pk=2).first()
    # models.Book.objects.create(title='红楼梦',price=666.23,publish=publish_obj)
    # 删
    # models.Publish.objects.filter(pk=1).delete() #级联删除

    # 修改
    # models.Book.objects.filter(pk=1).update(publish_id=2)
    # publish_obj = models.Publish.objects.filter(pk=1).first()
    # models.Book.objects.filter(pk=1).update(publish=publish_obj)

多对多外键增删改查

# 如何给书籍添加作者?
    # book_obj = models.Book.objects.filter(pk=2).first()
    # book_obj.authors.add(2,3) # 给书籍id为1的书籍绑定一个主键为1的作者
    # print(book_obj) # 就类似于你已经到了第3张关系表了
    # author_obj = models.Author.objects.filter(pk=1).first()
    # author_obj1 = models.Author.objects.filter(pk=2).first()
    # author_obj2 = models.Author.objects.filter(pk=3).first()
    # book_obj = models.Book.objects.filter(pk=2).first()
    # book_obj.authors.add(author_obj)
    # book_obj.authors.add(author_obj)
    # book_obj.authors.add(author_obj1,author_obj2)
    """
    add给第3张关系表添加数据
        括号内既可以传数字也可以传对象 并且都支持多个
    """

    # 删
    # book_obj = models.Book.objects.filter(pk=2).first()
    # book_obj.authors.remove(1,3)
    # author_obj = models.Author.objects.filter(pk=1).first()
    # author_obj1 = models.Author.objects.filter(pk=2).first()
    # book_obj.authors.remove(author_obj,author_obj1)
    """
    remove给第3张关系表删除数据
        括号内既可以传数字也可以传对象 并且都支持多个
    """

    # 修改 set
    # book_obj = models.Book.objects.filter(pk=1).first()
    # book_obj.authors.set([1,2]) # 括号内必须给一个可迭代对象
    # book_obj.authors.set([3])
    # author_obj = models.Author.objects.filter(pk=2).first()
    # author_obj1 = models.Author.objects.filter(pk=3).first()
    # book_obj.authors.set((author_obj,author_obj1))

    """
    set
        括号内必须传一个可迭代对象 该对象内既可以传数字也可以传对象 并且都支持多个
        先删除后新增
    """

    # 清空
    # 在第3张关系表中清空某个书籍与作者的绑定关系
    # book_obj = models.Book.objects.filter(pk=1).first()
    # book_obj.authors.clear()

    """
    clear
        括号内不要加任何参数
    """

正反向的概念

正向反向

        外键字段在我手上,我查你就是正向

        book >>> book 外键字段在书那儿(正向) >>>publish

        外键字段不在我手上,我查你就是反向

        publish >>> 外键字段在书那儿(反向) >>>book

        一对一和多对多正反向的判断也是如此

正向查询按字段

反向查询按表名小写

        _set

        ...

多表查询 

子查询(基于对象的跨表查询)

联表查询(基于双下划线的跨表查询)

    # 基于对象的跨表查询

    # 1. 查询书籍主键为1的出版社名称
    # book_obj = models.Book.objects.filter(pk=1).first()
    # 书查出版社 正向
    # res = book_obj.publish
    # print(res)
    # print(res.name)
    # print(res.addr)
    # print(res.email)

    # 2. 查询书籍主键为2的作者
    # book_obj = models.Book.objects.filter(pk=2).first()
    # 书查作者 正向
    # res = book_obj.authors #app01.Author.None
    # res = book_obj.authors.all() #<QuerySet [<Author: 对象:jason>, <Author: 对象:egon>]>
    # print(res)

    # 3. 查询作者jason的电话号码
    # author_obj = models.Author.objects.filter(name='jason').first()
    # res = author_obj.author_detail
    # print(res.phone)
    # print(res.addr)

    """
    在书写orm语句的时候跟写sql语句是一样的
    不要企图一次性将orm语句写完 如果比较复杂 就写一点看一点再写一点
    
    正向什么时候需要加.all()
        当你的结果可能有多个的时候需要加.all()
        如果是一个则直接拿到数据对象
            book_obj.publish
            book_obj.authors.all()
            author_obj.author_detail
    """

    # 4. 查询出版社是东方出版社出版的书
    # publish_obj = models.Publish.objects.filter(name='东方出版社').first()
    # 出版社查书 反向
    # res = publish_obj.book_set
    # print(res.all())

    # 5. 查询作者是jason写过的书
    # author_obj = models.Author.objects.filter(name='jason').first()
    # print(author_obj)
    # print(type(author_obj))
    # 作者查书 反向
    # res = author_obj.book_set
    # print(res.all())

    # 6. 查询手机号是110的作者姓名
    # author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
    # res = author_detail_obj.author
    # print(res)
    """
    基于对象
        反向查询的时候
            当你的查询结果可以有多个的时候 就需要加_set.all()
            当你的结果只有一个的时候 不需要加_set.all()
    """

    # 基于双下划线的跨表查询

    # 1. 查询jason的手机号和作者姓名
    # res = models.Author.objects.filter(name='jason').values('author_detail__phone','name')
    # print(res)
    # 反向
    # res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__name') # 拿作者姓名是jason的作者详情
    # print(res)


    # 2. 查询书籍主键为1的出版社名称和书的名称
    # res = models.Book.objects.filter(pk=1).values('publish__name','title')
    # print(res)
    # 反向
    # res = models.Publish.objects.filter(book__id=1).values('name','book__title')
    # print(res)

    # 3. 查询书籍主键为1的作者姓名
    # res = models.Book.objects.filter(pk=2).values('authors__name')
    # print(res)

    # res = models.Author.objects.filter(book__id=2).values('name')
    # print(res)

    # 查询书籍主键是1的作者的手机号
    # book author authordetail
    res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
    print(res)
    """
    你只要掌握了正反向的概念
    以及双下划线
    那么你就可以无限制的跨表
    """

聚合查询(聚合函数的使用)aggregate

    # 聚合查询  aggregate
    from app01 import models
    """
    聚合查询通常情况下都是配合分组一起使用的
    只要是跟数据库相关的模块 
        基本上都在django.db.models里面
        如果上述没有那么应该在django.db里面
    """
    from django.db.models import Max, Min, Sum, Count, Avg
    # 1. 书的平均价格
    # models.Book.objects.all()
    # res = models.Book.objects.aggregate(Avg('price'))
    # print(res)

    # 2. 上述方法一次性使用
    res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Avg('price'),Count('price'))
    print(res)

分组查询(group by的使用)annotate

"""
    MySQL分组查询都有哪些特点
        分组之后默认只能获取到分组的依据 组内其他字段都无法直接获取了
            严格模式
                ONLY_FULL_GROUP_BY
    """

    # 1. 统计每一本书的作者个数
    # res = models.Book.objects.annotate()  # models后面什么 就是按什么分组
    # res = models.Book.objects.annotate(author_num = Count('authors')).values('title','author_num')  # models后面什么 就是按什么分组
    # """
    # author_num 是我们自己定义的字段 用来存储统计出来的每本书对应的作者个数
    # """
    # res1 = models.Book.objects.annotate(author_num = Count('authors__pk')).values('title','author_num')  # models后面什么 就是按什么分组
    # print(res)
    # print(res1)
    """
    代码没有补全 不要怕 正常写
    补全功能是pycharm给你的 到后面在服务器上直接书写代码 什么补全都没有 颜色提示也没有
    """

    # 2. 统计每个出版社卖的最便宜的书的价格

    # res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
    # print(res.query)

    # 3. 统计不止一个作者的图书
        # 1. 先按照图书分组 求每一本书对应的作者个数
        # 2. 过滤出不止一个作者的图书

    # res = models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num').filter(author_num__gt=1)
    # """
    # 只要你的orm语句得出的结果还是一个queryset对象
    # 那么他就可以继续无限制的点queryset对象封装的方法
    # """
    # print(res)

    # 4. 查询每个作者出的书的总价格
    # res = models.Author.objects.annotate(sum_price = Sum('book__price')).values('name','sum_price')
    # print(res)

    """
    如果我想按照指定的字段分组该如何处理呢?
        models.Book.objects.values('price').annotate()
    后续BBS作业会使用
    
    你们的机器上如果出现分组查询报错的情况
        你需要修改数据库严格模式
    """

F与Q查询

# 1. 查询卖出数大于库存数的书籍
    # F 查询
    """
    能够帮助你直接获取到表中某个字段对应的数据
    """
    from django.db.models import F
    #
    # res = models.Book.objects.filter(saled__gt=F('store'))
    # print(res)

    # 2. 将所有书籍的价格提升500块

    # res = models.Book.objects.update(price=F('price')+5000)
    # print(res)

    # 3. 将所有书的名称后面加上爆款两个字
    """
    在操作字符类型的数据的时候  F不能够直接做到字符串的拼接
    """
    from django.db.models.functions import Concat
    from django.db.models import Value

    # res = models.Book.objects.update(title= Concat(F('title'),Value('爆款')))
    # res = models.Book.objects.update(title= F('title')+ 'CCC')
    # print(res)

    # res = models.Book.objects.update(price= F('price')-4500)

    # Q查询

    # 1. 查询卖出数大于100或者价格小于600的书籍

    # res = models.Book.objects.filter(saled__gt=100,price__lt=600)
    """
    filter括号内多个参数是and关系
    """
    from django.db.models import Q
    # res = models.Book.objects.filter(Q(saled__gt=100),Q(price__lt=600)) # Q包括逗号分割 还是and关系
    # res = models.Book.objects.filter(Q(saled__gt=100)|Q(price__lt=600)) # | or 关系
    # res = models.Book.objects.filter(~Q(saled__gt=100)|Q(price__lt=600)) # ~ not 关系
    #
    # print(res.query)
    # print(res)

    # Q的高阶用法 能够将查询条件的左边也变成字符串的形式
    q = Q()
    q.connector = 'or'
    q.children.append(('saled__gt',100))
    q.children.append(('price__lt',600))
    res = models.Book.objects.filter(q) # 默认还是and关系
    print(res.query)
    print(res)

django中如何开启事务

事务

        ACID

        原子性

                不可分割的最小单位        

        一致性

                跟原子性是相辅相成的

        隔离性

                事务之间互相不干扰

        持久性

                事务一旦确认永久生效

       

        事务的回滚

                rollback

        事务的确认

                commit

只需要掌握django中如何简单的开启事务

    from django.db import transaction
    try:
        with transaction.atomic():
            # sql 1
            # sql 2
            # 在with代码块内书写的所有orm操作都属于同一个事务
    except Exception as e:
        print(e)
    print('执行其他操作')

orm中常用字段及参数

AutoField

        主键字段 primary_key = True

CharField                varchar

        verbose_name  字段的注释

        max_length        长度

IntegerField        int

BigIntegerField        bigint

DecimalField

        max_digits =8

        decimal_places=2

EmailField        varchar(254)

DateField                date

DateTimeField        datetime

        auto_now: 每次修改数据的时候都会自动更新当前时间

        auto_now_add: 只在创建数据的时候记录创建时间后续不会自动修改了

BooleanField        布尔值类型

        该字段传布尔值(False/True)        数据库里面存0或1

TextField        文本类型

        该字段可以用来存大段内容(文章,博客) 没有字数限制

FileField

        upload_to = "/data" 给该字段传一个文件对象,会自动将文件保存到/data目录下,然后将文件路径保存到数据库中/data/a.txt

更多字段:参考博客 Django ORM中常用字段和参数 - JasonJi - 博客园

django除了提供了很多字段类型之外 还支持自定义字段

class MyCharField(models.Field):
    def __init__(self,max_length,*args,**kwargs):
        self.max_length = max_length
        # 调用父类的__init方法
        super().__init__(max_length=max_length,*args,**kwargs) #一定要是关键字的形式传入

    def db_type(self, connection):
        """
        返回真正的数据类型及各种约束条件
        :param connection:
        :return:
        """
        return 'char(%s)'%self.max_length

自定义字段使用

myfield = MyCharField(max_length=16,null=True)

外键字段及参数

unique = True

        ForeignKey(unique = True) === OneToOneField()

        # 用前面字段创建一对一 orm会有一个提示信息 orm推荐使用后者但是前者也能用

db_index

        如果db_index- True 则代表为此字段设置索引

to_field

        设置要关联的表的字段 默认不写关联的就是另外一张表的主键字段

on_delete

        当删除关联表的数据时,当前表与其关联的行的行为

数据库查询优化(only与defer,select_related与prefetch)

only 与 defer

orm语句的特点:

        惰性查询

                若果仅仅只是书写了orm语句        后面根本没有用到该语句所查询出来的参数

                那么orm会自动识别 直接不执行

    # 想要获取书籍表中所有书的名字
    # res = models.Book.objects.all().values_list('title')
    # print(res)
    # res = models.Book.objects.all().values('title')
    # for d in res:
    #     print(d.get('title'))
    # 你给我实现获取到的是一个数据对象,然后点title就能够拿到书名 并且没有其他字段
    # res = models.Book.objects.only('title')
    # res = models.Book.objects.all()
    # print(res) #<QuerySet [<Book: 对象:三国演义爆款>, <Book: 对象:红楼梦爆款>, <Book: 对象:水浒传爆款>, <Book: 对象:论语爆款>, <Book: 对象:聊斋爆款>, <Book: 对象:老子爆款>]>
    # for i in res:
    #     print(i.title) # 点击only括号内的字段不会走数据库
    #     print(i.price) # 点击only括号内没有的字段 会重新走数据库查询而all不需要
    res = models.Book.objects.defer('title') # 对象除了没有title属性之外其他的都有
    # print(res)
    for i in res:
        print(i.price)

    """
    defer与only刚好相反
        defer括号内放的字段不在查询出来的对象里面 查询该字段需要重新走数据库
        而如果查询的是非括号内的字段 则不需要走数据库了
    """

select_related 与 prefetch_related

    """
    selected_related 内部直接先将book表和publish表连起来    然后一次性将大表里面的所有数据全部封装给查询出来的对象
        这个时候对象无论是点击book表的数据还是publish表的数据都无需再走数据库查询了
    
    selected_related括号内只能放外键字段  一对多 一对一     
        多对多也不行
        
    
    """
    # res = models.Book.objects.select_related('publish') #INNER JOIN
    # print(res)
    # for i in res:
    #     print(i.publish.name)


    res = models.Book.objects.prefetch_related('publish')  # 子查询
    """
    prefetched_related该方法内部其实就是子查询
        将子查询查询出来的所有结果也给你封装到对象中
        给你的感觉好像也是一次性搞定的 
    """
    for i in res:
        print(i.publish.name)

 choices参数(数据库字段设计常见)

用户表

        性别

        学历

        工作经验

        是否结婚

        是否生子

        客户来源

        ...

针对某个可以列举完全的可能性字段,我们应该如何存储

只要某个字段的可能性是可以列举完全的,那么一般情况下都会采用choices参数

models.py

class User(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()
    # 性别
    gender_choices = (
        (1,'男'),
        (2,'女'),
        (3,'其他'),
    )


    gender = models.IntegerField(choices=gender_choices)
    """
    该gender字段存的还是数字 如果存的数字在上面元组列举的范围之内
    那么可以非常轻松的获取到数字对应的真正内容
    
    1. gender字段存的数字不在上述元组列举的范围之内
    2. 如果在 如何获取对应的中文信息
    """
    score_choices = (
        ('A','优秀'),
        ('B','良好'),
        ('C','及格'),
        ('D','不及格'),
    )

    # 保证字段类型跟列举出来的元组第一个数据类型一致即可
    score = models.CharField(choices=score_choices,null=True)

test.py

from django.test import TestCase

# Create your tests here.
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


if __name__ == '__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoproject.settings')
    import django
    django.setup()

    from app01 import models
    # models.User.objects.create(username='jason',age=18,gender=1)
    # models.User.objects.create(username='egon',age=85,gender=2)
    # models.User.objects.create(username='tank',age=40,gender=3)
    # 存的时候 没有列举出来的数字也能存 (范围还是按照字段类型决定)
    # models.User.objects.create(username='tony',age=45,gender=4)
    user_objs = models.User.objects.all()
    for user_obj in user_objs:
        print(user_obj.get_gender_display())
    user_obj = models.User.objects.filter(pk=1).first()
    # 只要是choices参数的字段 如果你想获取对应信息 固定写法 get_字段名_display()
    print(user_obj.gender)
    print(user_obj.get_gender_display())
    user_obj = models.User.objects.filter(pk=4).first()
    # 如果没有对应关系 那么字段是什么还是展示什么
    print(user_obj.get_gender_display())


choices参数使用场景是非常广泛的

MVT与MVC模型

MTV: django号称是MTV模型

        M: models

        T:  templates

        V: views

MVC: 其实django本质也是MVC

        M: models

        V: views

        C: controller(urls.py)

vue框架: MVVM模型

多对多三种创建方式

全自动:利用orm自动帮我们创建第三张关系表

class Book(models.Model):
    name = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Author')
    
class Author(models.Model):
    name = models.CharField(max_length=32)

"""
优点: 不需要写代码 非常方便 还支持orm提供操作第三章关系表的方法...
不足之处: 第三张关系表的扩展性极差(没有办法额外添加字段)
"""

纯手动

class Book(models.Model):
    name = models.CharField(max_length=32)

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

class Book2Author(models.Model):
    book_id = models.ForeignKey(to='Book')
    author_id = models.ForeignKey(to = 'Author')

"""
优点:第三张表可以随意进行扩展
不足之处:需要写的代码较多,不能够再使用orm提供的简单方法 add remove clear
不建议使用
"""

半自动

class Book(models.Model):
    name = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Author',through='Book4Author',through_fields=('book','author'))
    # authors = models.ManyToManyField(to='Author')

"""
through_fields字段先后顺序
    判断本质:
        通过第三张表查询对应的表需要用到哪个字段就把哪个字段放前面
    你也可以简化判断:
        当前表是谁   就把对应的关联字段放前面
"""
class Author(models.Model):
    name = models.CharField(max_length=32)


class Book4Author(models.Model):
    book = models.ForeignKey(to='Book',on_delete=models.CASCADE)
    author = models.ForeignKey(to='Author',on_delete=models.CASCADE)

"""
through_fields字段先后顺序
    判断本质:
        通过第三张表查询对应的表需要用到哪个字段就把哪个字段放前面
    你也可以简化判断:
        当前表是谁   就把对应的关联字段放前面
"""
半自动可以使用orm的正反向查询 但是没法使用add,set,remove,clear这4个方法

总结:需要掌握的是全自动和半自动 为了扩展性更高 一般采用半自动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值