1. 程式人生 > >Django Model 進階

Django Model 進階

.get 用戶 opinion ngs 個數 mode migration 比較 xtra

回顧:

  • 定義 models

  • settings.py激活app才能使用models

  • migrations:版本控制,當更改庫表結構時可以處理數據

  • 增刪改查

  • 常見Field

模型的價值在於定義數據模型,使用Python的形式操作數據庫

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

轉換為sql

CREATE TABLE
IF NOT EXISTS myapp_person( id INT(11) NOT NULL AUTO_INCREMENT, first_name VARCHAR(30) DEFAULT NULL, last_name VARCHAR(30) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB;

關系:

  • 一對多 ForeignKey,如廠商和汽車關系

  • 多對多 ManyToManyField,如用戶和組關系

  • 一對一 OneToOneField,如飯店和地址關系

# 多對一關系
class Manufacturer(models.Model):
    
pass class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer) # 多對多關系 class Group(models.Model): pass class User(models.Model): groups = models.ManyToManyField(Group) # 一對一關系 class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80)
class Restaurant(models.Model): place = models.OneToOneField(Place, primary_key=True) serves_hot_dogs = models.BooleanField(default=False) serves_pizza = models.BooleanField(default=False)

重寫save()方法:

如保存記錄之前,寫入日誌操作,註意一定要記得調用超類的save()方法,否則不會保存到數據庫

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        print(do something)
        super(Blog, self).save(*args, **kwargs)
        print(do something else)

模型繼承:

  • 抽象基類

只想使用父類來持有一些信息,不想在每個子模型中都敲一遍,這個類永遠不會單獨使用,使用abstract表明基類,數據庫不會建該表

如auth模塊 User繼承(AbstractUser)

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()
    class Meta:
        abstract = True
        ordering = [name]

class Student(CommonInfo):
    home_group = models.CharField(max_length=5)
    class Meta(CommonInfo.Meta):
        db_table = student_info

說明:

Meta 也可以繼承,如果子類不實現Meta則默認會繼承

  • 多表繼承

繼承一個存在的模型,每個模型都有自己的數據庫表。每一個層級下的每個model都是一個真正意義上完整的model,

每個model都有專屬的數據表,都可以查詢和創建數據表,

繼承關系在子model和它的每個父類之間都添加一個鏈接(通過自動創建的OneToOneField來實現)

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

  • 代理繼承

使用多表繼承時,model的每個子類都會自動創建一張新數據表,通常情況下,這正是我們想要的操作,

這是因為子類需要一個空間來存儲不包含在基類中的字段數據。但有時,可能只想更改model在Python層的行為實現,

比如:更改默認的manager,或是添加一個新方法

代理繼承可以做到:為原始模型創建一個代理。可以創建、刪除、更新代理model的實例,而且所有數據都可以像使用原始model一樣保存

不同之處在於:可以在代理model中改變默認的排序設置和默認的manager,不會對原始model產生影響

設置 Meta類中proxy的值為True,就完成了對代理model的聲明

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True
        
    def do_something(self):
        pass

說明:

MyPerson只是一個代理,數據庫中不會建立該表,MyPerson類和它的父類Person操作同一個數據表,

特別的是,Person的任何實例也可以通過MyPerson訪問,反之亦然

技術分享

  • 多重繼承

多重繼承和多表繼承沒什麽大的差別,會自動添加兩個OneToOneField,

需要指出,兩個基類的默認自增id要重新命名,否則添加OneToOneField會有問題,也不允許子類重寫父類字段

class Article(models.Model):
    article_id = models.AutoField(primary_key=True)

class Book(models.Model):
    book_id = models.AutoField(primary_key=True)

class BookReview(Book, Article):
    pass

創建記錄時,父類的各個字段需要填寫

b = BookReview.objects.create(headline=‘T‘ , body=‘body‘ , title= title‘)

Model querys

回顧:

  • 簡單增刪改查

  • 返回結果 object QuerySets

  • exclude

  • 反向查詢 b.entry_set.all() 可起別名,返回QuerySets

  • 復雜查詢 Q

  • 跨關聯關系查詢,__

  • QuerySet鏈式過濾

  • F查詢

  • 限制返回個數

查詢對象比較

比較兩個model實例,實質在後臺,它會比較兩個模型主鍵的值

技術分享

所有查詢api

https://docs.djangoproject.com/en/1.11/ref/models/querysets/

進階:

