zl程序教程

您现在的位置是:首页 >  后端

当前栏目

16.一篇文章学会django模型的使用

django 模型 文章 学会 16 一篇 使用
2023-09-27 14:23:04 时间

1.django模型简单示例

1.1 创建django项目

创建完项目,还需要创建django子项目

django-admin startproject model_study
cd .\model_study\
python manage.py  startapp model_app

在这里插入图片描述

1.2配置应用

将模型对应的应用程序(刚刚创建的子应用)添加到项目的settings中:

INSTALLED_APPS = [
    'model_app'# 一定要加逗号!!!!!
]

在settings中配置正确的数据库连接

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

安装对应数据库的驱动

pip install mysqlclient==2.0.3

在这里插入图片描述

1.3 预备迁移

在项目根目录的cmd中运行

python manage.py makemigrations model_app
  • model_app是子应用的名称,如果不指定,那么就是对所有
  • INSTALLED_APPS 中的应用都进行预备迁移
    指定该命令后,在对应的子应用下的 migrations 中会生成一个对应的迁移文件

1.4编写模型

在子应用的models.py中

from django.db import models

# Create your models here.
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField()
    

1.5 正式迁移

python manage.py migrate model_app

1.6 效果展示

在这里插入图片描述

2.django模型修改后重新应用

2.1概述

不管是新增模型,还是修改已有模型后,在编写完模型后,只需要重复1.3预备迁移和正式迁移步骤,会在migrations中自动生成新的迁移文件。即自动实现数据库中的表结构、表关系等信息的修改

2.2 在1.4基础上继续修改已有模型代码

from django.db import models

# Create your models here.
class Question(models.Model):
    question_title = models.CharField(max_length=20)
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField()

2.3预备迁移与正式迁移

在项目根目录的cmd中运行

python manage.py makemigrations model_app

此时出现报错
You are trying to add a non-nullable field ‘question_title’ to question without a default; we can’t do that (the database
needs something to populate existing rows).
Please select a fix:

  1. Provide a one-off default now (will be set on all existing rows with a null value for this column)
  2. Quit, and let me add a default in models.py
    Select an option:
    大概意思就是,本身已有的字段,需要为新的字段设置一个默认值,此时我们给新字段多加一个空值。
    修改模型代码如下
from django.db import models

# Create your models here.
class Question(models.Model):
    question_title = models.CharField(max_length=20, default='')
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField()

再次执行预备迁移命令
执行正式迁移命令

python manage.py migrate model_app

2.4效果展示

在这里插入图片描述

3.Django逆向从数据库表生成模型类

3.1创建新的Django应用

创建新的Django项目(reverse_model_study)与子应用(model_app)方法参考1.1,配置方法参考1.2

3.2 在navicat中新建表

创建新的数据库,然后新建表
在这里插入图片描述

3.2生成模型类

在根目录的cmd中运行:

python manage.py inspectdb > model_app/models.py

偶遇报错django.db.utils.OperationalError: unable to open database file
解决方法:发现是在1.2中的配置mysql出现错误,splite没改成mysql

3.3自动生成表结构

在这里插入图片描述

4.Django项目使用sqlite3数据库

4.1 准备项目

按照1.1方法创建django项目,并配置应用(不配置数据库的)

4.2 创建模型类

在子应用的models中

from django.db import models

# Create your models here.

# 创建模型类
class Student(models.Model):
    sname = models.CharField(max_length=20)
    score = models.CharField(max_length=20)
    age = models.CharField(max_length=20)

4.3 预备迁移与正式迁移

参考1.3与1.5即可
偶遇报错:model_app.Student.score: (fields.E120) CharFields must define a ‘max_length’ attribute.
原因是我的模型中对CharFild的score并没有给最大长度限制(上面的已修改)

4.4 效果展示

在这里插入图片描述

5.字段Field

5.1 字段命名限制

  • 字母,数字,下划线,首字母不能是数字

  • 字段名称不能是Python保留字

  • 由于Django查询查找语法的工作方式,字段名称不能在一行中包含多个下划线,譬如“abc__123”就是不允许的,一个下划线是可以的’abc_123’

