Django之模型(二)

模型元選項(xiàng)

  1. 每個(gè)model都可以定義一個(gè)Meta類,使用內(nèi)部的class Meta 定義模型的元數(shù)據(jù),這個(gè)類中可以定義一些關(guān)于你的配置,Meta是一個(gè)model內(nèi)部的類
  2. 模型元數(shù)據(jù)是“任何不是字段的數(shù)據(jù)”,比如排序選項(xiàng)(ordering),數(shù)據(jù)庫(kù)表名(db_table)或者人類可讀的單復(fù)數(shù)名稱(verbose_name 和verbose_name_plural)。在模型中添加class Meta是完全可選的,所有選項(xiàng)都不是必須的。
class Meta:
        db_table = "topic"
        managed = True
        ordering = ['-id']
        verbose_name = u"主題"
        verbose_name_plural = u"主題列表"

db_table:string,在數(shù)據(jù)庫(kù)中的表名,否則Django自動(dòng)生成為app名字_類名
managed: bool, 默認(rèn)值為T(mén)rue,這意味著Django可以使用syncdb和reset命令來(lái)創(chuàng)建或移除對(duì)應(yīng)的數(shù)據(jù)庫(kù)。
ordering: 數(shù)組, 默認(rèn)排序規(guī)則,每個(gè)字符串是一個(gè)字段名,前面帶有可選的“-”前綴表示倒序。前面沒(méi)有“-”的字段表示正序。使用"?"來(lái)表示隨機(jī)排序。
verbose_name: string model對(duì)象的描述
verbose_name_plural: string 復(fù)數(shù)時(shí)的描述

模型字段
image.png

模型字段常用的參數(shù)
image.png

null與blank:

  • blank 是針對(duì)表單的,如果 blank=True,表示你的表單填寫(xiě)該字段的時(shí)候可以不填,比如 admin 界面下增加 model 一條記錄的時(shí)候。直觀的看到就是該字段不是粗體,設(shè)置為False時(shí),字段是必須填寫(xiě)的。字符型字段CharField和TextField是用空字符串來(lái)存儲(chǔ)空值的。默認(rèn)不允許為空。
  • null 是針對(duì)數(shù)據(jù)庫(kù)而言,如果 null=True, 表示數(shù)據(jù)庫(kù)的該字段可以為空。默認(rèn)不允許。日期型、時(shí)間型和數(shù)字型字段不接受空字符串。所以設(shè)置IntegerField,DateTimeField型字段可以為空時(shí),需要將blank,null均設(shè)為T(mén)rue。

總之:
blank,只是在form表單驗(yàn)證時(shí)可以為空,而在數(shù)據(jù)庫(kù)上存儲(chǔ)的是一個(gè)空字符串;null是在數(shù)據(jù)庫(kù)上表現(xiàn)NULL,而不是一個(gè)空字符串;
需要注意的是,日期型(DateField、TimeField、DateTimeField)和數(shù)字型(IntegerField、DecimalField、FloatField)不能接受空字符串,如要想要在填寫(xiě)表單的時(shí)候這兩種類型的字段為空的話,則需要同時(shí)設(shè)置null=True、blank=True;

auto_now 與auto_now_add區(qū)別:
auto_now_add: 只有第一次才會(huì)生效,比如可以用于文章創(chuàng)建時(shí)間
auto_now: 每一次修改保存對(duì)象時(shí)都會(huì)將當(dāng)前時(shí)間更新進(jìn)去,只有調(diào)用Model.save()時(shí)更新,在以其他方式(例如 QuerySet.update())更新其他字段時(shí),不會(huì)更新該字段,但您可以在此類更新中為字段指定自定義值??捎糜谖恼滦薷臅r(shí)間;

模型中的關(guān)系

一對(duì)一:一本書(shū)籍有一個(gè)編號(hào);
多對(duì)一:一本書(shū)籍有多個(gè)評(píng)論;--注意Django只有多對(duì)一關(guān)系,站在多的角度去看待;
多對(duì)多:一本書(shū)籍有多個(gè)作者,一個(gè)作者可以寫(xiě)多本書(shū)籍;

class Author(models.Model):
    name = models.CharField(u'姓名', max_length=200)
    email = models.EmailField(u'郵箱')
class Number(models.Model):
    number = models.CharField(u'編號(hào)', max_length=200)
class Book(models.Model):
    headline = models.CharField(u'大標(biāo)題', max_length=255)
    pub_date = models.DateTimeField(u'出版時(shí)間')
    authors = models.ManyToManyField(Author)
    number = models.OneToOneField(Number)
class Reply(models.Model):
    book = models.ForeignKey(Book) #外鍵字段
    content = models.CharField(u'內(nèi)容', max_length=255)
  • ForeignKey外鍵
    多對(duì)一關(guān)系,關(guān)聯(lián)模型關(guān)聯(lián)的類,定義在多的類中,如上Reply類中;
    屬性:
    db_constraint:bool 是否建立外鍵約束
    to_field:string 關(guān)聯(lián)到的關(guān)聯(lián)對(duì)象的字段名稱。默認(rèn)地,Django 使用關(guān)聯(lián)對(duì)象的主鍵。
    related_name: string 關(guān)聯(lián)對(duì)象反向引用描述符。.如果你不想讓Django 自動(dòng)創(chuàng)建一個(gè)反向關(guān)聯(lián)(Django自動(dòng)是以子表_set命名的),則可以自己定義。
    on_delete: string 可以取如下值
    CASCADE:級(jí)聯(lián)刪除,如果刪除,相關(guān)聯(lián)的那個(gè)也會(huì)刪除
    PROTECT:保護(hù)類型。如果刪除,將會(huì)拋出一個(gè)ProtectedError錯(cuò)誤
    SET_NULL:如果刪除了本條數(shù)據(jù),外鍵的那條數(shù)據(jù)將會(huì)設(shè)置為null,這個(gè)只有在外鍵null為T(mén)rue的情況下才可以使用
    SET_DEFAULT:如果刪除了本條數(shù)據(jù),外鍵那條數(shù)據(jù)將會(huì)職位默認(rèn)值,這個(gè)只有在外鍵那個(gè)字段設(shè)置了default參數(shù)才可以使用
  • ManyToManyField
    多對(duì)多關(guān)系,定義在哪個(gè)類中都可以,如上Book類中;
    屬性:
    related_name: string 與ForeignKey相同
    db_table:string 需要建立關(guān)聯(lián)表的表名
    db_constraint: 同上
  • OneToOneField
    一對(duì)一關(guān)系,定義在哪個(gè)類中都可以,如書(shū)籍和編號(hào);
    屬性:
    on_delete 同F(xiàn)oreignKey
    to_field 同F(xiàn)oreignKey