1. annotate添加註釋屬性 與聚合函數一起使用返回聚合值,如統計問題的個數,q就有了choice_num這個屬性

技術分享

2. aggregate 聚合函數返回結果字典

技術分享

3. 聚合類函數,或註釋函數,在django.db.models模塊中

  • class Avg(expression, output_field=FloatField, **extra)

  • class Count(expression, distinct=False, **extra)

  • class Max(expression, output_field=None, **extra)

  • class Min(expression, output_field=None, **extra)

  • class Sum(expression, output_field=None, **extra)

  • class StdDev(expression, sample=False, **extra) # 標準差

  • class Variance(expression, sample=False, **extra) # 方差

4. distinct() 去重

5. values 返回ValuesQuerySet(類字典),而不是模型實例對象

6. vlaues_list與values類似,但返回元組而不是字典

7. defer only

8. using使用哪個數據庫

9. select_for_update 返回一個queryset,會鎖定相關行直到事務結束

在支持的數據庫上面產生一個 SELECT...FOR UPDATE

技術分享

10. raw 執行原始sql

11. get_or_create,有就獲取;沒就新增,註意返回結果類型和參數defaults含義

try:
    obj = Person.objects.get(first_name=John, last_name=Lennon)
except Person.DoesNotExist:
    obj = Person.objects.get(first_name=John, last_name=Lennon, birthday=(1993, 7, 1))
# Equal
obj, created = Person.objects.get_or_create(
    first_name = John,
    last_name = Lennon,
    defaults={birthday: date(1993, 7, 1)})

12. update_or_create

13. bulk_create

14. in_bulk

15. latest、earliest、first、last

事務:

事務是綁定在view中實現的

from django.db import transaction

@transaction.non_atomic_requests
def my_view(request):
    print(do_stuff)
    
@transaction.atomic
def viewfunc(request):
    print(do_stuff)
    
‘‘‘
警告!!!
雖然這種事務模式的優勢在於它的簡單性,但在訪問量增長到一定的時候會造成很大的性能損耗。
這是因為為每一個視圖開啟一個事物會有一些額外的開銷。
另外,這種性能影響還取決於你的應用程序的查詢模式以及你的數據庫對鎖的處理是否高效。
‘‘‘

自定義管理器:

  • 添加額外管理器方法

默認objects添加自定義方法,方法需寫原生sql。

添加額外管理器方法是類增加“表級”功能的首選方式(如果要添加行級功能,比如只對某個模型的實例起作用,應使用模型方法,而不是管理器方法)

額外管理器方法可以返回你想要的任何數據,而不需要返回一個查詢及集

例如,下面這個自定義管理器提供一個with_counts()方法,它返回所有OpinionPoll對象的列表,列表的每項都有一額外num_reponses屬性,

該屬性保存一個聚合查詢的結果(註:對應的是SQL查詢語句中的COUNT(*)生成的項)

# 添加額外管理器方法
class PollManager(models.Manager):
    def with_counts(self):
        from django.db import connection
        cursor = connection.cursor()
        cursor.execute("""
        SELECT p.id, p.question, p.poll_date, COUNT(*)
        FROM polls_opinionpoll p, polls_resopnse r
        WHERE p.id = r.poll_id
        GROUP BY p.id, p.question, p.poll_date
        ORDER BY p.poll_date DESC""")
        result_list = []
        for row in cursor.fetchall():
            p = self.model(id=row[0], question=row[1], poll_date=row[2])
            p.num_responses = row[3]
            result_list.append(p)
        return result_list

class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    poll_date = models.DateField()
    objects = PollManager()  # 關鍵點***

class Response(models.Model):
    poll = models.ForeignKey(OpinionPoll)
    person_name = models.CharField(max_length=50)
    response = models.TextField()

  • 添加自定義manager

簡單的說,就是在模型裏添加屬性=models.Manager(),或者它的自定義子類,從而實現對庫表的操作

class AuthorManager(models.Manager):
    def get_queryset(self):
        return super(AuthorManager, self).get_queryset().filter(role=A)

class EditorManager(models.Manager):
    def get_queryset(self):
        return super(EditorManager, self).get_queryset().filter(role=E)

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=((A, Author), (E, Editor)))
    people = models.Manager()  # 等同於原始 objects
    authors = AuthorManager()
    editros = EditorManager()

Person.objects.all()
Person.people.all()
Person.authors.all()
Person.editros.all()

Django Model 進階