Django框架

Django是什么

Django是别人写好的web框架,包罗万象,只要按照别人写好的方法来一个个的对应上去用就可以

MVC和MTV模型

1 MVC

M:model(模型层), 跟数据库打交道的
V:view(视图层), html页面
C:controller(控制器层), 处理逻辑,调用页面和数据

2 MTV

M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
T 代表模板 (Template):负责如何把页面展示给用户(html)。
V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template

 

Django的安装

pip install Django==1.1.1(这里的1.1.1是版本)

如何验证是都安装成功:cmd中,输入django-admin有东西跳出来,说明已经安装成功

 

Django项目创建

打开cmd,切换到安装Django的目录下,输入 djang-admin startproject mysite(mysite是项目名)

pycharm创建项目的话,file→newproject→Django

 

 

 

项目启动

打开cmd,切换到django目录下,输入命令

python manager.py runsever

python manager.py runsever 127.0.0.1:8000(本地启动默认是8000端口,也可以把IP地址省略)

pycharm启动:

点右上角的绿色启动按钮即可

创建的时候,老版本的django可能会右一个bug,就是templats文件夹的路径是错误,需要手动修改

在setting文件中,路径修改成os.path.jion(BASE_DIR, ‘templates’)

 

 

 

 

创建应用

什么是应用

Django相当于一个公司,而应用相当于公司下面的一个个部门,可以有多个部门

同理:一个django下面,可以有多个应用,比如ATM项目,下面有注册,登录,转账 等应用

如何创建应用

应用名 符合规范即可

打开cmd,定位到django项目目录下,输入 python manager.py startapp app01(应用名)

或者在pycharm中,tools → Run manager.py Task,之后输入 start app01

注意:创建完成之后,在setting.py(配置目录)中要注册,才生效(配置的时候,需要从app01里面导入views)

复制代码
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01',  # 新建的应用名
]
复制代码

 

主要文件介绍

复制代码
mysite1      # 项目名称
    app01   # 应用名称
        migrations  # 迁移数据库的时候,会把迁移记录存在这个文件夹里面
            __init__.py #
        admin.py    # django自带的后台管理系统
        apps.py        # 应用配置相关,暂时不用管
        models.py    # M:model层,跟数据库打交道的
        tests.py     # 测试脚本文件
        views.py     # V:view(逻辑层)
mysite1
    __init__.py
    settings.py       # 整个django项目的配置文件
    urls.py             # 路由文件,记录后缀与视图函数的对应关系
    wsgi.py             # wsgiref服务器,在本地项目使用wsgiref, 后续上线的时候,我们会换成 uwsgi服务器,uwsgi承载的并发量更高,而wsgiref承载的并发量小,WSGI协议
templates            # 写html页面的地方
db.sqlite3            # django框架自带的小型数据库,mysql
manage.py            # 整个django项目的入口文件
复制代码

 

django的小白三板斧

是写在views下面的,默认只有一个render

1. HttpResponse()
2. render()
3. redirect

 # return HttpResponse('index')  # 返回值是字符串
 # return render(request, 'index.html')  # 渲染html页面的
 # return redirect('http://www.mzitu.com')  # 重定向的
 # return redirect('/home/')  # 重定向的

 

静态文件配置

什么是静态文件

比如写好的html,js,css页面,图片,等

html文件,直接放在templates文件夹下

其他的文件,放在static文件夹下(需要自己手动新建)

 

 

 

配置文件路径

在setting.py的最后,写入以下(django提供的,变量名一个字母都不能变)

STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static')
    ]

配置完成之后,新建的html页面,可以直接动态导入

在head中

动态引入
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <script src="{% static 'js/jquery.min.js' %}"></script>
    <script src="{% static 'js/bootstrap.min.js' %}"></script>

 

form表单实现登录功能

因为get请求的话,请求的内容会在浏览器地址栏里显现出来,所以不安全,所以要用post请求来提交,如下

<form action="" method="post">
1. 什么都不写:当前地址提交
2. 写全:
3. 写后缀:/login/

使用form提交的时候需注意,先去配置文件中,把csrf中间件给注释掉,如下

复制代码
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',
]
复制代码

登录功能简单实现

html页面内容

复制代码
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/bootstrap.css' %}">
    <script src="{% static 'js/jquery-3.6.0.js' %}"></script>
    <script src="{% static 'js/bootstrap.js' %}"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h1 class="text-center">登录页面</h1>
            <form action="" method="post">
                <div class="form-group">
                    <lable for="username">用户名:</lable>
                    <input type="text" id="username" name="username" class="form-control">
                </div>
                <div class="form-group">
                    <lable for="username">密码:</lable>
                    <input type="password" id="password" name="password" class="form-control">
                </div>
                <input type="submit" class="btn btn-success" value="点我登录">
            </form>
        </div>
    </div>