持久操作

Model.save()
這個(gè)方法可以用來(lái)插入一條新的數(shù)據(jù),也可以用來(lái)更新一條數(shù)據(jù)。如果這個(gè)數(shù)據(jù)在之前數(shù)據(jù)庫(kù)中存在了,就只是調(diào)用sql的update語(yǔ)句。如果這條數(shù)據(jù)是新的,就會(huì)調(diào)用sql的insert語(yǔ)句。示例代碼如下:

class UpdateArticle(View):
    def get(self,request):
        # 批量修改
        # UPDATE hello_article SET status=3 WHERE status=2
        # Article.objects.filter(status=2).update(status=3)

        # 單個(gè)修改
        # 實(shí)際上save方法如果當(dāng)前實(shí)例已經(jīng)存在于數(shù)據(jù)庫(kù)中,它就會(huì)當(dāng)作一個(gè)update操作
        # Article.objects.filter(id=7).update(status=4)
        article = Article.objects.get(pk=8) #取出pk=7的對(duì)象,然后修改其狀態(tài),再保存
        article.status = 0
        article.save()

        #更新之后在查詢
        articleAll = Article.objects.all()
        return render(request, 'displayArticle.html', locals())

注意:save操作不止是更新修改的字段,而是會(huì)將所有的字段全部更新一遍,不管有沒(méi)有修改。當(dāng)你的所有操作都是串行,沒(méi)有什么并發(fā)同時(shí)操作同一個(gè)model的時(shí)候,這樣的處理方式,一般不會(huì)給你帶來(lái)任何麻煩。但是并行的時(shí)候可能會(huì)有影響。
Django后面在save方法里面新增加了一個(gè)update_fields參數(shù)。這樣就可以只修改特定字段了:

user.name = name
user.save(update_fields=['name'])

這樣便有效避免了并行save產(chǎn)生的數(shù)據(jù)沖突。

檢索對(duì)象

  • get(**kwargs)方法:查詢單個(gè)數(shù)據(jù),只會(huì)返回一個(gè)對(duì)象,如果所有條件都不滿足或者是滿足條件的有多個(gè),將拋出一個(gè)異常。
article = Article.objects.get(pk=1) #在django 的ORM查詢中,數(shù)據(jù)庫(kù)的主鍵可以用PK代替, 官方推薦使用pk
article = Article.objects.get(id=1)#等同于select * from hello_article where id=1;
  • all() 獲取所有
articleAll = Article.objects.all()
  • filter(**kwargs)方法:根據(jù)參數(shù)提供的提取條件,獲取一個(gè)過(guò)濾后的QuerySet。
Article.objects.filter(status = 0)
  • exclude(**kwargs)方法:根據(jù)參數(shù)提供的條件,排除符合條件的數(shù)據(jù),返回一個(gè)QuerySet對(duì)象。
Article.objects.exclude(status = 0)
  • order_by(*args):根據(jù)給定的參數(shù)進(jìn)行排序.
#正序:
Book .objects.filter(headline=u'標(biāo)題').order_by('pub_date')
#倒序,字段前面加-
Book .objects.filter(headline=u'標(biāo)題').order_by('-pub_date')
  • values()方法:將返回來(lái)的QuerySet中的Model轉(zhuǎn)換為字典
#<QuerySet [<Classesgrade: Classesgrade object>, <Classesgrade: Classesgrade object>]>
cgrade =  Classesgrade.objects.all()
#<QuerySet [{'update_time': datetime.datetime(2018, 4, 7, 4, 44, 56, 181307,
 cninfo=<UTC>), 'create_time': datetime.datetime(2018, 4, 7, 4, 44, 56, 181269, 
cninfo=<UTC>), u'number_id': 1L, u'id': 1L, 'name': u'django\u6846\u67b6\u73ed'},
 {'update_time': datetime.datetime(2018, 4, 7, 4, 44, 56, 405850, cninfo=<UTC>), 
'create_time': datetime.datetime(2018, 4, 7, 4, 44, 56, 405816, cninfo=<UTC>), 
u'number_id': 2L, u'id': 2L, 'name': u'django\u6846\u67b6\u73ed'}]>

cgrade.values()
  • count()方法:將返回當(dāng)前查詢到的數(shù)據(jù)的總數(shù),這個(gè)比length方法更有效
Book .objects.count()
  • latest(field_name=None)方法:根據(jù)提供的參數(shù)field_name來(lái)進(jìn)行排序,field_name這個(gè)參數(shù)必須是時(shí)間字段,然后提取離現(xiàn)在最近的一條數(shù)據(jù)
Book .objects.latest('pub_date')
  • earliest()方法:用法和latest一樣,只是這個(gè)是獲取最久遠(yuǎn)的一個(gè)。
