ORM
對象關(guān)系映射(ORM),用于實(shí)現(xiàn)面向?qū)ο缶幊汤锊煌愋拖到y(tǒng)的數(shù)據(jù)之間的轉(zhuǎn)換。換句話說,就是用面向?qū)ο蟮姆绞饺ゲ僮鲾?shù)據(jù)庫的創(chuàng)建表,增加、修改、刪除、查詢等操作。
- 數(shù)據(jù)庫和ORM映射關(guān)系
表名 <-------> 類名
字段 <-------> 屬性
表記錄 <------->類實(shí)例對象
-
models.py
每個(gè)模型相當(dāng)于單個(gè)數(shù)據(jù)庫表(這條規(guī)則的例外情況是多對多關(guān)系,多對多關(guān)系的時(shí)候會多生成一張關(guān)系表),每個(gè)屬性也是這個(gè)表中的一個(gè)字段。屬性名就是字段名,它的類型(例如CharField)相當(dāng)于數(shù)據(jù)庫的字段類型(例如varchar)。 -
演示ORM生成的sql語句
<1> 查看QuerySet的query屬性
def hello(request):
user_list=User.objects.all()
print user_list.query
<2> 配置日志系統(tǒng),將sql顯示到控制臺(修改settings.py)
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'DEBUG',
'propagate': True,
},
},
}
創(chuàng)建表(建立模型):
實(shí)例:我們來假定下面這些概念,字段和關(guān)系
<1>作者模型:一個(gè)作者有姓名。
<2>作者詳細(xì)模型:把作者的詳情放到詳情表,包含性別,email地址和出生日期,作者詳情模型和作者模型之間是一對一的關(guān)系(one-to-one)(類似于每個(gè)人和他的身份證之間的關(guān)系),在大多數(shù)情況下我們沒有必要將他們拆分成兩張表,這里只是引出一對一的概念。
<3>出版商模型:出版商有名稱,地址,所在城市,省,國家和網(wǎng)站。
<4>書籍模型:書籍有書名和出版日期,一本書可能會有多個(gè)作者,一個(gè)作者也可以寫多本書,所以作者和書籍的關(guān)系就是多對多的關(guān)聯(lián)關(guān)系(many-to-many),一本書只應(yīng)該由一個(gè)出版商出版,所以出版商和書籍是一對多關(guān)聯(lián)關(guān)系(one-to-many),也被稱作外鍵。
- 模型建立如下
from django.db import models
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
# 與AuthorDetail建立一對一的關(guān)系
authorDetail = models.OneToOneField(to="AuthorDetail")
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
birthday = models.DateField()
telephone = models.BigIntegerField()
addr = models.CharField(max_length=64)
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
publishDate = models.DateField()
price = models.DecimalField(max_digits=5, decimal_places=2)
pageNum = models.IntegerField()
# 與Publish建立一對多的關(guān)系,外鍵字段建立在多的一方
publish = models.ForeignKey(to="Publish", to_field="nid")
# 與Author表建立多對多的關(guān)系,ManyToManyField可以建在兩個(gè)模型中的任意一個(gè),自動創(chuàng)建第三張表
authors = models.ManyToManyField(to='Author')
注意事項(xiàng)
<1> 表的名稱myapp_modelName,是根據(jù) 模型中的元數(shù)據(jù)自動生成的,也可以覆寫為別的名稱
<2> id 字段是自動添加的
<3> 對于外鍵字段,Django 會在字段名上添加"_id" 來創(chuàng)建數(shù)據(jù)庫中的列名
<4> 這個(gè)例子中的CREATE TABLE SQL 語句使用PostgreSQL 語法格式,要注意的是Django 會根據(jù)settings 中指定的數(shù)據(jù)庫類型來使用相應(yīng)的SQL 語句
<5> 定義好模型之后,你需要告訴Django 使用這些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中設(shè)置,在其中添加models.py所在應(yīng)用的名稱
<6> 外鍵字段 ForeignKey 有一個(gè) null=True 的設(shè)置(它允許外鍵接受空值 NULL),你可以賦給它空值 None
<7> 模型之間的三種關(guān)系:一對一,一對多,多對多
- 一對一:實(shí)質(zhì)就是在主外鍵(author_id就是foreign key)的關(guān)系基礎(chǔ)上,給外鍵加了一個(gè)UNIQUE=True的屬性
- 一對多:就是主外鍵關(guān)系;(foreign key)
- 多對多:(ManyToManyField) 自動創(chuàng)建第三張表(當(dāng)然我們也可以自己創(chuàng)建第三張表:兩個(gè)foreign key)
模型常用字段類型
BooleanField: 布爾類型字段
CharField: 字符串類型字段
DateField: 日期字段
DateTimeField:日期時(shí)間字段
DecimalField:(精確)小數(shù)字段
EmailField:Email字段
FileField:文件字段
FloatField:(浮點(diǎn)數(shù))小數(shù)字段
ImageField:圖片字段
IntegerField:整數(shù)字段
IPAddressField:IP字段
SmallIntegerField:小整數(shù)字段
TextField:文本字段
URLField:網(wǎng)址地址字段
字段選項(xiàng)
(1)null
如果為True,Django 將用NULL 來在數(shù)據(jù)庫中存儲空值。 默認(rèn)值是 False.
(1)blank
如果為True,該字段允許不填。默認(rèn)為False。
要注意,這與 null 不同。null純粹是數(shù)據(jù)庫范疇的,而 blank 是數(shù)據(jù)驗(yàn)證范疇的。
如果一個(gè)字段的blank=True,表單的驗(yàn)證將允許該字段是空值。如果字段的blank=False,該字段就是必填的。
(2)default
字段的默認(rèn)值??梢允且粋€(gè)值或者可調(diào)用對象。如果可調(diào)用 ,每有新對象被創(chuàng)建它都會被調(diào)用。
(3)primary_key
如果為True,那么這個(gè)字段就是模型的主鍵。如果你沒有指定任何一個(gè)字段的primary_key=True,
Django 就會自動添加一個(gè)IntegerField字段做為主鍵,所以除非你想覆蓋默認(rèn)的主鍵行為,
否則沒必要設(shè)置任何一個(gè)字段的primary_key=True。
(4)unique
如果該值設(shè)置為 True, 這個(gè)數(shù)據(jù)字段的值在整張表中必須是唯一的
(5)choices
由二元組組成的一個(gè)可迭代對象(例如,列表或元組),用來給字段提供選擇項(xiàng)。 如果設(shè)置了choices ,默認(rèn)的表單將是一個(gè)選擇框而不是標(biāo)準(zhǔn)的文本框,而且這個(gè)選擇框的選項(xiàng)就是choices 中的選項(xiàng)。
這是一個(gè)關(guān)于 choices 列表的例子:
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
每個(gè)元組中的第一個(gè)元素,是存儲在數(shù)據(jù)庫中的值;第二個(gè)元素是在管理界面或 ModelChoiceField 中用作顯示的內(nèi)容。 在一個(gè)給定的 model 類的實(shí)例中,想得到某個(gè) choices 字段的顯示值,就調(diào)用 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'
ORM之增(create, save)
from app01.models import *
#create方式一: Author.objects.create(name='Alvin')
#create方式二: Author.objects.create(**{"name":"alex"})
#save方式一: author=Author(name="alvin")
author.save()
#save方式二: author=Author()
author.name="alvin"
author.save()
## 推薦使用create
重點(diǎn):如何創(chuàng)建存在一對多或多對多關(guān)系的一本書的信息呢?(如何處理外鍵關(guān)系的字段如一對多的publisher和多對多的authors)
#一對多(ForeignKey):
#方式一: 由于綁定一對多的字段,比如publish,存到數(shù)據(jù)庫中的字段名叫publish_id,所以我們可以直接給這個(gè)
# 字段設(shè)定對應(yīng)值:
Book.objects.create(title='php',
publisher_id=2, #這里的2是指為該book對象綁定了Publisher表中id=2的行對象
publication_date='2017-7-7',
price=99)
#方式二:
# <1> 先獲取要綁定的Publisher對象:
pub_obj=Publisher(name='河大出版社',address='保定',city='保定',
state_province='河北',country='China',website='http://www.hbu.com')
OR pub_obj=Publisher.objects.get(id=1)
# <2>將 publisher_id=2 改為 publisher=pub_obj
#多對多(ManyToManyField()):
author1=Author.objects.get(id=1)
author2=Author.objects.filter(name='alvin')[0]
book=Book.objects.get(id=1)
book.authors.add(author1,author2)
#等同于:
book.authors.add(*[author1,author2])
book.authors.remove(*[author1,author2])
#-------------------
book=models.Book.objects.filter(id__gt=1)
authors=models.Author.objects.filter(id=1)[0]
authors.book_set.add(*book)
authors.book_set.remove(*book)
#-------------------
book.authors.add(1)
book.authors.remove(1)
authors.book_set.add(1)
authors.book_set.remove(1)
#注意: 如果第三張表是通過models.ManyToManyField()自動創(chuàng)建的,那么綁定關(guān)系只有上面一種方式
# 如果第三張表是自己創(chuàng)建的:
class Book2Author(models.Model):
author=models.ForeignKey("Author")
Book= models.ForeignKey("Book")
# 那么就還有一種方式:
author_obj=models.Author.objects.filter(id=2)[0]
book_obj =models.Book.objects.filter(id=3)[0]
s=models.Book2Author.objects.create(author_id=1,Book_id=2)
s.save()
s=models.Book2Author(author=author_obj,Book_id=1)
s.save()
ORM之刪(delete)
Book.objects.filter(id=1).delete()
- django默認(rèn)使用 級聯(lián)刪除
ORM之改(update, save)
# <1> 使用update
Publisher.objects.filter(id=2).update(name='American publisher') # 不能用get(id=2)
# <2> 使用save
author = Author.objects.get(id=5)
author.name = 'tenglan'
author.save()
注意:
1)第一種方式修改不能用get的原因是:update是QuerySet對象的方法,get返回的是一個(gè)model對象,它沒有update方法,而filter返回的是一個(gè)QuerySet對象(filter里面的條件可能有多個(gè)條件符合,比如name='alvin',可能有兩個(gè)name='alvin'的行數(shù)據(jù))。
2)save()方法會更新一行里的所有列,而某些情況下,我們只需要更新行里的某幾列。
#---------------- update方法直接設(shè)定對應(yīng)屬性----------------
models.Book.objects.filter(id=3).update(title="PHP")
##sql:
##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3)
#--------------- save方法會將所有屬性重新設(shè)定一遍,效率低-----------
obj=models.Book.objects.filter(id=3)[0]
obj.title="Python"
obj.save()
# SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price",
# "app01_book"."color", "app01_book"."page_num",
# "app01_book"."publisher_id" FROM "app01_book" WHERE "app01_book"."id" = 3 LIMIT 1;
#
# UPDATE "app01_book" SET "title" = 'Python', "price" = 3333, "color" = 'red', "page_num" = 556,
# "publisher_id" = 1 WHERE "app01_book"."id" = 3;
- 此外,update()方法對于任何結(jié)果集(QuerySet)均有效,這意味著你可以同時(shí)更新多條記錄update()方法會返回一個(gè)整型數(shù)值,表示受影響的記錄條數(shù)。
注意,這里因?yàn)閡pdate返回的是一個(gè)整型,所以沒法用query屬性;對于每次創(chuàng)建一個(gè)對象,想顯示對應(yīng)的raw sql,需要在settings加上日志記錄部分:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
ORM之查(filter, value)
- 查詢API:
# 查詢相關(guān)API:
# <1>filter(**kwargs): 它包含了與所給篩選條件相匹配的對象
# <2>all(): 查詢所有結(jié)果
# <3>get(**kwargs): 返回與所給篩選條件相匹配的對象,返回結(jié)果有且只有一個(gè),如果符合篩選條件的對象超過一個(gè)或者沒有都會拋出錯誤。
#-----------下面的方法都是對查詢的結(jié)果再進(jìn)行處理:比如 objects.filter.values()--------
# <4>values(*field): 返回一個(gè)ValueQuerySet——一個(gè)特殊的QuerySet,運(yùn)行后得到的并不是一系列 model的實(shí)例化對象,而是一個(gè)可迭代的字典序列 ;-->拿到庫內(nèi) 固定列內(nèi)的 值作為 一對 key value ;如果條件是多個(gè),那就拿到多對,整體是一個(gè)列表
# <5>exclude(**kwargs): 它包含了與所給篩選條件不匹配的對象
# <6>order_by(*field): 對查詢結(jié)果排序
# <7>reverse(): 對查詢結(jié)果反向排序
# <8>distinct(): 從返回結(jié)果中剔除重復(fù)紀(jì)錄
# <9>values_list(*field): 它與values()非常相似,它返回的是一個(gè)元組序列,values返回的是一個(gè)字典序列;相比于 values 左后是一個(gè) 元祖,不是字典
# <10>count(): 返回?cái)?shù)據(jù)庫中匹配查詢(QuerySet)的對象數(shù)量。
# <11>first(): 返回第一條記錄
# <12>last(): 返回最后一條記錄
# <13>exists(): 如果QuerySet包含數(shù)據(jù),就返回True,否則返回False
---------------了不起的雙下劃線(__)之單表?xiàng)l件查詢----------------
# 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的數(shù)據(jù)
# 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,