</div>
</body>
</html>
复制代码

在后端 要 接收前端name 中的 username 和password 的数据

注意:前端穿过来变量,都是 str 的形式,用request.'POST'来接收

def login(request):
    if request.method == 'POST': # 因为会受到2个请求,get 和 post 所以需要判断
        # 接收登录的 用户名 和 密码
        username = request.POST.get('username')
        password = request.POST.get('password')

 

pycharm连接数据库

很简单,用过就会,略

 

Django连接MySQL数据库

1, 更改django中数据库的设置(在配置文件中修改)

2,在应用下面的__init__.py下面,输入一下代码

复制代码
DATABASES = {  #这是配置文件里的内容
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'app01',
        'HOST': 'localhost',
        'PORT': '3306',
        'USER': 'root',
        'PASSWORD': '123',
        'CHARSET': 'utf8'
    }
}
复制代码

 

 

 

Django操作orm

什么是orm

是一个关系映射对象

orm不仅在python中有,其他语言中也有orm,可以操作数据库

orm的特点

在操作的时候,不需要在写原生的sql语句了

orm写在哪里

写在应用下的models.py 文件里面

如何使用

要使用orm,必须先创建好库

用函数创建,一下是一一对应关系

表名  >>  类名

记录  >>  对象

字段  >>  类属性

如何创建一张表

表名:UserInfo

字段:uid,username,password 

class UserInfo(models.Model):
    uid = models.AutoField(primary_key=True) # 说明这个是主键,自增
    username = models.CharField(max_length=32)  # 32位长度的字符串
    password = models.CharField(max_length=32)

    def __str__(self):  # __str__方法,打印的时候,显示用户名
        return self.username

在models文件下写完之后,需要执行一下2行代码

python3 manage.py makemigrations # 只是生成迁移记录
python3 manage.py migrate # 才是真正的生成数据

 字段的增删改查

字段,传统的就叫做表头

原表如下

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

增加字段

比如要添加hobby字段

class Author(models.Model):
    name = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    hobby = models.CharField(max_length=32, verbose_name='爱好', default='study') 默认是study
    hobby = models.CharField(max_length=32, verbose_name='爱好', null=True)  字段可以为空

删除字段

直接注释掉对应的字段,在进行数据库的迁移和生成

修改字段

直接修改对应的字段,然后进行数据库迁移

查询字段

1.直接用pycharm,或者Navicat查询

2.后端匹配前端数据,查询数据库的话,直接看代码

 

数据的增删改查(重点)

先要在views文件中,导入models

from app01 import models

应用场景:

用户在前端,增删改查他自己的数据,就是访问了相对应的页面执行的程序

数据的增加

推荐用objects组件,里面封装了很多东西,也比较方便

def func(request):
  第一种(推荐,因为只需要一步,简单)
# res = models.Author.objects.create(name='gudawei', phone='111') # print(res)
  第二种
res = models.Author(name='gudawei2', phone='222') # 这个只是生成了对象 res.save() # 这才是真正的保存 return render(request, 'func.html')

数据的删除

分物理删除和软删除

物理删除

def func(request):
    res = models.Author.objects.filter(name='gudawei2').delete()
    # 上面的filter(筛选)中,name可以是任何一个字段里的值,
   # 比如pk=1(pk指的是主键),name='gudawei', phone='111'都可以
print(res) # 打印的话,是显示影响的行数, (1, {'app01.Author': 1}) return render(request, 'func.html')

软删除(常用)

不过在企业中,一般做的是软删除,不是真正的删除,因为可以恢复数据,可以减少企业损失

新建一个is_delete字段,默认值设置成0,代表当前数据有效,如果是1,那么就是删除

    '''默认值是0,如果删除了,把默认值是设置成1,那就代表删除了'''
    is_delete = models.BooleanField(default=0)  # 存true:1,False:0
    models.Author.objects.filter(pk=1).update(is_delete=True)
    # 执行之后,数据库中id=1的字段 的is_delete的值,就是1,代表软删除

 

修改数据

现在的authot表格如下

 

需求:把gudawei的phone修改成444

在SQL中,是update author set phone(phone=444) 

在orm中

models.Author.objects.filter(name='gudawei').update(phone='444')

数据的查询

用objects下面的filter方法来制定条件查询