Book .objects.latest('pub_date')
  • first()方法:獲取查詢到的數(shù)據(jù)的第一條數(shù)據(jù)。如果用了order_by,那么將獲取排序后的第一條。如果沒(méi)有用order_by,那么將根據(jù)id進(jìn)行默認(rèn)排序。
Book .objects.first()
  • last()方法:用法和first一樣,只不過(guò)是獲取的是最后一條的數(shù)據(jù)。
Book .objects.last()
  • update(**kwargs):更新數(shù)據(jù)方法,這個(gè)方法可以對(duì)查詢出來(lái)的QuerySet里面的所有元素進(jìn)行更新,并且更新參數(shù)的個(gè)數(shù)也是不限的。另外要注意的是,因?yàn)間et方法返回的不是QuerySet對(duì)象,因此使用get方法提取出來(lái)的數(shù)據(jù)不能使用update方法。出于這種情況,建議應(yīng)該使用filter(pk=xx)來(lái)替代get(pk=xxx)方法。并且,使用get出來(lái)的模型,修改數(shù)據(jù)后再save,會(huì)更新所有的數(shù)據(jù),比update的效率更低。
Book .objects.filter(id=1).update(headline="大標(biāo)題")
  • delete()方法:刪除QuerySet中的模型。
Book .objects.filter(id=1).delete()

查找對(duì)象的條件

查找對(duì)象的條件的意思是傳給以上方法的一些參數(shù)。比如name__contains=’abc’這個(gè)就代表name這個(gè)字段包含了abc的意思,相當(dāng)于是SQL語(yǔ)句中的where語(yǔ)句后面的條件,語(yǔ)法為字段名__規(guī)則,以下將對(duì)這些規(guī)則進(jìn)行說(shuō)明:

  • exact:相當(dāng)于等于號(hào)
Book .objects.filter(headline__exact=u'大標(biāo)題')
或
Book .objects.filter(headline=u'大標(biāo)題')
  • iexact:跟exact,只是忽略大小寫(xiě)的匹配。
Book .objects.filter(headline__iexact=u'大標(biāo)題')
  • contains:字符數(shù)據(jù)中包含等號(hào)后面的數(shù)據(jù)。
Book .objects.filter(headline__contains=u'標(biāo)題')
  • icontains:跟contains,唯一不同是忽略大小寫(xiě)。
Book .objects.filter(headline__icontains=u'標(biāo)題')
  • in:判斷字符的數(shù)據(jù)是否處在一個(gè)給定的列表中,如果在,則提取出來(lái)。
Book .objects.filter(id__in=[1, 2, 3])
  • gt:大于。
Book .objects.filter(id__gt=1)
  • gte:大于等于。
Book .objects.filter(id__gte=1)
  • lt:小于。
Book .objects.filter(id__lt=10)
  • lte:小于等于。
Book.objects.filter(id__lte=10)
  • startswith:以什么開(kāi)始。
Book .objects.filter(headline__startswith='title')
  • istartswith:同startswith,忽略大小寫(xiě)。
Book .objects.filter(headline__istartswith='title')
  • endswith:同startswith,以什么結(jié)尾。
Book .objects.filter(headline__iendswith='le')
  • iendswith:同istartswith,以什么結(jié)尾忽略大小寫(xiě)。
Book .objects.filter(headline__iendswith='le')
  • range:區(qū)間查詢。
import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2018, 1, 1)
Book .objects.filter(pub_date__range=(start_date, end_date))
  • isnull:判斷是否是空。
Book .objects.filter(pub_date__isnull=True) zz  ..,. MJ HM 
  • 切片:對(duì)查找出來(lái)的數(shù)據(jù)進(jìn)行切片,跟用數(shù)組是一樣的。
Book.objects.all()[:2]

*ps:負(fù)值的操作,是無(wú)法進(jìn)行。切片可以用來(lái)實(shí)現(xiàn)翻頁(yè)顯示內(nèi)容,比如每一頁(yè)顯示10條內(nèi)容可以利用切片,第page頁(yè)

        cnumber = Classesnumber.objects.all() #獲取Classesnumber的Queryset集
        cn_counts = cnumber.count() #總記錄數(shù)
        page_count = 4 #每頁(yè)顯示記錄
        #求出分多少頁(yè)
        for page in range((cn_counts / page_count + cn_counts % page_count)): 
            #利用for遍歷出每一頁(yè)的記錄數(shù)據(jù)
            for pagenum in (cnumber[page*page_count:page*page_count + page_count]):
                print(pagenum.classgrade_number)

外鍵操作

一對(duì)一
  • 訪問(wèn)
    在建立關(guān)聯(lián)屬性的類里面直接.關(guān)聯(lián)屬性即可
book = Book.objects.get(pk=1) #先獲取從表的對(duì)象,Book為從表
book.number #得到book主鍵為1的對(duì)應(yīng)Number對(duì)象主表
  • 增加
book = Book.objects.get(pk=1)
book.number = number #獲取Number主類的對(duì)象,再賦給book中的關(guān)聯(lián)屬性
book.save()  #再save保存
  • 反查
number = Number.objects.get(pk=1) #Number為主類,Book為從類
number.book     #查詢編號(hào)對(duì)應(yīng)的書(shū)籍
多對(duì)一
  • 訪問(wèn)
    訪問(wèn)外鍵:Reply(多)的對(duì)象要訪問(wèn)他所持有的book(一)對(duì)象
