一个建立好的模型,django会生成一套完整的API,以便对对象进行crud操作,下面就详细介绍crud操作。
先说一下操作环境: python2.7, django1.11.8, win7系统,借助于pycharm工具。
from django.db import models
# Create your models here.
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __unicode__(self): # __self__ on Python 3
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __unicode__(self): # __self__ on Python 3
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog)
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 __unicode__(self): # __self__ on Python 3
return self.headline
Django 使用一种直观的方式把数据库表中的数据表示成Python 对象:一个模型类代表数据库中的一个表,一个模型类的实例代表这个数据库表中的一条特定的记录。
首先讲述单一对象(也就是不跨模型的crud操作)。
增加对象数据
使用关键字参数实例化模型实例来创建一个对象,然后调用
save()
把它保存到数据库中。
>>> from mysite import models #首先导入models模块
#第一种创建一个Blog对象,传参,调用save函数,需要注意只有在调用save函数的时候,才会与数据库交互。
>>> b = models.Blog(name="first", tagline="one")
>>> b.save()
#第二种: 创建一个objects对象直接调用create方法,保存数据
>>> models.Blog.objects.create(name="second", tagline="two")
<Blog: second>
#第三种方法和第一种差不多,只不过把传参过程单独写了出来
>>> bg = models.Blog()
>>> bg.name = "third"
>>> bg.tagline = "three"
>>> bg.save()
#第四种:会尝试着先获取数据,若是数据不存在,则再创建。返回结果是一个布尔值,创建成功返回True,若是值已经存在则返回的是False。
>>> models.Blog.objects.get_or_create(name="fourth", tagline="four")
(<Blog: fourth>, True)
#上面的四种方法都是向数据库中写入了数据,实质均是转为insert语句而已。
获取全部的数据:
>>> models.Blog.objects.all()
<QuerySet [<Blog: first>, <Blog: second>, <Blog: third>, <Blog: fourth>, <Blog: first>, <Blog: second>]>
>>> models.Blog.objects.all()[:2]
<QuerySet [<Blog: first>, <Blog: second>]>
#all():方法会获取对象中所有的数据,但是返回的结果是一个QuerySet对象。
#返回的结果是一个列表,因此支持切片操作。
过滤查询: 就像select语句一样,通常我们的查询都是有条件限制,不可能是获取全部的所有数据。
QuerySet查询有两个过滤设置:
filter(**kwargs):
返回一个新的
QuerySet
,它包含满足查询参数的对象。
exclude(**kwargs):
返回一个新的
QuerySet
,它包含
不
满足查询参数的对象。
>>> models.Blog.objects.all().filter(name="first") #返回符合条件的数据
<QuerySet [<Blog: first>, <Blog: first>]>
>>> models.Blog.objects.all().exclude(name="first") #返回不符合条件的数据
<QuerySet [<Blog: second>, <Blog: third>, <Blog: fourth>, <Blog: second>]>
#链式过滤,先过滤出不包含name="first"的对象,然后再过滤出tagline="three"的对象
>>> models.Blog.objects.all().exclude(name="first").filter(tagline="three")
<QuerySet [<Blog: third>]>
QuerySet是惰性的,创建QuerySet对象不会造成任何数据库的访问,只有在使用这个QuerySet对象才会通过数据库访问获取数据。
get返回一个QuerySet对象:
上面的查询返回的QuerySet对象一般都不是一个结果,若是想要获取一个结果,可以使用get方法。
>>> models.Blog.objects.get(name="third") #返回一个结果
<Blog: third>
>>> models.Blog.objects.get(name="first") #若是查询出来的结果又多个,则get会报错
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "C:\Python27\lib\site-packages\django-1.10.8-py2.7.egg\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Python27\lib\site-packages\django-1.10.8-py2.7.egg\django\db\models\query.py", line 389, in get
(self.model._meta.object_name, num)
MultipleObjectsReturned: get() returned more than one Blog -- it returned 2!
查询结果的values方法与value_list方法。
values(*fields, **expression): 正常的查询返回的是一个模型实例的可迭代对象,values返回的是一个模型字典的可迭代对象。
>>> models.Blog.objects.values() #返回的是一个每个记录的key-value构成的字典。注意多了一个id字段,这个id是django自动创建的主键。
<QuerySet [{'tagline': u'one', u'id': 1, 'name': u'first'}, {'tagline': u'two', u'id': 2, 'name': u'second'}, {'tagline': u'three', u'id': 3, 'name': u'third'}, {'tagline': u'four', u'id': 4, 'name': u'fourth'}, {'tagline': u'one', u'id': 5, 'name': u'first'}, {'tagline': u'two', u'id': 6, 'name': u'second'}]>
>>> models.Blog.objects.values("id","name") #fields参数,类似于select查询中要查询的字段,也就是显示指定的字段。
<QuerySet [{'id': 1, 'name': u'first'}, {'id': 2, 'name': u'second'}, {'id': 3, 'name': u'third'}, {'id': 4, 'name': u'fourth'}, {'id': 5, 'name': u'first'}, {'id': 6, 'name': u'second'}]>
values还有一个**expression参数,这是一个关键字参数,values会把关键字参数传递给
annotate()方法。django1.11之后支持**expression参数。
values_llist(
*fields
,
flat=False
): 与values类似,只不过返回的是元组而不是字典。
>>> models.Blog.objects.values_list()
<QuerySet [(1, u'first', u'one'), (2, u'second', u'two'), (3, u'third', u'three'), (4, u'fourth', u'four'), (5, u'first', u'one'), (6, u'second', u'two'), (7, u'SECOND', u'ABC')]>
>>> models.Blog.objects.values_list("id","name")
<QuerySet [(1, u'first'), (2, u'second'), (3, u'third'), (4, u'fourth'), (5, u'first'), (6, u'second'), (7, u'SECOND')]>
当只查询一个字段时,设置flat为True,返回的结果不再是一个元组,而是单个值。若设置为False,则返回的仍然是元组。但是,查询有多个字段时,设置为True会报错。
>>> models.Blog.objects.values_list("id", flat=True)
<QuerySet [1, 2, 3, 4, 5, 6, 7]>
>>> models.Blog.objects.values_list("id", flat=False)
<QuerySet [(1,), (2,), (3,), (4,), (5,), (6,), (7,)]>
>>> models.Blog.objects.values_list("id","name", flat=False)
<QuerySet [(1, u'first'), (2, u'second'), (3, u'third'), (4, u'fourth'), (5, u'first'), (6, u'second'), (7, u'SECOND')]>
>>> models.Blog.objects.values_list("id","name", flat=True) #报错
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "C:\Python27\lib\site-packages\django-1.10.8-py2.7.egg\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Python27\lib\site-packages\django-1.10.8-py2.7.egg\django\db\models\query.py", line 729, in values_list
raise TypeError("'flat' is not valid when values_list is called with more than one field.")
TypeError: 'flat' is not valid when values_list is called with more than one field.
查询中的一些关键字使用以及正则表达式简单使用。
#exact精确匹配,默认的就是这种匹配方式。
>>> models.Blog.objects.all().filter(name="second")
<QuerySet [<Blog: second>, <Blog: second>]>
>>> models.Blog.objects.all().filter(name__exact="second") #这里是双下划线
<QuerySet [<Blog: second>, <Blog: second>]>
#iexact忽略大小写
>>> models.Blog.objects.all().filter(name__iexact="second")
<QuerySet [<Blog: second>, <Blog: second>, <Blog: SECOND>]>
#contains大小写敏感,包含关系测试;icontains大小写不敏感,不包含关系测试
在数据库中修改数据,方便做测试。
>>> models.Blog.objects.all().filter(name__contains="second")
<QuerySet [<Blog: second>, <Blog: one second >]>
>>> models.Blog.objects.all().filter(name__icontains="second")
<QuerySet [<Blog: second>, <Blog: one second >, <Blog: one SECOND >]>
#startwith和endwith分别表示以“xxx”开始和以“xxx”结尾。
Person.objects.filter(name__regex="^abc") # 正则表达式查询
Person.objects.filter(name__iregex="^abc")# 正则表达式不区分大小写
更新数据和sql语句类似,查找到某一条数据更新,查找到某类型数据的集合更新。
>>> models.Blog.objects.all().filter(name__icontains="second")
<QuerySet [<Blog: second>, <Blog: one second >, <Blog: one SECOND >]>
>>> models.Blog.objects.values().filter(name="third")
<QuerySet [{'tagline': u'three', u'id': 3, 'name': u'third'}]>
>>> models.Blog.objects.values().filter(name="third").update(tagline="Fuck") #更新一个特定的记录
>>> models.Blog.objects.values().filter(name="third")
<QuerySet [{'tagline': u'Fuck', u'id': 3, 'name': u'third'}]>
>>> models.Blog.objects.all().update(tagline="Wonderful") #批量更新,慎用!
>>> models.Blog.objects.values("tagline")
<QuerySet [{'tagline': u'Wonderful'}, {'tagline': u'Wonderful'}, {'tagline': u'Wonderful'}, {'tagline': u'Wonderful'}, {'tagline': u'Wonderful'}, {'tagline': u'Wonderful'}, {'tagline': u'Wonderful'}]>
删除数据和更新一样,批量删除,和删除特定的数据。
>>> models.Blog.objects.all().filter(name="fourth").delete()
(1, {u'mysite.Blog': 1})
>>> models.Blog.objects.all().filter(name="first").delete()
(2, {u'mysite.Blog': 2})
django中自带的QuerySet API还是蛮多的,不太可能一一列举完成,这里给出文档连接:
QuerySet API
跨模型操作
上面的crud操作都是针对单个模型的,下面来说明跨模型的操作。
有下面的model,我们根据下面的model来说明夸模型操作的应用。
class Business(models.Model): # django中会默认创建主键id
caption = models.CharField(max_length=32)
code = models.CharField(max_length=32)
class Host(models.Model):
nid = models.AutoField(primary_key=True)
hostname = models.CharField(max_length=50, db_index=True)
ip = models.GenericIPAddressField(protocol="IPV4")
port = models.IntegerField()
b = models.ForeignKey(to="Business", to_field="id")
class Application(models.Model):
name = models.CharField(max_length=64)
r = models.ManyToManyField("Host")
多对一关系的查询与添加:
插入外键的数据:
上面的models可知,host的字段b关联到Business中的隐藏主键(id),Business中的数据如下:
我们向host表中写入数据如下:
>>> from mysite import models
>>> h1 = models.Host()
>>> h1.hostname = "nginx-one"
>>> h1.ip = "10.0.102.110"
>>> h1.port = 80
#到这里为止一切都是美好的,然后开始向外键b写入数据。
>>> h1.b = 1
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "C:\Python27\lib\site-packages\django-1.10.8-py2.7.egg\django\db\models\fields\related_descriptors.py", line 211, in __set__
self.field.remote_field.model._meta.object_name,
ValueError: Cannot assign "1": "Host.b" must be a "Business" instance.
因为外键b关联到Business表,因此这里报错说b必须是一个Business实例。
>>> id1 = models.Business.objects.get(caption="web") #主机名是nginx自然归类到web中
>>> h1.b = id1
>>> h1.save()
#查看插入的数据
>>> models.Host.objects.values()
<QuerySet [{'ip': u'10.0.102.110', u'b_id': 1, 'hostname': u'nginx-one', 'port': 80, 'nid': 3}]>
#跨表查询,这个查询从子表----->父表
#这里仅查出了外键的数字值为1,因为Business中id字段值是数字,但是数字看起来没有任何的意义,我们想同时查处来,这个主机对应的caption值是多少?
>>> models.Host.objects.values("hostname","ip","port","b__caption") #注意这里的写法b后面是双下划线然后才是caption。
<QuerySet [{'b__caption': u'web', 'ip': u'10.0.102.110', 'hostname': u'nginx-one', 'port': 80}]>
#跨表查询,我们要从父表----->子表
>>> models.Business.objects.get(caption="web").host_set.values()
<QuerySet [{'ip': u'10.0.102.110', u'b_id': 1, 'hostname': u'nginx-one', 'port': 80, 'nid': 3}]>
Django默认每个主表对象都有一个外键的属性。 可以通过它来查询所有属于主表的子表信息, 查询方式:主表.子表_set(), 返回值为一个queryset对象
#在定义外键的时候也可以使用参数related_name指定这个外键关系的名字,在反向查询的时候可以直接使用这个名字。
#删除操作时根据on_delete参数的值,来确定如何删除数据。
多对多关系的操作
在上面的模型中Application应用与主机之间的关系是多对多的。上面的测试中,我们在Host表中插入了一条数据,继续再插入多条数据。
多对多关系的数据插入,分为两步。我们知道在多对多关系中,django另外创建一张表维护多对多的关系。因此插入数据时,先插入多对多的数据表,然后在插入多对多的关系表。
>>> app1 = models.Application() #创建一个Application对象
>>> app1.name = "test_app" #这个模型有个字段,先插入这个字段的值,然后再插入关系字段的值。
>>> app1.save()
>>> value_r = models.Host.objects.get(hostname="redis1") #创建一个Host对象,然后把这个对象附加到app1上面。
>>> app1.r.add(value_r)
add方法可以接受多个参数,每个参数用逗号分隔,可以一次性添加多个关系。
多对多关系的查询:
#本质上是转化为一对多关系的查询。
>>> app2 = models.Application.objects.get(name="test_app")
>>> app2.r.all()
<QuerySet [<Host: Host object>]>
>>> app2.r.values()
<QuerySet [{'ip': u'10.0.104.66', u'b_id': 3, 'hostname': u'redis1', 'port': 6379, 'nid': 3}]>
#反向查询:【和上面的多对一的反向查询一样】
>>> h1 = models.Host.objects.get(hostname="redis1")
>>> h1.application_set.values()
<QuerySet [{u'id': 3, 'name': u'test_app'}]>
在关联对象时有几个方法可以使用:
https://yiyibooks.cn/xx/Django_1.11.6/ref/models/relations.html#django.db.models.fields.related.RelatedManager.add
add方法:
上面已经使用过了add方法:
add(*obj, bulk=True)
#添加一指定的模型对象到关联的对象集中。bulk的值请使用默认的!
create方法:
创建一个新的对象,将它保存并放在关联的对象集中。
返回新创建的对象:
>>> b = Blog.objects.get(id=1)
>>> e = b.entry_set.create(
... headline='Hello',
... body_text='Hi',
... pub_date=datetime.date(2005, 1, 1)
... )
# 这里不需要调用e.save() — 它已经被保存。
# 这完全等价于下面的(不过更加简洁于):
>>> b = Blog.objects.get(id=1)
>>> e = Entry(
... blog=b,
... headline='Hello',
... body_text='Hi',
... pub_date=datetime.date(2005, 1, 1)
... )
>>> e.save(force_insert=True)