最后加 .first 的话,如果前端传来用户名匹配到的对象为空,那就说明用户名不存在

  filter里面填的是前端传来的值如果是username,那就是filter(username=‘前端传来的username’).first(),如果判断是None,那就说明该用户不存在

res = models.Author.objects.filter(phone=444) # 查询phone是444的第一条记录
print(res[0].name)
# 打印的是queryset对象,可以后面加。来取值,空就是没有<QuerySet []>
res = models.Author.objects.filter(phone=666).first() # 查询phone是666的第一条记录,last()就是最后一条
print(res) # 查询不到,打印是 None

模板页面传值

就是把后台的代码,传值到前端页面中用,通过render来传过去,有2中方式

第一种(因为变量可能比较多,不推荐)

def func(request):
    a = '哈哈哈'
    return render(request, 'func.html', {'username': a})
<body>
<div>
    func{{ username }}
</div>
</body>
</html>

第二种(用locals()因为数据库里传来的值都是对象,可以取值)

def func(request):
    author_list = models.Author.objects.all()
    return render(request, 'func.html', locals())
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    func
    {% for author in author_list %}  <!-- 这里是for循环,有头有尾,把表格写活了-->
        <div>
        <p>{{ author.name }}</p>
        <p>{{ author.phone }}</p>
        </div>
    {% endfor %}

</div>
</body>
</html>
复制代码

 

使用orm创建外键关系

外键关系有三种

一对一  外键字段建立在查询频率较多的表

一对多 外键字段建在多的一方

多对多 外键字段不建在任何一张表中,而是会自动创建出第三张虚拟表

当创建外检关系的时候,字典名后面会自动加上_id,不需要手动添加

 

 

创建外键字段的时候,需要先创建基础字段,写完之后,在一个个写外键关系

复制代码
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    # 创建和出版社的外键关系
    publish = models.ForeignKey(to='Publish')
    # to指哪张表,to_field指的是哪个字段来关联,如果是主键id可以省略
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    title = models.CharField(max_length=32, verbose_name='图书标题')
    addr = models.CharField(max_length=32, verbose_name='地址')


class Author(models.Model):
    name = models.CharField(max_length=32)
    # 与作者详情表建立一对一的外键关系
    author_detail = models.OneToOneField(to='AuthorDetail')


class AuthorDetail(models.Model):
    phone = models.CharField(max_length=64)
    wx = models.CharField(max_length=32)
复制代码

在Django 1.X中,级联更新级联删除 是 默认的

在django 2.X中,级联更新级联删除 是 需要手动作指定的,

  需要在ForeignKey时候,里面要加上一个参数 on_delete=models.CASCADE

  OneToOneFreid里面,同样加上on_delete=models.CASCADE

django 2.X中,on_delete属性有多个参数

CASCADE:这就是默认的选项,级联删除,你无需显性指定它。
PROTECT: 保护模式,如果采用该选项,删除的时候,会抛出ProtectedError错误。
SET_NULL: 置空模式,删除的时候,外键字段被设置为空,前提就是blank=True, null=True,定义该字段的时候,允许为空。
SET_DEFAULT: 置默认值,删除的时候,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值。
SET(): 自定义一个值,该值当然只能是对应的实体了

 

Django的请求周期生命图(必须会自己画出来)

 

 

 

 

 

 

路由层

复制代码
1. 路由匹配:
urls.py 这个文件是django框架的总路由文件,意味着还有分路由文件,每个应用都支持有自己的路由文件。
# 控制django是否自动加斜杠匹配 APPEND_SLASH = False url(r'^test/$', views.test), ''' 路由匹配规则: 路由从上往下依次匹配,如果路由第一次匹配到了,那么,就会执行对应的视图函数,就不在往下匹配了。 ''' 2. django2中的路由文件 path # 精准匹配 re_path # 类似于url,支持正则表达式
复制代码

 

无名分组有名分组

复制代码
# 1. 无名分组: 正则表达式匹配大的内容用括号括起来
# 2. 有名分组:正则表达式匹配大的内容用括号括起来,然后起个名字
url(r'^test/(\d+)', views.test), 
#正则中/d+ 表示至少匹配一个数字,匹配完成之后,以位置参数的形式传给views.test,text函数中,需要加一个参数 url(r
'^test/(\d+)/(\w+)/(\d+)/(\d+)', views.test), 无名分组会把括号中匹配的数字当成位置参数传给视图函数, 支持多个匹配规则。 url(r'^test/(?P<year>\d+)/(?P<month>\d+)/(?P<day>\d+)', views.test), 有名分组会把括号中匹配的数字当成关键字参数传给视图函数, 支持多个匹配规则。
在test函数中,添加关键字参数year, mont,day,
# 无名和有名不能混合使用 get请求传参方式: https://www.bilibili.com/movie/?spm_id_from=333.1007.0.0 https://www.cnblogs.com/qingmiaokeji/p/16026631/a/1/b/2/c/3.html
复制代码