reply = Reply.objects.get(pk=1) #先獲取多類(子表)對(duì)象
reply.book   # 字表
  • 反查(一查多,主查從)
    外鍵反向訪問(wèn):如果Book對(duì)象想要訪問(wèn)所有引用了他的Reply對(duì)象,因?yàn)闆](méi)有外鍵屬性,不能采用“.”方式訪問(wèn),可以通過(guò)“模型名_set“進(jìn)行訪問(wèn)。

  • 如果模型I有一個(gè)ForeignKey,那么該ForeignKey 所指的模型II實(shí)例可以通過(guò)一個(gè)管理器回前面有ForeignKey的模型I的所有實(shí)例。默認(rèn)情況下,這個(gè)管理器的名字為foo_set,其中foo 是源模型的小寫(xiě)名稱。默認(rèn)是以子表小寫(xiě)名稱_set()來(lái)表示(上面默認(rèn)以b_set訪問(wèn)),

可以在從表定義時(shí)設(shè)置related_name 參數(shù)來(lái)覆蓋foo_set 的名稱。通過(guò)主表來(lái)查詢子表信息用到反向查詢(返回的是管理器,需要all之類返回QuerySet),

# 本身Number對(duì)象是沒(méi)有book屬性,django提供的一個(gè)反查機(jī)制
# 如果設(shè)置related_name, 我們就通過(guò)related_name反查
# 如果沒(méi)有設(shè)置, django默認(rèn)設(shè)置的是反查class的小寫(xiě)名字
# related_name 可以當(dāng)成反查所用的別名
book = Book.objects.get(pk=1)
book.reply_set.all()
#還可以進(jìn)行查詢
book = Book.objects.get(pk=1)
book.reply_set.filter(content__contains=’I love’).all()
  • 添加
# 多對(duì)一創(chuàng)建的對(duì)象,它是默認(rèn)沒(méi)有加載外鍵的,只有當(dāng)你引用時(shí),它才會(huì)加載
reply = Reply.objects.get(pk=1)  #首先主表要有數(shù)據(jù),從表才能與之相對(duì)應(yīng)
reply.book = book   #book是從主類獲取的對(duì)象,此處省略
reply.save()   #再保存

#  反向操作

多對(duì)多
  • 訪問(wèn)
    多對(duì)多訪問(wèn):跟外鍵訪問(wèn)一樣,“模型名_set“進(jìn)行反向訪問(wèn)。
Book .authors 等于同Book .objects.filter(reply_id=Reply.id).all()
book = Book.objects.get(pk=1)
book.authors
  • 反查
    “模型名_set“進(jìn)行反向訪問(wèn)。與多對(duì)一類似

  • 添加
    多對(duì)多數(shù)據(jù)添加需要先保存子表對(duì)象,然后再add進(jìn)行添加主類對(duì)象

book = Book(headline="標(biāo)題",number=number)
book.save()
book.authors.add(author)
book.authors.add(author1)
book.save()
  • 刪除
book.authors.remove(author)
book.save()
處理關(guān)聯(lián)對(duì)象方法

add(obj1, obj2, ...) 添加的已經(jīng)存在數(shù)據(jù)庫(kù)的數(shù)據(jù)

添加一指定的模型對(duì)象到關(guān)聯(lián)的對(duì)象集中。
d = Department.objects.get(pk=1)
s = Student.objects.get(pk=1)
d.student_set.add(s) # 學(xué)院d添加學(xué)生s

create(**kwargs) 添加不存在的數(shù)據(jù) ,將數(shù)據(jù)直接存入數(shù)據(jù)庫(kù)

創(chuàng)建一個(gè)新的對(duì)象,將它保存并放在關(guān)聯(lián)的對(duì)象集返回新創(chuàng)建的對(duì)象。
d.student_set.create(name='小明') # 創(chuàng)建一個(gè)叫小明的學(xué)生,并將他添加到學(xué)院d中

remove(obj1, obj2, ...)
從關(guān)聯(lián)的對(duì)象集中刪除指定的模型對(duì)象。刪除的是關(guān)系表中的數(shù)據(jù)

d.student_set.remove(s) # 從學(xué)院d中刪除學(xué)生s

clear() 從關(guān)聯(lián)的對(duì)象集中刪除所有的對(duì)象
d.student_set.clear() # 清除學(xué)院d中所有學(xué)生

注意對(duì)于所有類型的關(guān)聯(lián)字段,add()、create()、remove()和clear()都會(huì)馬上更新數(shù)據(jù)庫(kù)。換句話說(shuō),在關(guān)聯(lián)的任何一端,都不需要再調(diào)用save()方法

多表查詢

跨關(guān)聯(lián)關(guān)系的查詢
Django 提供一種強(qiáng)大而又直觀的方式來(lái)“處理”查詢中的關(guān)聯(lián)關(guān)系,它在后臺(tái)自動(dòng)幫你處理JOIN。 若要跨越關(guān)聯(lián)關(guān)系,只需使用關(guān)聯(lián)的模型字段的名稱,并使用雙下劃線分隔,直至你想要的字段:


image.png

舉例:

# 查詢學(xué)院名字為‘軟件’的學(xué)生的信息 
Student.objects.filter(department__d_name='軟件') 
#這種跨越可以是任意的深度。

#查詢學(xué)生名字中包含‘xiao’的學(xué)生的學(xué)院信息
Department.objects.filter(student__s_name__contains='xiao')

# 查詢學(xué)號(hào)為1的學(xué)生所有的課程
Course.objects.filter(student__s_id=1)

# 查詢報(bào)了課程1的所有的學(xué)生
Student.objects.filter(course__c_id=1)

