目录
为查询创建一个博客应用模型
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField()
number_of_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __str__(self):
return self.headline
创建对象
>>> from myapp.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()
上面的代码会执行一条SQL的INSERT语句. 如果不调用save()方法, Django不会立刻执行该操作.
提示 :
在代码或者shell中使用API的方式直接创建模型实例是不会引发数据验证的.
上面的diam可以简写为 :
b = Blog.objects.create(name='Beatles Blog', tagline='All the latest Beatles news.')
修改对象并保存
使用save()方法, 保存对象数据库已有对象的修改, 例如如果已经存在b对象在数据库内 :
>>> b.name = 'New name'
>>> b.save()
在后台会运行一条SQL的UPDATE语句.
可以重新编写save方法, 添加其他的业务逻辑
from django.db import models
class Blog(models.Model):
name = models.CharField(lmax_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something() #保存前添加其他的业务逻辑
super().save(*args, **kwargs)
do_something_else() # 保存后再添加其他的业务逻辑
super().save(*args, **kwargs)
这行确保了Django源码中关于save方法的代码依然会被执行
模型实例保存的注意事项
- 执行save()方法后, Django才会真正为对象设置自增主键的值.
>>> b1 = Blog(name='laowang', tagline='kkkkkk')
>>> b1.id # 返回None
>>> b1.save()
>>> b1.id
>>> 2
- 也可以自己指定主键的值, 要确保自己指定的主键值是没有被使用过的
>>> b2 = Blog(id=4, name='xiaoming', tagline='ggggg')
>>> b2.save()
>>> b2.id
4
- 有时候必须自己强制进行INSERT或者UPDATE操作, 而不是让Django自动决定
- 数据冲突的问题, 使用
F表达式
>>> from django.db.models import F
>>> entry = Entry.objects.get(name='laowang')
>>> entry.number_of_pingbacks = F('number_of_pingbacks') + 1
>>> entry.save()
- save方法的最后一个参数是update_fields, 它用于指定你要对模型的那些字段进行更新.
>>> blog.name = 'name'
>>> blog.save(update_fields=['name'])
保存外键和多对多字段
>>> from myapp.models import Blog, Entry
>>> blog = Blog.objects.get(pk=1)
>>> entry = Entry.objects.create(blog=blog, headline='laowang blog', body_text='hhhhhhh', number_of_comments=1, number_of_pingbacks=1,rating=1)
这里有一个blog的实例, 把blog的实例作为值赋给了entry的blog属性
多对多字段的保存
多对多字段的保存需要调用一个add()方法
>>> from myapp.models import Author
>>> joe = Author.objects.create(name="joe")
>>> entry = Entry.objects.get(pk=1)
>>> entry.authors.add(joe)
在一行语句内, 可以同时添加多个对象到多对多的字段
>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)
检索对象
- 从数据库检索对象, 需要基于模型类, 通过管理器操作数据库并返回一个查询结果集(QuerySet)
检索所有对象
使用all()方法
>>> all_entries = Entry.objects.all()
>>> all_entries
<QuerySet [<Entry: laowang blog>]>
过滤对象
filter(**kwargs)
: 返回一个根据指定参数查询出来的QuerySetexclude(**kwargs)
: 返回除了根据指定参数查询出来结果的QuerySet
例如 : 获取2020年的所有文章
>>> Entry.objects.filter(pub_date__year=2020)
<QuerySet [<Entry: laowang blog>]>
QuerySets都是懒惰的
一个创建QuerySets的动作不会立刻导致任何的数据库行为.
>>> q = Entry.objects.filter(headline__startswith="laowang")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains='aa')
>>> print(q)
<QuerySet [<Entry: laowang blog>]>
上面的代码, 在print语句执行时才会访问一次数据库.
检索单一对象
使用get()方法检索会获得一个对象
>>> one_entry = Entry.objects.get(pk=1)
- 如果在查询时没有匹配到对象, get()方法将抛出
DoesNotExist
异常 - 如果查询结果超过1个, 则会抛出
MultipleObjectsReturned
QuerySet的切片
>>> Entry.objects.all()[:5] # 获取前5个对象
>>> Entry.objects.all()[5:10] # 获取第6个到第10个对象
注意 : 不支持负索引
- 通常情况, 切片操作会返回一个新的QuerySet, 并且不会被立刻执行. 有一个例外, 那就是指定步长的时候, 查询操作会立刻在数据库内执行
>>> Entry.objects.all()[:10:2]
[<Entry: laowang blog>]
- 若要获取单一的对象可以使用索引
>>> Entry.objects.order_by('headline')[0]
字段查询
基本格式为 : field__lookuptype=value
例如 :
>>> Entry.objects.filter(pub_date__lte='2021-01-01')
查询外键字段需要在其后面添加一个 _id
>>> Entry.objects.filter(blog_id=1)
<QuerySet [<Entry: laowang blog>]>
查询类型
exact
默认类型. 不提供查询类型, 或者关键字参数不包含一个双下划线, 那么查询类型就是默认的exact.
iexact
不区分大小写的查询
# 匹配"LaoWang", "LAOWANG" 等.
>>> Blog.objects.get(name__iexact="laowang")
contains
包含类型的匹配, 区分大小写
>>> Entry.objects.get(headline__contains='lao')
# 相当于
# select ... where headline like '%lao%';
icontains
contains的大小写不敏感模式
startswith和endswith
以什么开头和以什么结果
istartswith和iendswith
不区分大小写的模式
in
在给定的列表里查找
>>> Entry.objects.filter(id__in=[1, 2, 3])
还可以使用动态查询集
>>> blog_name = Blog.objects.filter(name__contains='Beatles Blog')
>>> entries = Entry.objects.filter(blog__in=blog_name)
>>> print(entries)
gt
大于
get
大于或等于
lt
小于
lte
小于或等于
range
查询的范围
>>> start_date = datetime.date(2020,12,31)
>>> end_date = datetime.date(2021,1,2)
>>> Entry.objects.filter(pub_date__range=(start_date, end_date))
<QuerySet [<Entry: laowang blog>]>
date
进行日期对比
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
year
对年份进行匹配
Entry.objects.filter(pub_date__year=2021)
month
对月份进行匹配
Entry.objects.filter(pub_date__month=12)
day
对具体到某一天的匹配
week_day
进行星期几的匹配
>>> Entry.objects.filter(pub_date__week_day=6)
<QuerySet [<Entry: laowang blog>]>
iso_week_day
以ISO8601标准确定的星期几.
>>> Entry.objects.filter(pub_date__iso_week_day=6)
quarter
季度, 取值范围1-4
>>> Entry.objects.filter(pub_date__quarter=2)
time
将字段的值转为datetime.time格式并进行对比
hour
对小时进行匹配。 取0和23之间的整数。
minute
对分钟匹配。取0和59之间的整数。
second
对秒数进行匹配。取0和59之间的整数。
regex
区分大小写的正则表达式匹配。