反向解析(了解)

就是通过路由别名可以得到该别名对应的路由地址

需要在views文件里面,render之后导入reverse

# 后端反向解析
url(r'^test/v1/v2/v3/v4', views.test, name='aaaa'),
print(reverse('aaaa')) 打印的结果是test/v1/v2/v3/v4
# 前端反向解析
打开浏览器,检查,elements中能看到,路由名字可以变,但是只要指定aaaa就是动态的
url(r'^test/v1/v2/v3/v4', views.test, name='aaaa'), <a href="{% url 'aaaa' %}">点我</a>
# 无名分组反向解析
url(r'^test/(\d+)', views.test, name='xxxx'),
# 后端反向解析 # 一般用不到,如果真的用到了,一般传主键值,或这id值
 print(reverse('xxxx', args=(id, ))) # 同样在test中需要加上1个参数
# 前端反向解析
<a href="{% url 'xxxx' 3333 %}">点我</a> # 333是符合urls中的正则表达式
# 有名分组反向解析
url(r'^test/(?P<year>\d+)/(?P<month>\d+)', views.test, name='xxxx'),
# 后端反向解析
 print(reverse('xxxx', args=(id, )))
# 前端反向解析
<a href="{% url 'xxxx' year=2022 month=9 %}">点我</a>

路由分发

因为项目大了之后,总路由压力会很大,所以可以当成中转站,分发给不同的应用

django支持每一个应用下面有自己的
1. urls.py
2. static文件夹
3. templates

复制代码
路由分发的两种方式:
    1.  
     from app01 import urls as app01_urls
    from app02 import urls as app02_urls
    # url(), 的第一个参数不能加$
    url(r'^app01/', include(app01_urls)),
    url(r'^app02/', include(app02_urls)),
    
    2.
    # 第二种方式,需要导入模块
  from django.conf.urls import include
    url(r'^app01/', include('app01.urls')),
    url(r'^app02/', include('app02.urls')),
复制代码

 

名称空间(了解)

url(r'^app01/', include('app01.urls', namespace='app01')),
    url(r'^app02/', include('app02.urls', namespace='app02')),
    
    print(reverse('app01:xxxx'))
    print(reverse('app02:xxxx'))

虚拟环境

# 针对不同的项目,创建出来一个新的python环境,类似于是一个纯净版的python解释器

很简单,创建试一下就知道了

 

JsonResponse

复制代码
# 1. 混合项目开发:用不到json格式的数据
# 2. 前后端分离项目:用的都是json格式的数据

json格式的数据:跨语言数据传输
序列化数据为json
import json

1. dumps: 序列化
2. loads:反序列化

# 前端中序列化和反序列化
1. JSON.stringify()  # 序列化
2. JOSN.parse()      # 反系列化

from django.http import JsonResponse
def ab_json(request):
    # user_dict = {'username': 'ly大帅哥', 'gender': 'male'}
    l = [1, 2, 3]
    # res = json.dumps(user_dict, ensure_ascii=False)
    # return HttpResponse(res)
    # return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})
    return JsonResponse(l, safe=False)
复制代码

CBV的书写

1. FBV: function base viewd  基于函数的视图,之前的def都是FBV
2. CBV:class base viewd   基于类的视图

CBV的路由:
    # cbv的路由如何写?固定写法
    url(r'^index_cbv/', views.IndexView.as_view()),
复制代码
 CBV:必须继承View类
from django.views import View

# 在CBV中,类中的方法名不能所以叫,只能叫请求方式的名称
# get, post
# 请求方式一共有8种,目前学了2种
# 关键问题在于:CBV的路由该如何写?

# 是如何请求到get方法的呢?
# 如果你是get请求方式,那么就会触发get方法
# 如果你是post请求方式,那么就会触发post方法
class IndexView(View):
    def get(self, request):
        return HttpResponse('get')

    def post(self, request):
        return HttpResponse('post')
复制代码

form表单上传文件

前端建立form表单

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data"> # 注意这里的改变
    <p>
        <input type="file" name="mafile">
    </p>
    <input type="submit" value="提交">

</form>
</body>
</html>
复制代码

后端接受