# 查詢報(bào)了'python'課程的的學(xué)生的所屬學(xué)院的信息
Department.objects.filter(student__course__c_name='python') #三個(gè)表關(guān)聯(lián)查詢
總結(jié):
  1. Object對(duì)象獲取某一列值(或者說(shuō)是獲取某個(gè)屬性)的時(shí)候,使用點(diǎn)來(lái)獲取。我們跨表查詢時(shí),也是使用點(diǎn)來(lái)獲取。或者用_set反查詢,_set:提供了對(duì)象訪問(wèn)相關(guān)聯(lián)表數(shù)據(jù)的方法。但這種方法只能是相關(guān)類訪問(wèn)定義了關(guān)系的類(主鍵類訪問(wèn)外鍵類)。
  2. QuerySet查詢集做跨表查詢時(shí),使用雙下劃線"",:兩個(gè)下劃線可以生成連接查詢,查詢關(guān)聯(lián)的字段信息,可以跨表關(guān)聯(lián)查詢,只要一層接一層,可以從頭查到尾。

聚合操作

使用aggregate方法進(jìn)行數(shù)據(jù)庫(kù)的聚合操作, 使用QuerySet.aggregate 代表需要使用聚合操作,它返回的是一個(gè)字典:

  • 引入對(duì)應(yīng)的聚合函數(shù)
    from django.db.models import Avg, Max, Min, Count, Sum
  • Avg, 平均數(shù)
#查詢當(dāng)前作者的平均年齡
Author.objects.aggregate(Avg("age"))
  • Max 最大數(shù)
#取最大年齡的作者
Author.objects.aggregate(Max("age"))
  • Min 最小數(shù)
#取最小年齡的作業(yè)
Author.objects.aggregate(Max("age"))
  • Count 統(tǒng)計(jì)行數(shù)
#統(tǒng)計(jì)有多少個(gè)作者
Author.objects.aggregate(Count("id"))
#如果需要去重
Author.objects.aggregate(Count("age", distinct=True))
  • Sum 統(tǒng)計(jì)和
#獲取所有作者的年齡總和

Author.objects.aggregate(Sum("age"))
  • 多個(gè)聚合結(jié)果
Author.objects.aggregate(Sum("age"),Min("age"), Max("age"))
  • 自定義名稱聚合字段別名, 你的參數(shù)名就是你需要自定義的聚合結(jié)果名稱
Author.objects.aggregate(avg_age=Avg("age"))   >>>{avg_age:平均年齡值}
  • 使用annotate進(jìn)行聚合查詢
    1 它與aggregate類似,但是aggregate的結(jié)果一定只有一個(gè)值,且aggregate執(zhí)行后就是最終結(jié)果。
    2 aggregate返回的是一個(gè)字典,annotate返回的是一個(gè)QuerySet,可以繼續(xù)進(jìn)行查詢。
    3 annotate的聚合結(jié)果是針對(duì)每行數(shù)據(jù)的,而不是整個(gè)查詢結(jié)果。
    在博客常見(jiàn)側(cè)邊欄有分類列表,顯示博客已有的全部文章分類?,F(xiàn)在想在分類名后顯示該分類下有多少篇文章,該怎么做呢?這個(gè)就可以用到annotate函數(shù),先按類名進(jìn)行分組,然后再統(tǒng)計(jì)每個(gè)組分別為多少數(shù)量即可。
  • 使用annotate進(jìn)行集合查詢
#統(tǒng)計(jì)每本書(shū)的作者有多少
Book.objects.annotate(Count("anthors"))

#統(tǒng)計(jì)每個(gè)年齡分別有多少人
# values 就等同于group by,返回的是一個(gè)字典
# values_list 就等同于group by,, 返回的是一個(gè)元祖
Author.objects.values("age").annotate(Count("id"))

事務(wù)

如果需要支持事務(wù),mysql引擎必須是InnoDB類型。

  • 引入django處理事務(wù)的包
 from django.db import transaction
  • 使用裝飾器事務(wù)控制
    ps: 使用裝飾器的事務(wù)必須是view視圖函數(shù)
    ps: 通用視圖類還未提供裝飾器事務(wù)控制器
# 這個(gè)裝飾器不能在通用View視圖中使用
# 現(xiàn)在django還只提供了在view函數(shù)中使用的裝飾器
@transaction.atomic
def model_study(request):
    # 使用事務(wù)裝飾器以后
    # 整個(gè)view函數(shù)里面的數(shù)據(jù)庫(kù)操作
    # 要么全部成功
    # 要么全部失敗
    # 下面的代碼在一個(gè)事務(wù)中執(zhí)行,一但出現(xiàn)異常,整個(gè)函數(shù)中所有的數(shù)據(jù)庫(kù)操作全部都會(huì)回滾
    book = Book.objects.create(headline="事務(wù)操作1")
    author = Author.objects.create(name="kevin", email="test@qq.com", age=28)
    book.authors.add(author)
    book.save()
    # assert not book.headline.find("操作") >= 0, "這是敏感關(guān)鍵字"
    return render(request, "model_study/model_study.html")
  • 使用上下文管理器的方式
from django.db import transaction
class ViewClass(View)
    def get(self, request)
        # 下面的代碼在自動(dòng)提交模式下執(zhí)行(Django的默認(rèn)模式)
        with transaction.atomic():   # with中的代碼,全部都會(huì)保持原子性     
            # 下面的代碼在一個(gè)事務(wù)中執(zhí)行,一但出現(xiàn)異常,整個(gè)with函數(shù)內(nèi)部的數(shù)據(jù)庫(kù)操作都會(huì)回滾
            book = Book.objects.create(headline="事務(wù)操作3")
            author = Author.objects.create(name="kevin", email="test@qq.com", age=28)
            book.authors.add(author)
            book.save()
            assert 1==1
            # raise Exception, "這里出現(xiàn)了一個(gè)bug"

ps: with 內(nèi)部最好不要使用try...catch...模塊,否則可能會(huì)影響django的事務(wù)異常判斷。

課后練習(xí)

