python_new
  • Introduction
  • First Chapter
  • 一、python基础
    • 1.1 常识
      • sys
    • 1.2 基础语法(1)
    • 1.2 基础语法(2)
    • 1.3 常见问题求解
    • 1.4 算法
  • 二、爬虫
    • urllib库(1)
      • urllib库(2)
    • requests模块(1)
      • requests模块(2)
    • 2.1 爬虫基础(3)HTTP原理
    • 2.1 爬虫基础(4)会话和Cookies
    • 2.1 爬虫基础(5)数据存储
      • Mysql存储
      • MongoDB存储
      • Redis存储
    • 2.3 正则表达式
    • 2.4 解析库lxml
      • BeautifulSoup
      • pyquery(1)
      • pyquery(2)
    • 2.5 selenium(1)
    • 2.5 seleium(2)
    • 2.6 Json
    • 2.7 scrapy
      • scrapy(2)
    • 2.9 异步加载
    • 2.6 Splash
  • ORM框架
    • SQLAlchemy
  • Django
    • 1、初阶(一)
    • 初学:投票教程(一)
    • 初学:投票教程(二)
    • 初学:投票教程(三)
    • 初学:投票教程(总结)
    • 模型(一)
    • 模型(二)
    • 视图(一)
    • 视图(二)
    • 模板(一)
    • django实际使用笔记
  • 面试题收集总结
    • 数据结构原理
    • 算法篇
      • 排序
    • 题目篇
  • python数据分析
    • 基础了解(一)
    • 基础了解(二)
    • 基础了解(三)
  • 多线程
  • 深度学习
    • 疑问
  • keras(一)
  • 神经网络
  • 图像识别
  • Docker
    • 一、基础了解
Powered by GitBook
On this page
  • 一、创建对象
  • 二、修改对象
  • 三、检索对象
  • 四、字段查询
  • 五、跨关系查找
  • 六、删除对象
  • 七、一次更新多个对象
  • 八、相关对象或者反转manager

Was this helpful?

  1. Django

模型(二)

Previous模型(一)Next视图(一)

Last updated 6 years ago

Was this helpful?

在第一部分中,主要是关于如何创建更好数据模型用来存储数据。而在django中,还有一个部分也很重要。那就是关于提取和使用数据,而这就涉及到这些模型类的方法。

参考资料:

一旦建立好数据模型之后,django会自动给你一个数据库抽象api来让你创建、撤回、更新和删除这些对象。

范例

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)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):
        return self.headline

一、创建对象

使用save()方法

from blog.models import Blog

b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
b.save()

只有执行了save()方法,所有对对象做的操作才会保存到数据库层次。

使用create()可以完成创建和保存的操作。

二、修改对象

1、普通字段类型

同样使用save()方法

b5.name = 'New name'
b5.save()

2、外键字段

外键与普通的完全一致

from blog.model import Blog, Entry

entry = Entry.objects.get(pk=1)
cheese_blog = Blog.objects.get(name="Cheddar Talk")
entry.blog = cheese_blog
entry.save()

哇,还真的是很简单呢

3、多对多类型的字段

之前也提到过,普通的方法不适用于使用了关系表的多对多模型,但是这里好像还是可以使用add方法,是不是因为没有使用关系表呢?确实没有!

from blog.models import Author
# add one record
joe = Author.objects.create(name = 'Joe')
entry.authors.add(joe)
# add multiple records
join = Author.objects.create(name = 'John')
paul = Author.objects.create(name = 'Paul')
george = Author.objects.create(name = 'George')
ringo = Author.objects.create(name = 'Ringo')
entry.author.add(john, paul, george, ringo)

三、检索对象

为了检索对象,需要通过Manager构造QuerySet。一个QuerySet代表着多个数据库记录的对象,他可以有零个或多个筛选条件,类似于sql中的select语句。

QuerySet是通过manager得到的,每一个模型至少有一个Manager,默认为objects。

>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
    ...
AttributeError: "Manager isn't accessible via Blog instances."

Manger是模型QuerySet的主要来源,比如Blog.objects.all(),这个会返回所有Blog模型中的对象。