复制代码
def ab_file(request):
    if request.method == 'POST':
        print(request.FILES)  # <MultiValueDict: {'myfile': [<InMemoryUploadedFile: 1211.png (image/png)>]}>
        file_obj = request.FILES.get('myfile')
        print(file_obj)  # 接收到图片的名字 1211.png
        print(file_obj.name)  # 接收到图片的名字 1211.png
        print(file_obj.size)  # 接收到图片的名字 1211.png
        if file_obj.size > 1024000:  # 限制大小1M
            return HttpResponse('文件太大了,重新上传')  # 这里可以用那个ajax做弹窗告知,点击确定后返回当前页
        # 然后要把图片存进来
        with open(file_obj.name, 'wb') as f:  # 这里的file_obj.name是路径,file_obj是对象,对应的名字下面的2进制数据
            # 上传文件读取的时候要一行一行的读取,目的是为了防止内存爆满
            for line in file_obj:
                # print(line)
                f.write(line)
    return render(request, 'ab_file.html')
复制代码

 当然,为了防止图片很多,名字重复覆盖的问题,可以自己加随机的字段来拼接图片的名字之后来存储

import uuid
        rend_str=uuid.uuid4()
        import random
        file_path = str(rend_str) + '.png'
        # with open(file_obj.name, 'wb') as f:
        with open(file_path, 'wb') as f:
            for line in file_obj:
                f.write(line)

CBV的源码分析(这个一定要搞懂)

自己看视频分析后,自己看源码

 

模板层

模板语法 之 传值

1. 传值
{{ }} # 一般给变量使用相关
{% %} # 一般跟逻辑相关的

2. 所有需要加括号的都不用加
3. '''模板文件一律使用点语法'''

模板语法之过滤器

复制代码
# 相当于python的内置函数

语法:
    {{ 变量|过滤器:参数 }}
    '''竖杠左边的变量当成过滤器的第一个参数,冒号后面的当成第二个参数
    
        如果冒号后面没有参数了,那就是只有一个参数。
        过滤器最多传递两个参数。
    '''
# 过滤器有很多个,大概有六七十个,我们需要掌握四五个就行
1. length:求长度
2. default:竖杠左边为真,就显示竖杠左边的值,如果为假,则显示默认值。{{Tue|dafault:‘我是默认值’}}
3. date:格式化时间
4. filesizeformat
5. safe

'''
    安全相关的:
        1. MySQL中会出现SQL注入的问题
        2. 前端中会出现xss攻击
        3. django中的form表单中会出现csrf跨站请求问题
'''

 '''xss攻击:不让外部的标签直接生效'''
    # 前端的代码有时候也可以在后端写好,然后传递到前端使用
    from django.utils.safestring import mark_safe

    s = mark_safe('<h1>xss攻击</h1>')
复制代码

模板语法 之 标签

字典三剑客:keys,values,items

复制代码
# 在html中可以使用 if else for循环等

{% for foo in user_dict.keys %}
    <p>
        {{ foo }}
    </p>
{% endfor %}

{% for foo in user_dict.values %}
    <p>
        {{ foo }}
    </p>
{% endfor %}

{% for foo in user_dict.items %}
    <p>
        {{ foo }}
    </p>
{% endfor %}
复制代码

模板语法 之 继承

复制代码
# 导入模板
{% include 'left.html' %}

# 模板继承
{% extends 'home.html' %}

{% block css %}
    <style>
        h1 {
            color: red;
        }
    </style>
{% endblock %}

{% block js %}
    <script>
        alert(123)
    </script>
{% endblock %}

{% block content %}
    <h1>登录页面</h1>
    <form action="">
        <p>username: <input type="text" class="form-control"></p>
        <p>password: <input type="text" class="form-control"></p>
        <input type="submit" class="btn btn-success btn-block">
    </form>

    {% include 'left.html' %}


{% endblock %}
复制代码

模板语法 之 导入

 {% include 'aa.html' %}
这里的aa.html是被导入的页面,一般只导入局部的页面
被导入页面中所有东西要删掉,只写需要被导入的东西

测试环境的准备

在test.py文件下,输入以下代码

import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day06.settings")
    import django
    django.setup()
    '''测试环境已经准备好了,只能在这个代码一下写ORM相关操作,否则不生效'''
    from app01 import models

orm的其他查询方法

