ORM(object relational mapping): 对象关系映射
映射关系:
表名 <-------> 类名 字段 <-------> 属性 表记录 <------->类实例对象
建立模型(model)
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
1 class Author(models.Model): 2 nid = models.AutoField(primary_key=True) 3 name=models.CharField( max_length=32) 4 age=models.IntegerField() 5 6 # 与AuthorDetail建立一对一的关系 7 authorDetail=models.OneToOneField(to="AuthorDetail") 8 9 class AuthorDetail(models.Model): 10 11 nid = models.AutoField(primary_key=True) 12 birthday=models.DateField() 13 telephone=models.BigIntegerField() 14 addr=models.CharField( max_length=64) 15 16 class Publish(models.Model): 17 nid = models.AutoField(primary_key=True) 18 name=models.CharField( max_length=32) 19 city=models.CharField( max_length=32) 20 email=models.EmailField() 21 22 23 class Book(models.Model): 24 25 nid = models.AutoField(primary_key=True) 26 title = models.CharField( max_length=32) 27 publishDate=models.DateField() 28 price=models.DecimalField(max_digits=5,decimal_places=2) 29 keepNum=models.IntegerField()<br> commentNum=models.IntegerField() 30 31 # 与Publish建立一对多的关系,外键字段建立在多的一方 32 publish=models.ForeignKey(to="Publish",to_field="nid") 33 34 # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表 35 authors=models.ManyToManyField(to=‘Author‘)
通过设置项目目录中settings.py文件中LOGGING参数可查看每一次数据库操作对应的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、 表的名称APPName_ModelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称
2、id 字段是自动添加的
3、对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
4、定义好模型之后,你需要告诉Django 使用这些模型。你要做的就是修改配置文件中的INSTALL_APPS中设置,在其中添加models.py所在应用的名称。
5、外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None,blank=True用于Admin操作时允许输入框为空(如下)
字段选项
(1)null 如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False. (1)blank 如果为True,该字段允许不填。默认为False。 要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。 如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。 (2)default 字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。 (3)primary_key 如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True, Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为, 否则没必要设置任何一个字段的primary_key=True。 (4)unique 如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的 (5)choices 由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。 这是一个关于 choices 列表的例子: YEAR_IN_SCHOOL_CHOICES = ( (‘FR‘, ‘Freshman‘), (‘SO‘, ‘Sophomore‘), (‘JR‘, ‘Junior‘), (‘SR‘, ‘Senior‘), (‘GR‘, ‘Graduate‘), ) 每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。 在一个给定的 model 类的实例中,想得到某个 choices 字段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。例如: from django.db import models class Person(models.Model): SHIRT_SIZES = ( (‘S‘, ‘Small‘), (‘M‘, ‘Medium‘), (‘L‘, ‘Large‘), ) name = models.CharField(max_length=60) shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES) >>> p = Person(name="Fred Flintstone", shirt_size="L") >>> p.save() >>> p.shirt_size ‘L‘ >>> p.get_shirt_size_display() ‘Large‘
添加表记录
1.普通字段
方式1 publish_obj=Publish(name="人民出版社",city="北京",email="[email protected]") publish_obj.save() # 将数据保存到数据库 方式2 <br>返回值publish_obj是添加的记录对象 publish_obj=Publish.objects.create(name="人民出版社",city="北京",email="[email protected]")
2. 外键字段
方式1: publish_obj=Publish.objects.get(nid=1) Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=665,pageNum=334,publish=publish_obj) 方式2: Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=665,pageNum=334,publish_id=1)
3. 多对多字段
book_obj=Book.objects.create(title="追风筝的人",publishDate="2012-11-12",price=69,pageNum=314,publish_id=1) author_yuan=Author.objects.create(name="yuan",age=23,authorDetail_id=1) author_egon=Author.objects.create(name="egon",age=32,authorDetail_id=2) book_obj.authors.add(author_egon,author_yuan) # 将某个特定的 model 对象添加到被关联对象集合中。 ======= book_obj.authors.add(*[]) book_obj.authors.create() #创建并保存一个新对象,然后将这个对象加被关联对象的集合中,然后返回这个新对象。
查询表记录
查询相关API
<1> all(): 查询所有结果 <2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 <3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个, 如果符合筛选条件的对象超过一个或者没有都会抛出错误。 <4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 <5> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列 <6> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 <7> order_by(*field): 对查询结果排序 <8> reverse(): 对查询结果反向排序 <9> distinct(): 从返回结果中剔除重复纪录 <10> count(): 返回数据库中匹配查询(QuerySet)的对象数量。 <11> first(): 返回第一条记录 <12> last(): 返回最后一条记录 <13> exists(): 如果QuerySet包含数据,就返回True,否则返回False
注意区分object与querySet的区别
双下划线之单表查询
类型 | 描述 |
---|---|
exact | 精确匹配: polls.get_object(id__exact=14). |
iexact | 忽略大小写的精确匹配: polls.objects.filter(slug__iexact="foo") 匹配 foo, FOO, fOo, 等等. |
contains |
大小写敏感的内容包含测试: polls.objects.filter(question__contains="spam") 返回question 中包含 "spam" 的所有民意测验. (仅PostgreSQL 和 MySQL支持. SQLite 的LIKE 语句不支持大小写敏感特性. 对Sqlite 来说, contains 等于 icontains.) |
icontains | 大小写不敏感的内容包含测试: |
gt | 大于: polls.objects.filter(id__gt=4). |
gte | 大于等于. |
lt | 小于. |
lte | 小于等于. |
ne | 不等于. |
in | 位于给定列表中: polls.objects.filter(id__in=[1, 3, 4]) 返回一个 polls 列表(ID 值分别是 1或3或4). |
startswith |
大小写敏感的 starts-with: polls.objects.filter(question__startswith="Would").(仅PostgreSQL 和MySQL支持. SQLite 的LIKE 语句不支持大小写敏感特性. 对Sqlite 来说,``startswith`` 等于 istartswith) |
endswith | 大小写敏感的 ends-with. (仅PostgreSQL 和 MySQL) |
istartswith | 大小写不敏感的 starts-with. |
iendswith | 大小写不敏感的 ends-with. |
range |
范围测试: polls.objects.filter(pub_date__range=(start_date, end_date)) 返回 pub_date 位于 start_date 和 end_date (包括)之间的所有民意测验 |
year | 对 date/datetime 字段, 进行精确的 年 匹配: polls.get_count(pub_date__year=2005). |
month | 对 date/datetime 字段, 进行精确的 月 匹配: |
day | 对 date/datetime 字段, 进行精确的 日 匹配: |
isnull | True/False; 做 IF NULL/IF NOT NULL 查询: polls.objects.filter(expire_date__isnull=True). |
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in models.Tb1.objects.filter(name__contains="ven") models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and startswith,istartswith, endswith, iendswith
基于对象的跨表查询
1. 一对多查询(如Publish和Book)
正向查询(按字段:publish):
# 查询nid=1的书籍的出版社所在的城市 book_obj=Book.objects.get(nid=1) print(book_obj.publish.city) # book_obj.publish 是nid=1的书籍对象关联的出版社对象
反向查询(按表名:book_set):
# 查询 人民出版社出版过的所有书籍 publish=Publish.objects.get(name="人民出版社") book_list=publish.book_set.all() # 与人民出版社关联的所有书籍对象集合 for book_obj in book_list: print(book_obj.title)
2. 一对一查询(如Author和AuthorDetail)
正向查询(按字段:authorDetail):
# 查询egon作者的手机号 author_egon=Author.objects.get(name="egon") print(author_egon.authorDetail.telephone)
反向查询(按表名:author):
# 查询所有住址在北京的作者的姓名 authorDetail_list=AuthorDetail.objects.filter(addr="beijing") for obj in authorDetail_list: print(obj.author.name)
3. 多对多查询(如Author和Book)
正向查询(按字段:authors):
# 金瓶眉所有作者的名字以及手机号 book_obj=Book.objects.filter(title="金瓶眉").first() authors=book_obj.authors.all() for author_obj in authors: print(author_obj.name,author_obj.authorDetail.telephone)
反向查询(按表名:book_set):
# 查询egon出过的所有书籍的名字 author_obj=Author.objects.get(name="egon") book_list=author_obj.book_set.all() #与egon作者相关的所有书籍 for book_obj in book_list: print(book_obj.title)
注意:
可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Book model 中做一下更改: publish = ForeignKey(Blog, related_name=‘bookList‘),那么接下来就会如我们看到这般:
# 查询 人民出版社出版过的所有书籍 publish=Publish.objects.get(name="人民出版社") book_list=publish.bookList.all() # 与人民出版社关联的所有书籍对象集合
基于双下划线的跨表查询
# 练习1: 查询人民出版社出版过的所有书籍的名字与价格(一对多) # 正向查询 按字段:publish queryResult=Book.objects.filter(publish__name="人民出版社") .values_list("title","price") # 反向查询 按表名:book queryResult=Publish.objects.filter(name="人民出版社") .values_list("book__title","book__price") # 练习2: 查询egon出过的所有书籍的名字(多对多) # 正向查询 按字段:authors: queryResult=Book.objects.filter(authors__name="yuan") .values_list("title") # 反向查询 按表名:book queryResult=Author.objects.filter(name="yuan") .values_list("book__title","book__price") # 练习3: 查询人民出版社出版过的所有书籍的名字以及作者的姓名 # 正向查询 queryResult=Book.objects.filter(publish__name="人民出版社") .values_list("title","authors__name") # 反向查询 queryResult=Publish.objects.filter(name="人民出版社") .values_list("book__title","book__authors__age","book__authors__name") # 练习4: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称 queryResult=Book.objects.filter(authors__authorDetail__telephone__regex="151") .values_list("title","publish__name")
注意:
反向查询时,如果定义了related_name ,则用related_name替换表名,例如: publish = ForeignKey(Blog, related_name=‘bookList‘):
# 练习1: 查询人民出版社出版过的所有书籍的名字与价格(一对多) # 反向查询 不再按表名:book,而是related_name:bookList queryResult=Publish.objects.filter(name="人民出版社").values_list("bookList__title","bookList__price")