5.2 AutoField、ID、主键PRIMARY_KEY

​ 默认会自动创建一个自增,主键的id列

​ 如果指定了 primary_key 为其它列,那么不会自动创建id列

​ 可以在模型中指定(自动生成主键):

id = models.AutoField(primary_key=True)

5.3常见的Filed

所有的Field类型,见 官方网址
在这里插入图片描述
特殊使用:uuid不加括号

import uuid
from django.db import models

class MyUUIDModel(models.Model):
    # uuid.uuid4 千万别写成 uuid.uuid4() ,不要写 ()
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

5.4 Field常见参数

  • max_length:字段最大长度,用于字符串等,字符串类型CharField必须设置该值
  • null:如果True,Django将在数据库中存储NULL空值。默认是False
  • blank:如果True,该字段被允许为空白(“”)。默认是False。请注意,这不同于null。null纯粹是与数据库相关的,而blank与验证相关。如果一个字段有blank=True,表单验证将允许输入一个空值。如果一个字段有blank=False,该字段将是必需的。
  • choices:示例:YEAR_IN_SCHOOL_CHOICES = ((‘FR’, ‘Freshman’),(‘SO’, ‘Sophomore’),(‘JR’, ‘Junior’),(‘SR’, ‘Senior’),(‘GR’, ‘Graduate’)) ,中文示例:SEX_CHOICES=((1, ‘男’),(2, ‘女’)),元组中的第一个元素是将存储在数据库中的值,第二个元素是将在页面中显示的值,最常见用于下拉选择框select
  • default:字段的默认值
  • help_text:用于显示额外的“帮助”文本
  • primary_key:如果True,这个字段是模型的主键,默认是False
  • unique:如果True,该字段在整个表格中必须是唯一的
  • verbose_name:详细字段名,不指定则是属性名的小写,并且用 空格 替换 ‘_’

6.模型之间的关系

6.1 模型一对多关系映射

概述

  • 主外关系中,关联操作最常用的是: models.CASCADE 级联删除(主表子表一起删) 和 models.SET_NULL 设置为null

  • 一对多关系中,ForeignKey 写在一对多关系中,多的那个模型中
    例如:使用django.db.models.ForeignKey,例如,如果一个Car模型有一个Manufacturer,也就是说,一个Manufacturer可以对应多个汽车,但Car只有一个汽车生产商Manufacturer。Car的数量是多种,所以他是外键。

from django.db import models

# Create your models here.

# 主表
class Manufacturer(models.Model):
    name = models.CharField(max_length=20)

# 子表
class Car(models.Model):
    # 外键名是对应类名的小写
    # on_delete 是必须属性(级联删除)
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    name = models.CharField(max_length=20)
    

效果展示

在这里插入图片描述

6.2 模型一对一关系映射

使用django.dbOneToOneField,例如,地址Place和餐馆Restaurant是一对一关系,而餐馆Restaurant和服务员Waiter是一对多关系

![from django.db import models
from django.db.models import CharField,CASCADE,Model
# Create your models here.
class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=100)

    # python内置函数,自动调用,为了后续查询数据时输出
    def __str__(self) -> str:
        return "%s the place" % self.name

class Restaurant(Model):
    # 建立一对一关系
    place = models.OneToOneField(Place, on_delete=CASCADE, primary_key=True)
    # BolleanField 在数据库中使用tinyint类型
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

    def __str__(self) -> str:
        return "%s the restaurant" % self.place.name

class Waiter(Model):
    restaurant = models.ForeignKey(Restaurant, on_delete=CASCADE)
    name = CharField(max_length=150)

    def __str__(self) -> str:
        return "%s the waiter at %s" % (self.name, self.restaurant)

在这里插入图片描述

6.3 模型多对多关系映射

自关联
对于多对多表,例如学生表,一个学生可能会有多位好友。然而好友和学生都是同属于学生表的,这个就是最简易的多对多表——其实他们都是通过生成一张中间表(会自动生成),拆分成一对多的关系进行存储

from django.db import models