复制代码
# get
    # res = models.Book.objects.filter(pk=10).first() # 查不到数据为None
    # try:
    #     res = models.Book.objects.get(pk=10) # 查不到直接报错,而且,get查询的数据有切只能为一条
    # print(res)

    # order by 排序,
    # res = models.Book.objects.all().order_by('id') # 默认是升序排列
    # res = models.Book.objects.all().order_by('-id') # 降序排列
    # res = models.Book.objects.all().order_by('-id', 'price') # 降序排列
    # res = models.Book.objects.all().order_by('-id', '-price') # 降序排列
    # sql:  select * from table order by id desc, price desc, title asc
    # res = models.Book.objects.all().order_by('-id', '-price', 'title')  # 降序排列
    # print(res)

    # 反转数据:前提是排序了
    # res = models.Book.objects.all().order_by('id').reverse()
    # print(res)

    # count:
    # 查询表中一共多少条数据
    # sql: select count(*) from table where id=1
    # res = models.Book.objects.count() # 5
    # res = models.Book.objects.filter(id=1).count() # 5
    # print(res)

    # values, value_list
    # SQL:select title, price from table
    # SQL:select * from table
    # <QuerySet [{'title': '西游记', 'price': Decimal('111.22')}]>
    # res = models.Book.objects.filter(pk=1).values('title', 'price')
    # res1 = models.Book.objects.filter(pk=1).all()
    # print(res)
    # # print(res1)
    # for j in res:
    #     print(j['title'])

    # res2 = models.Book.objects.filter(pk=1).values_list('title', 'price')
    # print(res2)
    #
    # for i in res2:
    #     print(i[0])

    # exclude
    # res = models.Book.objects.exclude(pk=1) # 排除数据
    # print(res)

    # exist
    # res = models.Book.objects.filter(pk=111).exists()
    # print(res)
复制代码

基于双下划线的查询

复制代码
 # 1. 查询书籍的价格大于200的
    # sql:where price > 200
    # orm:  gt => greater than
    # res = models.Book.objects.filter(price__gt=200)
    # print(res)

    # 2. 查询书籍的价格小于等于200的
    # sql: where price <= 200   less than equal
    # res = models.Book.objects.filter(price__lte=200)
    '''如何查看ORM执行的SQL语句?'''
    # 1. 如果返回结果是queryset对象的,都可以点query属性查看
    # print(res)
    # print(res.query)

    # 3. 查询书籍价格是111, 222的
    # sql: where price =111 or price =222
    # sql: where price in (111, 222)
    # orm:
    # res = models.Book.objects.filter(price__in=[111, 222])
    # print(res)

    # 4. 查询书籍价格在100-300之间的所有书籍
    # sql: where price >= 100 and price <= 300
    # sql: where price between 100 and 300
    # orm:
    # res = models.Book.objects.filter(price__range=[100, 300])
    # print(res)

    # 5. 查询书籍名称带有西的所有书籍
    # sql: where title like '%西%'
    # orm:
    # res = models.Book.objects.filter(title__contains='西')
    # SQL:where title like '%西'
    # res = models.Book.objects.filter(title__startswith='西')
    # SQL:where title like '西%'
    # res = models.Book.objects.filter(title__endswith='西')
    # print(res)

    # 6. 查询书籍添加时间是2021-03月的
    # res = models.Book.objects.filter(create_time__year=2021, create_time__month=3)
    res = models.Book.objects.filter(create_time__year=2021, create_time__month=3)
    print(res)
复制代码

查看SQL语句执行情况

复制代码
在配置文件中复制一下代码就可以了,操作orm的时候,下面会自己打印出来对应的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. 针对与一对多, 一对一
#
publish_obj = '' # 对象
create(publish_id=1)
create(publish=publish_obj)

#
update(publish_id=1)
update(publish=publish_obj)

2. 针对多对多的
book_obj = Book.objects.filter(pk=1).first()
# 增加
book_obj.authors.add(1, 2)
book_obj.authors.add(author_obj, author_obj2);

#
book_obj.authors.set([1, 2, 3])
book_obj.authors.set([author_obj, author_obj1, author_obj2])

# 删除
book_obj.authors.remove(1)

# 清空
book_obj.authors.clear()


'''针对多对多外键关系的增删改查,有4个方法:add, set, remove, clear, 以上4个方法只适用于自动创建的第三张表,其他情况不能用'''

# 创建多对多关系的另外两种方式。
复制代码

 

正反向的概念

复制代码
1. 正向
2. 反向
'''
    外键字段在我手上,我查你就是正向
    外键字段不在我手上,我查你就反向
'''

举例:
    1. book     >>>  publish     >>> 正向
    2. publish  >>> book        >>>  反向
    3. author   >>> book        >>>  反向
    4. book     >>>  author        >>> 正向

 # 在多表查询的时候,按照正反向查询的以上口诀