1、检索所有对象

all_entries = Entry.objects.all()

2、检索特定对象

两种方式:

  • filter(**kwargs):返回所有符合条件的对象

  • exclude(**kwargs):返回所有不符合条件 对象

示例:

Entry.ojects.filter(pub_date__year=2006)
Entry.objects.all().filter(pub_date__year=2006)
# same result

我终于明白了。前面的是模型中的字段,而双下划线后面的是这个字段的属性!慢着,不是还有一个get吗?

这是错误的理解。

3、链式筛选

 Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime.date(2005, 1, 30)
... )

这个可能跟我们想的不一样。。。

4、懒惰的QuerySet

大部分queryset筛选操作不会涉及到数据库,如下

q = Entry.objects.filter(headline__startswith='What')
q = q.filter(pub_date__lte=datetime.date.today())
q = q.exclude(body_text__icontains='food')
print(q)

1、gt:大于某个时间

now = datetime.datetime.now()

#前一天

start = now – datetime.timedelta(hours=23, minutes=59, seconds=59)

a=yourobject.objects .filter(youdatetimcolumn__gt=start)

2、gte:大于等于某个时间:

a=yourobject.objects .filter(youdatetimcolumn__gte=start)

3、lt:小于

a=yourobject.objects .filter(youdatetimcolumn__lt=start)

4、lte:小于等于

a=yourobject.objects .filter(youdatetimcolumn__lte=start)

5、range:查询时间段

start_date = datetime.date(2005, 1, 1)

end_date = datetime.date(2005, 3, 31)

Entry.objects.filter(pub_date__range=(start_date, end_date))

qs.filter(name__contains="e")

qs.filter(name__icontains="e")

对应sql

'contains': 'LIKE BINARY %s', 其中的BINARY是 精确大小写

'icontains': 'LIKE %s', 而’icontains’中的’i’表示忽略大小写

其中只有最后一步print涉及到数据库,

5、用get获取单个对象

filter永远都会返回一个QuerySet,即使只有一个符合条件的对象。而get()方法可以保证回复一个对象

one_entry = Entry.objects.get(pk=1)

值得注意的是,符合筛选条件的对象只能有一个,如果没有或者多个,都会导致报错。

Note that there is a difference between using get(), and using filter() with a slice of [0].

这是什么意思?

6、其他的查询方式

除了get、all、filter、exclude等,django中还有很多很多查询方式。

7、使用分片限制查询结果

Entry.objects.all()[:5]
Entry.objects.all()[5:10]
# 但是负值不支持
Entry.objects.all()[-1]

一般来说,切片不会影响原本的查询结果,但是有一个例外,那就是使用跳步的方式切片,这样的话query中会 修改部分来得到这个结果

Entry.objects.all()[:10:2]

然后这个分片只能执行一次,为了避免歧义。

四、字段查询

基本的格式是field__lookuptype=value

Entry.objects.filter(pub_date__lte='2006-01-01')

一般来说,查询的字段名必须与模型属性名一致,但是有一个例外情况,那就是外键,它可以以id为后缀,但是也只能在主键没有设置,默认为id的情况下才可以吧?

Entry.objects.filter(blog_id=4)

1、exact

Entry.objects.get(headline__exact="Cat bites dog")
Entry.objects.get(headline="Cat bites dog")

当然这也是默认的情况,如果不加后面的exact,直接相等也是一样的

2、iexact

Entry.objects.get(name__iexact="beatles blog")
# match "Beatles Blog"/"beatles blog"/"BeAtlES blOG"

忽略大小写

3、contains

Entry.objects.get(headline__contains='Lennon')

也就是匹配包含这个字符串的

icontains忽略大小写

4、startswith,endwith

之前我以为是调用的是字符串的方法,但是看到这里才知道不是。

istartwith, iendwith忽略大小写

五、跨关系查找

django这种跨关系查找的功能确实很让人吃惊。

Entry.objects.filter(blog__name='Beatles Blog')

上面的例子是沿着外键去查外键连接的数据表,django甚至还可以反过来查询,这时只需要把表名小写放在前面即可。