1、創(chuàng)建一個(gè)班級(jí)的模型(名稱、班號(hào)(一對(duì)一外鍵),創(chuàng)建時(shí)間(自動(dòng)添加),修改時(shí)間(自動(dòng)更新))
2、創(chuàng)建一個(gè)班號(hào)的模型(號(hào)碼)
3、創(chuàng)建一個(gè)學(xué)生的模型(名稱、班級(jí)(多對(duì)一外鍵 ),老師(多對(duì)多),年齡,性別)
4、創(chuàng)建一個(gè)老師的模型( 名稱,年齡,性別,班級(jí)(多對(duì)一外鍵))

  • app應(yīng)用models.py中建立模型類
#班號(hào)模型
class Classesnumber(models.Model): #類名代表了數(shù)據(jù)庫(kù)表名,且繼承了models.Model,類里面的字段代表數(shù)據(jù)表中的字段(name)
    # 屬性=models.字段類型(選項(xiàng))
    # 如果沒(méi)有添加主鍵,django會(huì)默認(rèn)添加一個(gè)ID的主鍵
    classgrade_number = models.CharField(u'號(hào)碼',max_length=20) #備注也可用verbose_name
    class Meta: #模型元選項(xiàng)
        db_table = u'classnumber'   #在數(shù)據(jù)庫(kù)中的表名,否則Django自動(dòng)生成為app名字_類名
        managed = True
        # 數(shù)據(jù)的默認(rèn)排序, 如果需要倒序,則添加"-"號(hào)即可,這里可以按多個(gè)排序
        # 多個(gè)排序會(huì)按你的數(shù)組列表順序進(jìn)行排序
        ordering = ['id','classgrade_number']
        # 描述,當(dāng)查詢結(jié)果是一條記錄時(shí),它的描敘
        verbose_name = u'班號(hào)' #對(duì)象的描述
        # 如果查詢結(jié)果為多個(gè)記錄,則返回verbose_name_plural的描敘
        verbose_name_plural = u'班號(hào)集' #復(fù)數(shù)時(shí)的描述
#班級(jí)模型
class Classesgrade(models.Model):
    name = models.CharField(u'名稱',max_length=20)
    create_time = models.DateTimeField(u'創(chuàng)建時(shí)間',auto_now_add=True)  #auto_now_add只有第一次才會(huì)生效
    update_time = models.DateTimeField(verbose_name= u'修改時(shí)間',auto_now=True)#auto_now每一次修改的動(dòng)作,都會(huì)更新一個(gè)時(shí)間
    #與班級(jí)一對(duì)一關(guān)系,關(guān)聯(lián)屬性,Django會(huì)自動(dòng)建立個(gè)屬性_id字段
    number = models.OneToOneField(Classesnumber)
    class Meta:
        db_table = 'classesgrade'
        managed = True
#老師模型
class Teacher(models.Model):
    name = models.CharField(u'姓名',max_length=20)
    age = models.IntegerField(u'年齡')
    sex = models.CharField(u'性別',max_length=2)
    #老師與班級(jí)多對(duì)一,關(guān)聯(lián)屬性在多方定義
    #db_constraint:bool  是否建立外鍵約束,
    #to_field:string 關(guān)聯(lián)到的關(guān)聯(lián)對(duì)象的字段名稱。默認(rèn)地,Django 使用關(guān)聯(lián)對(duì)象的主鍵。
    #CASCADE:級(jí)聯(lián)刪除,如果刪除,相關(guān)聯(lián)的那個(gè)也會(huì)刪除
    classesno = models.ForeignKey(Classesgrade,db_constraint = True,to_field='id',on_delete = models.CASCADE)
    class Meta:
        db_table = 'teacher'
        managed = True
#學(xué)生
class Student(models.Model):
    name = models.CharField(u'姓名',max_length=20)
    age = models.IntegerField(u'年齡')
    sex = models.CharField(u'性別',max_length=2)
    classesno = models.ForeignKey(Classesgrade,db_constraint = True,to_field='id',on_delete = models.CASCADE)
    # 多對(duì)多,Django會(huì)自動(dòng)創(chuàng)建中間表
    teacher = models.ManyToManyField(Teacher,related_name = 'teacher_set')
    class Meta:
        db_table = 'student'
        managed = True
  • 添加數(shù)據(jù)
    班號(hào)(“001”,“002“)
    老師 ("k", “28”, "男",“django框架班001”)
    老師 ("山", “28”, "男",“django框架班001” )
    老師 ("不", “28”, "男",“django框架班002” )
    班級(jí)(“django框架班” ,“班號(hào)(001) ”,“自動(dòng)創(chuàng)建”, “自動(dòng)更新”)
    班級(jí)(“django框架班” ,“班號(hào)(002) ”,“自動(dòng)創(chuàng)建”, “自動(dòng)更新”)
    學(xué)生(“學(xué)生1”,20,“男”,“001 ”,[“k老師”,“山”])
    學(xué)生(“學(xué)生2”,22,“女”,“002 ”,["山", “不”])
    學(xué)生(“學(xué)生3”,21,“男”,“001 ”, ["k", “不”])