'''
    1. 正向查询按照字段(外键字段)
    2. 反向查询按照表名小写或者表名小写_set.all()
'''
复制代码

 

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

复制代码
# 1. 查询书籍主键为1的出版社名称
    # 书查出版社  >>> 正向  =>>> 按字段
    # book_obj = models.Book.objects.filter(pk=1).first()
    # print(book_obj.publish)  # Publish object
    # print(book_obj.publish.title)


    # 2. 查询书籍主键为1的作者
    # 书 >> 作者  >>> 正向  >>> 字段
    # book_obj = models.Book.objects.filter(pk=1).first()
    # print(book_obj.authors) # app01.Author.None
    # print(book_obj.authors.all())  # <QuerySet [<Author: ly>]>
    # for i in book_obj.authors.all():
    #     print(i.name)

    # 3. 查询作者ly的手机号
    # 作者  >>> 详情  >>> 正向  >>> 字段
    # author_obj = models.Author.objects.filter(name='ly').first()
    # print(author_obj.author_detail)
    # print(author_obj.author_detail.phone)
    
    '''
        总结: 
            针对与正向查询,查询出的结果可能有多个的时候,就要使用字段名.all()
            如果查询结果只有一个的情况,直接使用字段名即可。
    '''


    # 4. 查询东京出版社出版的所有书籍
    # 出版社  >>> 书  >>> 反向  >>> 表名小写
    publish_obj = models.Publish.objects.filter(title='东京出版社').first()
    print(publish_obj.book_set.all())

    # 5. 查询作者ly写过的书
    # 作者 >>> 书  >>> 反向  >>> 表名小写
    author_obj = models.Author.objects.filter(name='ly').first()
    print(author_obj.book_set.all())

    # 6. 查询手机号是120的作者的姓名
    # 详情表 >>> 作者 >>> 反向  >>> 表名小写
    author_detail = models.AuthorDetail.objects.filter(phone=120).first()
    print(author_detail.author)
    print(author_detail.author.name)
    
    '''
        总结:
            针对于反向查询只要查询出来的结果可能有多个的时候,就要加_set.all()
            如果查询出来的结果只有一个,就不用在加_set.all(),直接用表名小写
    '''
复制代码

 

基于双下划线的跨表查询

复制代码
'''基于双下换线跨表查询: 正反向的查询概念同样适用在这里'''
    # 1. 查询书籍主键为1的出版社名称,图书标题
    # 书 》 出版本 》正向 》 字段
    # res = models.Book.objects.filter(pk=1).values('title', 'publish__title')
    # print(res)

    # 2. 查询书籍主键为1的作者
    # 书 作者 正向 字段
    # res = models.Book.objects.filter(pk=1).values('title', 'authors__name')
    # print(res)

    # 3. 查询作者ly的手机号
    # 作者 详情 正向 字段
    # res = models.Author.objects.filter(name='ly').values('name', 'author_detail__phone')
    # print(res)

    # 4. 查询东京出版社出版的所有书籍
    # 出版社  书 反向 表名小写
    # res = models.Publish.objects.filter(title='东京出版社').values('title', 'book__title')
    # print(res)

    # 5. 查询作者ly写过的书
    # 作者查书 反向 表名小写
    # res = models.Author.objects.filter(name='ly').values('name', 'book__title')
    # print(res)

    # 6. 查询手机号是120的作者的姓名
    # 详情查作者 反向 表名小写
    # res = models.AuthorDetail.objects.filter(phone=120).values('phone', 'author__name')
    # print(res)
    
    # 7. 查询书籍主键为1的作者的手机号
    # book   author   author_detail    phone
    res = models.Book.objects.filter(pk=1).values('title', 'authors__author_detail__phone')
    print(res)
复制代码

F与Q查询**

复制代码
# F查询
'''F查询可以拿到数据表的字段原数据'''

1. 把书籍的价格在原来的基础上增加100元
sql: update book set price = price + 100;

   '''F查询'''
    from django.db.models import F
    # 1. 把书籍的价格在原来的基础上增加100元
    # orm
    '''F默认是操作的数字'''
    # models.Book.objects.update(price=F('price')+100)

    # 2. 把所有书籍的标题后面都加上'好产品'三个字
    # orm
    # models.Book.objects.update(title=F('title') + '好产品')

    from django.db.models.functions import Concat
    from django.db.models import Value

    models.Book.objects.update(title=Concat(F('title'), Value('好产品')))