Blog.objects.filter(entry__headline__contains='Leenon')

如果全部查完之后,没有找到一个值,django也不会报错,只是返回空的对象。哪怕查的字段在表中都不存在(假如headline不存在entry),也不会报错。

1、多值查询

先看两个例子

# 1
Blog.objects.filter(entry__headline__contains='Lennon',entry__pub_date__year=2008)
# 2
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_datr__year=2008)

我之前一直以为第二个是指同时满足两个条件的查询,但现在看起来并不是,方式一才是同时满足两个条件查询,而方式是指满足条件一和满足条件二的结果的返回。那假如我把满足条件一的queryset保存为q,再对q做filter操作,那算是什么呢?应该是在q的结果中继续筛选吧?

2、比较操作

有时候我们并不是想直接与一个常量比较,而希望跟同一个模型中的另一个字段的值作比较,这s时候可以使用F expression

from django.db.models import F

Entry.objects.filter(n_comments_gt=F('n_pingbacks'))

或者

Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
Entry.objects.filter(authors__name=F('blog__name'))

靠,居然还支持位操作!

.bitand(), .bitor(), .bitrightshift(), and .bitleftshift().

3、pk快速查找

pk也就是primary key,使用pk快速查找可以实现快速查找对象

Blog.objects.get(id__exact=14)
Blog.objects.get(id=14)
Blog.objects.get(pk=14)
# 三个都是相同的
Blog.objects.filter(pk__in=[1,4,7])
Blog.objects.filter(pk__gt=14)

4、缓存queryset

每当新建一个queryset的时候,缓存其实是空的,当它真的查询数据库的时候,才会将结果缓存起来。

print([e.headline for e in Entry.objects.all()])
print([e.pub_date for e in Entry.objects.all()])

如果这样操作的话,会进行两次数据库的查询,因为是两次,所以在这期间,数据库可能会被修改,也因此,对应的数据可能会发生改变。

>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again

>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache

为了避免上面的情况,保证取出的数据是同一个数据,这时候就可以执行缓存。

5、使用Q来实现复杂的查询

之前的查询主要用到的查询条件是“and”,如果需要使用其他的查询条件的话,可以使用Q 对象。

from django.db.modelsimport Q

Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

目前我看到就是可以使用or,应该还有很多类型。

六、删除对象

简单使用实例对象的delete方法即可

e.delete()
# 也可以这样
Entry.objects.filter(pub_date__year=2005).delete()

这里值得注意的是,如果你已经自定义了delete方法,就不能使用第二方方法来删除多个实例对象,只能通过循环一个一个地调用实例对象的delete方法来实现。还有就是,django默认的是ON DELETE CASCADE,也就是说当在数据库中删除一个记录时,它会自动删除所有连接到它的外键记录。而这个可以在设置外键的时候修改。

同时,delete也是唯一一个queryset有但是manager没有的方法,这是为了避免意外删除所有对象,如果想要删除全部的对象,可以

Entry.objects.all().delete()

七、一次更新多个对象

使用update()方法

Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')

只有非关系型字段和外键字段可以使用这个方法,如果是菲关系型,就设置为对应的数据类型常量即可,如果是外键,可以设置为某个模型的实例。这个方法会返回修改的记录数。唯一一个麻烦一点的是一个模型的主键,等会

The only restriction on the QuerySet being updated is that it can only access one database table: the model's main table. You can filter based on related fields, but you can only update columns in the model's main table.

不对吧,前面的pub_date并不是主键啊,为什么它说只能通过主键才能找到?而且主键往往是唯一值,也不可能对应多个值,这样update不就没有意义了吗?

这里并不是主键,看一下例子就明白了,是外键的问题

b = Blog.objects.get('pk=1')
Entry,objects.select_related().filter(blog=b).update(headline='Everything is the same')

当它想要找所有外键连接到某个特定的记录时,才会用这就related的方式。

另外,update不需要save,它会直接执行到数据库上。

八、相关对象或者反转manager

相关对象的额外操作

https://docs.djangoproject.com/zh-hans/2.0/topics/db/queries/