class AddInfor(View):
    def get(self,request):
        #添加班號(hào)數(shù)據(jù)
        #可以用save和create方式
        cnumber1 = Classesnumber.objects.create(classgrade_number = '001')
        cnumber2 = Classesnumber.objects.create(classgrade_number = '002')

        #添加班級(jí)數(shù)據(jù):
        #如果存在外鍵關(guān)聯(lián),主表必須首先得先有數(shù)據(jù),一對(duì)一關(guān)系的添加數(shù)據(jù)
        #一對(duì)一賦值必須保持一個(gè)對(duì)象只對(duì)應(yīng)一個(gè)外鍵
        cnumber3 = Classesnumber.objects.get(pk=1)  #1對(duì)1,先取出主表的pk=1對(duì)象
        Classesgrade.objects.create(name = u'django框架班',number = cnumber3 ) #再賦給外鍵
        cnumber4 = Classesnumber.objects.get(pk=2)
        cgrade = Classesgrade(name = u'django框架班',number = cnumber4)
        cgrade.save()

        # 添加老師數(shù)據(jù):
        #獲取班級(jí)對(duì)象
        cgrade1 = Classesgrade.objects.get(pk=1)  #1對(duì)多,先取出主表的pk=1對(duì)象
        cgrade2 = Classesgrade.objects.get(pk=2)  #1對(duì)多,先取出主表的pk=2對(duì)象

        # # 通過(guò)獲取一端對(duì)象,則這里要用類屬性而不是表字段的名字
        Teacher.objects.create(name = u'k',age = 28,sex = u'男',classesno = cgrade1)
        Teacher.objects.create(name = u'山',age = 28,sex = u'男',classesno = cgrade1)
        Teacher(name = u'不',age = 28,sex = u'男',classesno = cgrade2).save()

        #添加學(xué)生數(shù)據(jù)views.py中:
        #與班級(jí),多對(duì)一外鍵
        stu1 = Student()
        stu1.name = u'學(xué)生1'
        stu1.age = 20
        stu1.sex = u'男'
        stu1.classesno = cgrade1
        stu1.save()

        stu2 = Student(name = u'學(xué)生2',age = 22,sex = u'女',classesno = cgrade2)
        stu2.save()
        stu3 = Student.objects.create(name = u'學(xué)生3',age = 21,sex = u'男',classesno = cgrade1)
        #與老師多對(duì)多
        #多對(duì)多的添加,需要使用add方法
        #多對(duì)多的添加,必須主類必須在數(shù)據(jù)庫(kù)已經(jīng)存在
        tch1 = Teacher.objects.get(name__exact= u'k')
        tch2 = Teacher.objects.get(name__exact= u'山')
        tch3 = Teacher.objects.get(name__exact= u'不')

        stu1.teacher.add(tch1)
        stu1.teacher.add(tch2)
        stu1.save()

        stu2.teacher.add(tch2,tch3)
        stu2.save()

        stu3.teacher.add(tch1,tch3)
        stu3.save()
        return render(request,'result.html',locals())
  • 查詢數(shù)據(jù)views.py中
#app應(yīng)用urls.py中
urlpatterns = [
    url(r'^register/$', views.Register.as_view(),name='register'),
    url(r'^login/$', views.Login.as_view(),name = 'login'),
    url(r'^index/$', views.Index.as_view(),name='index'),
    url(r'^logout/$', views.Logout.as_view(),name='logout')
]
class QueryInfor(View):
    def get(self,request):
        #查詢性別為男的學(xué)生
        stu_boys = Student.objects.filter(sex__exact = u'男')
        #查詢年齡大于20歲的學(xué)生
        stu_20 = Student.objects.filter(age__gt = 20)
        #獲取所有學(xué)生、并按年齡排序
        stu_all = Student.objects.all().order_by('age')
        #獲取所有學(xué)生并排除性別為女的學(xué)生
        stu_nogirls = Student.objects.exclude(sex = u'女')
        #獲取學(xué)生總數(shù)
        stu_sum = Student.objects.count()
        #獲取最后創(chuàng)建的班級(jí)
        last_grade = Classesgrade.objects.latest('create_time')

        cgrade1 = Classesgrade.objects.get(pk = 1)
        cgrade2 = Classesgrade.objects.get(pk = 2)

        #查詢班級(jí)下老師
        #本身班級(jí)對(duì)象是沒(méi)有老師屬性,django提供的一個(gè)反查機(jī)制
        # 如果設(shè)置related_name, 我們就通過(guò)related_name反查
        # 如果沒(méi)有設(shè)置, django默認(rèn)設(shè)置的是反查class的小寫(xiě)名字
        # related_name 可以當(dāng)成反查所用的別名
        tchs1 = cgrade1.teacher_set.all() #xxx_set 實(shí)際返回的是一個(gè)空值,如需訪問(wèn)它們,則要進(jìn)行一個(gè)查詢QuerySet操作
        tchs2 = cgrade2.teacher_set.all()

        #查詢班級(jí)下學(xué)生
        stu1 = cgrade1.student_set.all()
        stu2 = cgrade2.student_set.all()

        #查詢一個(gè)班級(jí)的編號(hào),1對(duì)1 反查
        cgradeno1 =cgrade1.number #cgrade1.number是個(gè)<Classesnumber: Classesnumber object>

        cgradeno2 = cgrade2.number

        #查詢年齡最大的學(xué)生
        sage = Student.objects.aggregate(max_age = Max('age')) #以字典形式返回年齡最大值,{'age__max': 22}
        maxage_stu = Student.objects.filter(age = sage['max_age'])
        #filter(**kwargs)方法:根據(jù)參數(shù)提供的提取條件,獲取一個(gè)過(guò)濾后的QuerySet。所以在前端不能直接獲取對(duì)象的方式來(lái),需要遍歷來(lái)獲取,即使只有一個(gè);

        #查詢年齡最小的學(xué)生
        sage = Student.objects.aggregate(Min('age')) #以字典形式返回年齡最大值,{'age__max': 22}
        minage_stu = Student.objects.filter(age = sage[sage.keys()[0]]) #sage.keys()[0]獲取字典的鍵

        #avgage_stu查詢學(xué)生的平均年齡
        sage = Student.objects.aggregate(Avg('age'))
        
        for key,value in sage.items():
            keys = key
            age = value

        #查詢每個(gè)年齡的學(xué)生數(shù)量,按年齡進(jìn)行分組,然后統(tǒng)計(jì)數(shù)量select age,count(age) from student group by age;
        agestucount = Student.objects.values('age').annotate(Count('id')) #annotate返回的是一個(gè)QuerySet,可以繼續(xù)進(jìn)行查詢
        return render(request,'queryInfor.html',locals())