复制代码
复制代码
# 1. 查询书籍价格大于300的或者标题为哈哈的书籍
 '''Q查询'''
    # # 1. 查询书籍价格大于300的或者标题为哈哈的书籍
    # and关系
    # or的关系需要借助于 Q
    from django.db.models import Q
    # res = models.Book.objects.filter(Q(price__gt=300), Q(title='哈哈')) # Q包括起来之后,逗号连接还是and关系
    # res = models.Book.objects.filter(Q(price__gt=300) | Q(title='哈哈')) # Q包括起来之后,逗号连接还是and关系
    # res = models.Book.objects.filter(~Q(price__gt=300) | Q(title='哈哈')) # Q包括起来之后,~连接还是not关系
    res = models.Book.objects.filter(~(Q(price__gt=300) | Q(title='哈哈'))) # Q包括起来之后,~连接还是not关系

    print(res)
复制代码

聚合查询**

复制代码
# max, min, sum, count, avg
sql: select count(*) from table
    
'''在django中使用聚合函数的时候,前提是要用关键字 aggregate'''
# 查询书籍的最高价格
'''聚合查询'''
    # 查询书籍的最高价格
    # orm
    from django.db.models import Max, Min, Sum, Count, Avg
    # res = models.Book.objects.aggregate(Max('price'))
    # print(res)
    #
    # res = models.Book.objects.aggregate(Min('price'))
    # print(res)

    res = models.Book.objects.aggregate(max_price=Max('price'), min_price=Min('price'), sum_price=Sum('price'), count=Count('id'), avg_price=Avg('price'))
    print(res)
复制代码

分组查询**

复制代码
# group by 
# sql_mode = 'only_full_group_by'
1. concat
2. concat_ws
3. group_concat

'''在ORM中,分组查询需要使用关键字:annotate'''
# 1. 统计每一本书的作者个数

'''分组查询'''
    # 1. 统计每一本书的作者个数
    '''正反向查询同样适用'''
    # 书 作者 正向 字段
    # res = models.Book.objects.annotate() # 只要遇到annotate,annotate前面没有values的情况下,就是按照models后面的表id进行分组
    # res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
    # print(res)

    # 2. 统计出每个出版社的最便宜的书
    # 出版社  书 反向 表名小写
    # res = models.Publish.objects.annotate(min_price=Min('book__price')).values('title', 'min_price')
    # print(res)

    # 3. 统计不止一个作者的图书
    # 统计 图书的作者个数,筛选出作者个数大于1的
    # 3.1 查询每个图书的作者个数: 书查作者   正向  字段
    # 3.2 过滤掉作者个数大于1的
    # res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
    res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title', 'author_num')
    print(res)
复制代码

 事物

复制代码
# 事务的四大特性和隔离级别
1. start transaction
2. rollback
3. commit

   try:
        '''执行的SQL语句'''
        with transaction.atomic():
            # '''SQL1'''
            # '''SQL2'''
            pass
            # '''在这个代码块中的SQL语句,都是属于同一个事务'''
    except Exception as e:
        print(e) # 查询事务事务失败的原因
        # 回滚事务
        transaction.rollback()
复制代码

choices参数

复制代码
# 如果你的字段存储的可能性能够列举完,一般采用choices参数
举例:
    性别:男, 女, 其他 # 1 2 3
    学历:小学,中学,大专,本科,硕士,博士,博士后,... # 1 2 3 4 5 6 7 8 9
    来源:朋友圈,qq,广告,朋友介绍,... # 1 2 3  4 5 
    
# 使用choices参数就可以实现

  '''choices参数''' # gender int
    gender_choices = (
        (1, ''),
        (2, ''),
        (3, '其他'),
    )
    gender = models.IntegerField(choices=gender_choices, null=True)

    score_choices = (
        ('A', '优秀'),
        ('B', '良好'),
        ('C', '一般'),
        ('D', '及格'),
    )
    '''存储的数据类型该如何选择? 数据类型的选择按照第一个参数的数据类型'''
    score = models.CharField(max_length=16, choices=score_choices, null=True)  
    
  res = models.Author.objects.filter(pk=4).first() # 对象
    print(res)
    print(res.gender)
    '''如果你想获取数字对用的中文,固定句式:get_字段名_display()'''
    print(res.get_gender_display())
    print(res.get_score_display())
复制代码

ORM中常用的参数

复制代码
unique
如果设置为unique=True 则该字段在此表中必须是唯一的 。

db_index
如果db_index=True 则代表着为此字段设置索引。
res.book_set.all()  #> res.students.all()

related_name
反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。

db_constraint
是否在数据库中创建外键约束,默认为True。

db_table
默认创建第三张表时,数据库中表的名称。
复制代码

 

posted @   Damon1899  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示