# Create your models here.
class Student(models.Model):
    name = models.CharField(max_length=20)
    friends = models.ManyToManyField("self")
    

简易多对多
对于教室和老师关系,每个老师需要在多个教室上课,每个教室会有多个老师来上课。

from django.db import models
class SchoolClass(models.Model):
    name = models.CharField(max_length=20)

class Teacher(models.Model):
    name = models.CharField(max_length=20)
    school_class = models.ManyToManyField(SchoolClass)
    

在这里插入图片描述
自定义中间表
不使用Django中自动生成的中间表:
人与组之间的关系也是一种多对多关系

from django.db import models
class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

class Group(models.Model):
    name = models.CharField(max_length=128)
    # through: 中间表的名称, through_fields:中间表的字段(为两张主表的小写名称为外键)
    members = models.ManyToManyField(Person,through='Membership', through_fields=('group', 'person') )

class Membership(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    level = models.IntegerField(default=1)

在这里插入图片描述

  • 通过 through=‘Membership’ 指定Membership作为中间表
  • 通过 through_fields=(‘group’, ‘person’) 指定中间模型的属性
  • 一般需要自定义中间表时,都是有额外的字段,譬如 level = models.IntegerField(default=1)

7.Django模型之方法

除了运行程序时,可以测试模型,还可以在根目录的cmd执行:
先进入父级目录,执行以下代码

python manage.py shell

​打开django脚本控制台,测试执行模型的方法,会比启动项目更方便

模型对象中有一个objects属性,该属性是管理器Manager类型的对象,几乎所有的方法都是通过该对象执行的,具体见下面的代码:

7.1 新增数据

具体的字段可以从第6点中查看,在VSCode的终端运行
普通模型插入
save或create

from model_app.models import *# model_app是子级目录,models是他模型存储的文件名
# 方法1:create
Question.objects.create(question_title='insert1',question_text='insert_content',pub_date='2020-1-1')
# 方法2:save
p1=Question(question_title='insert1',question_text='insert_content',pub_date='2020-1-1')
p1.save()

一对一关系插入
首先,先增加未使用OneToOneField这一方,查询出来后作为一个对象,作为字段插入另一个使用OneToOneField这一方

from one_one_app.models import *
# 插入一方数据
Place.objects.create(name='北京',address='北京饭店') 
# 方法1:
#先获取到一中一方数据为一个对象:<Place: 北京 the place>
place = Place.objects.get(pk=1) 
# 插入另一张表的数据:<Restaurant: 北京 the restaurant>
Restaurant.objects.create(place=place) 

一对多关系插入
方法1:还是按照一对一方式
方法2:先添加其中一方,然后再创建多方对象,设置外键(可以选择直接给主表的对象,也可以赋值关联的外键),保存对象
这里就演示方法2

# 方法1:
r = Restaurant.objects.get(pk=1) #先查询
Waiter.objects.create(restaurant=r,name="小张") # 插入

# 方法2:赋值外键为对象
r = Restaurant.objects.get(pk=1) #先查询
W = Waiter(name="小李")
W.restaurant = r
W.save()

# 方法2:赋值外键为主键id
w = Waiter(name="小王")                                        
w.restaurant_id = 1
w.save()

多对多关系插入
先对两张主表进行插入数据,用普通模型插入方式,再获取其中一方的对象,使用set方法为中间表赋值。
给中间表赋值有两种方法,一种是set,一种是add。set在二次插入时,会重新赋值,add是增加,遇到重复则保留。

# 先对主表分别插入
Teacher.objects.create(name="李老师") 
Teacher.objects.create(name="王老师") 
Teacher.objects.create(name="张老师") 
SchoolClass.objects.create(name="班级1")
SchoolClass.objects.create(name="班级2") 
SchoolClass.objects.create(name="班级3") 
# 使用set添加中间表
t = Teacher.objects.get(pk=1) 
t.school_class.set([1,2,3]) 
t.school_class.set([1,2])  # 执行完发现,上一条语句内容被覆盖
# 使用add添加中间表
t = Teacher.objects.get(pk=2) 
t.school_class.add(1,2,3) 
t.school_class.add(1,2)  # 执行完发现,上一条语句内容不会被覆盖

7.2 查询数据

7.2.1 常规查询与过滤器

  • 大部分检索是懒惰执行的,只在真实获取值的时候,才会去连接数据库获取数据
  • 查询集通过过滤器进行查询,允许链式调用
  • pk是主键的别名(primary key),如果真实主键是id,那么 pk 和 id 使用是一样的效果
# get 获取一个对象
# 查询主键等于 1 的 , 如果主键是ID,也可以使用 id=1
# 如果条件找不到对应的记录,会抛出 DoesNotExist 错误
# 如果条件找到多个记录,会抛出 MultipleObjectsReturned 错误
>>> from model_app.models import Question
>>> Question.objects.get(id=1) 
<Question: Question object (1)>
>>> Question.objects.get(pk=2) 
<Question: Question object (2)>
# all 获取所有对象
# 查询所有,得到的QuerySets对象
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>
#过滤使用filter
>>> Question.objects.filter(question_title='insert1') 
<QuerySet [<Question: Question object (1)>]>

7.2.2 字段查找

字段查找

  • 字段检索,是在字段名后加 ‘__’ 双下划线,再加关键字,类似 SQL 语句中的 where 后面的部分, 如: 字段名__关键字

  • 在查找中指定的字段必须是模型字段的名称,但有一个例外,如果是ForeignKey字段,则是属性名+ ‘_id’: Entry.objects.filter(blog_id=4) , 定义的 ForeignKey是 blog

  • 完整的字段检索文档:

    https://docs.djangoproject.com/en/2.2/ref/models/querysets/#field-lookups

    常见的字段检索:

    exact :判断是否等于value,一般不使用,而直接使用 ‘=’

    contains:是否包含,大小写敏感,如果需要不敏感的话,使用icontains

    startswith:以value开头,大小写敏感

    endwith:以value结尾,大小写敏感

    in:是否包含在范围内

    isnull:是否为null, 如:filter(name__isnull=Flase)

    gt:大于,如:filter(sage__gt=30) , 年龄大于30

    gte:大于等于

    lt:小于

    lte:小于等于

>>> Question.objects.filter(id__exact=1) 
<QuerySet [<Question: Question object (1)>]>
>>> Question.objects.filter(id=1)        
<QuerySet [<Question: Question object (1)>]>

>>> Question.objects.filter(question_text__contains='i') 
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>
>>> Question.objects.filter(question_text__contains='I') 
<QuerySet []>
>>> Question.objects.filter(question_text__icontains='I') 
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>

>>> Question.objects.filter(question_text__startswith='I') 
<QuerySet []>
>>> Question.objects.filter(question_text__istartswith='I') 
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>
>>> Question.objects.filter(question_text__iendswith='T')   
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>

>>> Question.objects.filter(id__in=(1,2))                 
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>

>>> Question.objects.filter(question_text__isnull=True) 
<QuerySet []>
>>> Question.objects.filter(question_text__isnull=False) 
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>

>>> Question.objects.filter(id__gt=1)                    
<QuerySet [<Question: Question object (2)>]>
>>> Question.objects.filter(id__gte=1) 
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>
>>> Question.objects.filter(id__lt=2)  
<QuerySet [<Question: Question object (1)>]>
>>> Question.objects.filter(id__lte=2) 
<QuerySet [<Question: Question object (1)>, <Question: Question object (2)>]>

7.2.3 日期时间的过滤

year/month/day/week_day/hour/minute/second:时间查询,如: filter(pub_date__year=2015) 年份是2015的, filter(pub_date__day=15) 天数是15的

#时间可以直接使用gt gte lt lte
Account.objects.filter(register_date__gt='2021-7-1')

#__range查询某个时间段
Account.objects.filter(register_date__range=('2021-7-1','2021-7-7'))

#查询某年某月某日 __date
Account.objects.filter(register_date__date='2021-7-6')
<QuerySet [<Account: Account object (2)>, <Account: Account object (3)>]>

#查询某年 __year
# exclude 例外
# 查询 日期年份 不是2006的
persons = Person.objects.exclude(pub_date__year=2006)

# filter 获取对象列表
# 查询 日期年份 是 2006 的
persons = Person.objects.filter(pub_date__year=2006)

# filter 获取对象列表,支持切片,但是不支持负数切片
# limit  5 :前5个
persons = Person.objects.filter(pub_date__year=2006)[:5]
# limit  5,5 : 第6个到10个
persons = Person.objects.filter(pub_date__year=2006)[5:10]
# Entry.objects.all()[-1] 不支持

# 返回前10个记录的, 0 ,2 , 4, 6, 8, 10 ,并且会立刻执行,而不是懒惰执行
Entry.objects.all()[:10:2]

#查询某月 __month
Account.objects.filter(register_date__month=7)

#查询某天 __day
Account.objects.filter(register_date__year=2021,register_date__day=6)
Account.objects.filter(register_date__year=2021).filter(register_date__day=6)

#查询星期几__week_day  注意 from 1(sunday)to 7(saturday)
Account.objects.filter(register_date__week_day=3)

7.2.4 order_by排序

# 正序排列
>>> Question.objects.order_by('pub_date') 
<QuerySet [<Question: Question object (4)>, <Question: Question object (1)>, <Question: Question object (6)>, <Question: Question object (3)>, <Question: Question object (5)>, <Question: Question object (2)>]>
# 倒叙排列
>>> Question.objects.order_by('-pub_date') 
<QuerySet [<Question: Question object (2)>, <Question: Question object (5)>, <Question: Question object (3)>, <Question: Question object (6)>, <Question: Question object (1)>, <Question: Question object (4)>]>
# 多个字段排序
>>> Question.objects.order_by('-pub_date','id') 
<QuerySet [<Question: Question object (2)>, <Question: Question object (5)>, <Question: Question object (3)>, <Question: Question object (6)>, <Question: Question object (1)>, <Question: Question object (4)>]>

7.2.5 查询的其他方法

# count() 对结果集统计
count = Person.objects.filter(pub_date__year=2006).count()

#reverse() 对结果集进行反转 注意使用reverse必须先排序
#比如要取数据的最后10条,可以先反转再切片
Account.objects.all().order_by('id').reverse()[:1]



# first() 返回结果集的第一个对象
person = Person.objects.filter(pub_date__year=2006).first()
person = Person.objects.first()
# last() 返回结果集的最后一个对象
person = Person.objects.filter(pub_date__year=2006).last()

# values() 返回一个 字典对象 列表
person_dict_li = Person.objects.filter(pub_date__year=2006).values()

#values()传入字段
Account.objects.all().values('user_name')
<QuerySet [{'user_name': 'lili'}, {'user_name': 'tom'}, {'user_name': '李四'}]>

Account.objects.all().values('user_name','register_date')
<QuerySet [{'user_name': '李四', 'register_date': datetime.datetime(2021, 7, 1, 3, 18, 18, tzinfo=<UTC>)}, {'user_name': 'tom', 'register_date': datetime.datetime(2021, 7, 6, 1, 52, 54, 5896, tzinfo=<UTC>)}, {'user_name': 'lili', 'register_date': datetime.datetime(2021, 7, 6, 2, 10, 42, 927481, tzinfo=<UTC>)}]>

只有真正取值时候,才会连接数据库获取数据——懒惰查询

7.2.6 多对象关联查询

当我们需要在一对一关系、一对多关系、多对多关系中获取数据时,这就需要使用我们的多对象关联查询

  1. 一对一关系,通过一个模型获取另一个模型
    得先判定当前表是否有关联关系,是两种方法:
# 通过关联的方式,过滤数据
>>> Restaurant.objects.filter(place__address__startswith='北京') 
<QuerySet [<Restaurant: 北京 the restaurant>]>
>>> Restaurant.objects.filter(place__name__startswith='北京')    
<QuerySet [<Restaurant: 北京 the restaurant>]>

# 关联两表
>>> Restaurant.objects.first().place
<Place: 北京 the place>
>>> Place.objects.get(pk=1).restaurant     
<Restaurant: 北京 the restaurant>
  1. 一对多关系,通过一个模型获取另一个模型
    得先判定当前表是否有关联关系,是两种方法:
# 先获取id=1的餐厅信息,再查询到其中的关联的waiter表中的所有信息
>>> r = Restaurant.objects.get(pk=1) 
>>> r.waiter_set.all()
<QuerySet [<Waiter: 小张 the waiter at 北京 the restaurant>, <Waiter: 小李 the waiter at 北京 the restaurant>, <Waiter: 小
王 the waiter at 北京 the restaurant>]>
  1. 多对多关系,通过一个模型获取另一个模型
    得先判定当前表是否有关联关系,是两种方法:
# 当前表存在关联关系(ManyToManyField):直接调用关联关系字段
>>> t = Teacher.objects.first()
>>> t
<Teacher: Teacher object (1)>
>>> t.school_class.all()
<QuerySet [<SchoolClass: SchoolClass object (1)>, <SchoolClass: SchoolClass object (2)>]>
# 不存在关联关系:使用set方法
>>> s = SchoolClass.objects.first() 
>>> s
<SchoolClass: SchoolClass object (1)>
>>> s.teacher_set.all()
<QuerySet [<Teacher: Teacher object (1)>, <Teacher: Teacher object (2)>]>

7.2.7 聚合函数

使用aggregate()函数返回聚合函数的值

常用的聚合函数有:Avg、Count、Max、Min、Sum

>>> from model_app.models import *
>>> from django.db.models import *
# 常规查询
>>> Question.objects.aggregate(Max('id')) 
{'id__max': 6}
# 设置结果名称
>>> Question.objects.aggregate(idMax=Max('id'))   
{'idMax': 6}
# 连续使用聚合函数
>>> Question.objects.aggregate(idMax=Max('id'), idMin=Min('id'), idAvg=Avg('id')) 
{'idMax': 6, 'idMin': 1, 'idAvg': 3.5}

7.2.8 分组查询

使用annotate()函数实现分组查询,得配合其他函数:

  • annotate:用于分组,配合 Avg,Count等聚合函数,如:annotate(max=Max(‘age’))
  • filter: 用于过滤,在 annotate之前使用表示 where 条件,在annotate之后使用表示having条件
  • values:在annotate之前使用表示分组字段,在annotate之后使用表示取值
# 对于每个老师都有不同的班级,每个班级都有不同的老师,是多对多关系
# 查询每个班级有多少位老师(存在关联关系,就可以利用中间表取查询统计)
Teacher.objects.values('school_class').annotate(tcount=Count('id')) 
<QuerySet [{'school_class': 1, 'tcount': 2}, {'school_class': 2, 'tcount': 2}, {'school_class': 3, 'tcount': 1}, {'school_class': None, 'tcount': 1}]>
# 查询每个老师教了多少班级(不存在关联关系字段,先通过班级关联另一张表,再过滤)
>>> SchoolClass.objects.values('teacher').annotate(tcount=Count('id')) 
<QuerySet [{'teacher': 1, 'tcount': 2}, {'teacher': 2, 'tcount': 3}]>

# 过滤 ,结果一样,但是第二种的效率更高(where效率高于having)
>>> SchoolClass.objects.values('teacher').annotate(tcount=Count('id')).filter(name__contains='班级2') 
<QuerySet [{'teacher': 1, 'tcount': 1}, {'teacher': 2, 'tcount': 1}]>
>>> SchoolClass.objects.values('teacher').filter(name__contains='班级2').annotate(tcount=Count('id')) 
<QuerySet [{'teacher': 1, 'tcount': 1}, {'teacher': 2, 'tcount': 1}]>

7.3 修改数据

单条语句修改,获取对象后,直接对对象的属性名进行修改,再save
多条语句修改,先获取多条语句对象,再使用update就行

>>> q = Question.objects.first()
>>> q.question_title = 'update title' 
>>> q.save()
>>> Question.objects.filter(pub_date__day='1').update(question_title='update titles') 
2

7.4 删除数据

删除数据直接获取对象,然后执行delete方法即可

>>> Question.objects.filter(pub_date__day='1').delete()                              
(2, {'model_app.Question': 2})

7.5 刷新对象

当我们从数据库先获取了一个对象,但是如果这之后进行了修改,需要刷新对象,重新获取。就避免了再一次的编写获取对象的代码

>>> q = Question.objects.first()
>>> q.question_text
'insert2_content'
# 此时在navicat中修改数据,发现还是没变化
>>> q.question_text
'insert2_content'
>>> q.refresh_from_db()
>>> q.question_text     
'insert2_content7777'

7.6 模型Q对象的使用

Q对象就是完成过滤时的且、或、非等操作
filter() 等方法中的关键字参数查询都是并且(‘AND’)的, 如果你需要执行更复杂的查询(例如or语句),那么可以使用Q 对象。

​ Q 对象 (django.db.models.Q) 对象用于封装一组关键字参数,可以使用 & 和 | 操作符组合起来,当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。

>>> from django.db.models import Q

>>> Question.objects.filter(Q(pub_date__year='2021') | Q(question_text__startswith='O'))
<QuerySet [<Question: Question object (3)>, <Question: Question object (4)>]>

>>> Question.objects.filter(Q(pub_date__year='2021') & Q(question_text__startswith='O') | Q(question_text__startswith='N'))
<QuerySet [<Question: Question object (3)>]>

>>> Question.objects.filter(Q(pub_date__year='2021'))                                                                     
<QuerySet [<Question: Question object (3)>]>
>>> Question.objects.filter(~Q(pub_date__year='2021')) 

<QuerySet [<Question: Question object (2)>, <Question: Question object (4)>, <Question: Question object (6)>]>
>>> Question.objects.filter(Q(pub_date__year='2021'), pub_date__year='2021') 
<QuerySet [<Question: Question object (3)>]>

# 当Q对象和普通的使用一起时,Q对象需放在前面
>>> Question.objects.filter(pub_date__year='2021',Q(pub_date__year='2021'))  
  File "<console>", line 1
    Question.objects.filter(pub_date__year='2021',Q(pub_date__year='2021'))
                                                                          ^
SyntaxError: positional argument follows keyword argument

7.7 模型F对象的使用

模型的属性名出现在操作符的右边,就使用F对象进行包裹

# 找出女生数量大于男生数量的年级
# 对应sql:select * from grade where girlnum > boynum
grades = Grade.objects.filter(girlnum__gt=F('boynum'))
# 找出女生数量大于 男生数量+10 的年级
# 对应的sql: select * from grade where girlnum > boynum + 10
Grade.objects.filter(girlnum__gt=F('boynum') + 10)

# 所有书籍的价格 +1
# 对应的 sql: update book set price = price + 1 
Book.objects.update(price=F("price")+1)

8.sql语句的使用

8.1通过模型使用sql

通过raw函数执行原始的sql语句进行查询

# 定义个 person 模型
class Person(models.Model):
    first_name = models.CharField()
    last_name = models.CharField()
    birth_date = models.DateField()

# 执行 原始 SQL
# 表名前面必须加 应用名myapp,即在数据库中的真实表名,否则会抛出异常
for p in Person.objects.raw('SELECT * FROM myapp_person'):
    print(p)

# 字段先后顺序没关系
Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person')
# 等同于
Person.objects.raw('SELECT last_name, birth_date, first_name, id FROM myapp_person')

# 可以从其他表格中查询出匹配 person 模型的记录集
# 总之最终的数据集的结构必须和 Person一样
Person.objects.raw('SELECT first AS first_name, last AS last_name,bd AS birth_date,pk AS id,FROM some_other_table')

# 返回的结果集一样可以执行切片
first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]
# 但是上述语句会返回所有结果,基于节省传输的需要,在数据库缩小结果集范围更正确
first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]


# 传递参数
lname = 'Doe'
Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])

不应用模型,直接执行sql

from django.db import connection

def my_custom_sql(obj):
    with connection.cursor() as cursor:
        cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [obj.baz])
        cursor.execute("SELECT foo FROM bar WHERE baz = %s", [obj.baz])
        row = cursor.fetchone()

    return row