頁(yè)面效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>查詢信息</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>

<h2>1.查詢性別為男的學(xué)生:</h2>
<p>
{%for stu_boy in stu_boys%}
姓名:{{stu_boy.name}}<br/>
性別:{{stu_boy.sex}}<br/>
年齡:{{stu_boy.age}}<br/>
班級(jí):{{stu_boy.classesno_id}}<br/>
{%endfor%}
</p>
<hr/>

<h2>2.查詢年齡大于20歲的學(xué)生:</h2>
<p>
{%for stu in stu_20%}
姓名:{{stu.name}}<br/>
性別:{{stu.sex}}<br/>
年齡:{{stu.age}}<br/>
班級(jí):{{stu.classesno_id}}<br/>
{%endfor%}
</p>
<hr/>

<h2>3.獲取所有學(xué)生、并按年齡排序:</h2>
<p>
{%for stu in stu_all%}
姓名:{{stu.name}}<br/>
性別:{{stu.sex}}<br/>
年齡:{{stu.age}}<br/>
班級(jí):{{stu.classesno_id}}<br/>
{%endfor%}
</p>
<hr/>

<h2>4.獲取所有學(xué)生并排除性別為女的學(xué)生:</h2>
<p>
{%for stu in stu_nogirls%}
姓名:{{stu.name}}<br/>
性別:{{stu.sex}}<br/>
年齡:{{stu.age}}<br/>
班級(jí):{{stu.classesno_id}}<br/>
{%endfor%}
</p>
<hr/>

<h2>5.獲取學(xué)生總數(shù):</h2>
<p>
    {{stu_sum}}
</p>
<hr/>

<h2>6.獲取最后創(chuàng)建的班級(jí):</h2>
<p>
名稱:{{last_grade.name}}<br/>
創(chuàng)建時(shí)間:{{last_grade.create_time}}<br/>
更新時(shí)間:{{last_grade.update_time}}<br/>
對(duì)應(yīng)班號(hào):{{last_grade.number_id}}
</p>
<hr/>

<h2>11.查詢一個(gè)班級(jí)下有那些老師:</h2>
<p>
{{cgrade1.name}}第{{cgrade1.number.id}}個(gè)班老師:<br/>
{%for tch in tchs1%}
姓名:{{tch.name}}<br/>
性別:{{tch.sex}}<br/>
年齡:{{tch.age}}<br/>
班級(jí):{{tch.classesno_id}}<br/> <!--tch.classesno 對(duì)象.類屬性形式不行,因?yàn)檫€是一個(gè)對(duì)象,要么用對(duì)象.表字段名,要么對(duì)象.類屬性.主表主鍵 -->
{%endfor%}
<hr/>
{{cgrade2.name}}第{{cgrade2.number_id}}個(gè)班老師:<br/>
{%for tch in tchs2%}
姓名:{{tch.name}}<br/>
性別:{{tch.sex}}<br/>
年齡:{{tch.age}}<br/>
班級(jí):{{tch.classesno.id}}<br/>
{%endfor%}
</p>
<h2>22.查詢一個(gè)班級(jí)下有那些學(xué)生:</h2>
<p>
{{cgrade1.name}}第{{cgrade1.number.id}}個(gè)班學(xué)生:<br/>
{%for stu in stu1%}
姓名:{{stu.name}}<br/>
性別:{{stu.sex}}<br/>
年齡:{{stu.age}}<br/>
班級(jí):{{stu.classesno_id}}<br/>
{%endfor%}
<hr/>
{{cgrade2.name}}第{{cgrade2.number_id}}個(gè)班學(xué)生:<br/>
{%for stu in stu2%}
姓名:{{stu.name}}<br/>
性別:{{stu.sex}}<br/>
年齡:{{stu.age}}<br/>
班級(jí):{{stu.classesno_id}}<br/>
{%endfor%}
</p>
<h2>33.查詢一個(gè)班級(jí)的編號(hào):</h2>
<p>
第{{cgrade1.number_id}}個(gè)班編號(hào):{{cgradeno1.classgrade_number}}<br/>
第{{cgrade2.number_id}}個(gè)班編號(hào):{{cgrade2.number.classgrade_number}}
</p>
<h2>44、查詢年紀(jì)最大的學(xué)生</h2>
<p>
{% for stu in maxage_stu %}
姓名:{{stu.name}}<br/>
性別:{{stu.sex}}<br/>
年齡:{{stu.age}}<br/>
班級(jí):{{stu.classesno_id}}<br/>
{% endfor %}
</p>
<h2>55、查詢年紀(jì)最小的學(xué)生</h2>
<p>
{% for stu in minage_stu %}
姓名:{{stu.name}}<br/>
性別:{{stu.sex}}<br/>
年齡:{{stu.age}}<br/>
班級(jí):{{stu.classesno_id}}<br/>
{% endfor %}
</p>
<h2>66、查詢學(xué)生的平均年齡</h2>
<p>
平均{{keys}}:{{value}}z
</p>
<h2>77、查詢每個(gè)年齡的學(xué)生數(shù)量</h2>
{% for dic in agestucount%} <!--agestucount是一個(gè)QuerySet,需要從中迭代出來(lái) -->
    {% for age,countstu in dic.items %} <!--再將每一個(gè)遍歷出來(lái)的字典通過(guò)items將鍵值取出來(lái) -->
        {{age}}:{{countstu}}
    {% endfor %}
    ;<br/>
{% endfor %}
</body>
</html>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容