Django模型層ORM學習筆記

一. 鋪墊

1. 鏈接Django自帶數據庫sqlite3

  以前提到過Django自帶一個叫作sqlite3的小型數據庫,當咱們作本地測試時,能夠直接在sqlite3上測試。不過該數據庫是小型的,在有些細節可能體驗不大好,好比用ORM用雙下劃線查詢語法時,使用__contains和__icontains的結果是同樣的,由於sqlite3不管怎麼樣都不區分大小寫,並且它還會自動把日期格式的字段轉爲時間戳(該體驗賊差)。python

  不過除此以外還好,目前也沒發現其餘問題,作一些數據的小測試仍是綽綽有餘的。mysql

  1.1 項目settings.py文件git

  鏈接其餘數據庫時,咱們須要修改settings.py裏面的相關配置,不過默認的配置就是sqlite3的,因此咱們不須要修改涉及數據庫的配置。sql

  回憶一下,鏈接其餘數據庫時(以鏈接myqsl數據庫爲例),應該修改成如下配置:數據庫

  1.2 修改應用下或項目下的的__init__.py文件django

  跟鏈接其餘數據庫同樣,須要在__init__.py中加入替換操做數據庫的模塊的語句:併發

import pymysql

pymysql.install_as_MySQLdb()

  1.3 鏈接sqlite3數據庫app

  選擇鏈接sqlite3數據庫:框架

  

  安裝驅動,測試鏈接成功後點擊OK鏈接sqlite數據庫:less

  

   1.3步驟執行完畢後,咱們能夠將下圖顯示的那個數據庫刪除(固然留着也沒事)。

       

  1.4 在模型層中寫幾個映射數據庫中表的類

from django.db import models

# Create your models here.


class User(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    register_time = models.DateField()

   1.5 執行數據庫遷移命令(可使用Tools中的Run manage.py Task)

#pycharm下方點擊Terminal打開命令行窗口,執行如下兩句命令
python3 manage.py makemigrations

python3 manage.py migrate

  執行完數據庫遷移命令後,咱們會發現項目中出現了db.sqlite3數據庫,直接雙擊打開:

  

 

 

 

 

  1.6 數據庫sqlite3鏈接完成

2. 測試ORM的準備工做

  數據庫鏈接完畢後,咱們能夠選擇新建.py文件或者使用應用文件夾下自帶的tests.py來導入模型層models.py中的類來熟悉一下ORM語句,不過在這以前要作一些準備工做,否則當你經過ORM操做數據庫時會報錯。

  咱們直接使用應用文件夾下自帶的tests.py文件測試ORM。首先輸入如下語句(前幾行能夠從manage.py中複製)。

from django.test import TestCase

# Create your tests here.
import os


if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day58.settings")
    import django
    django.setup()
    # 以上語句固定寫法,接下來寫導入models模塊的語句
    from app01 import models
    # 從這開始正常寫ORM操做數據庫的語句便可

3.  查看ORM操做數據庫內部走的mysql語句

  當咱們操做數據庫拿回來的是QuerySet對象是,咱們能夠經過QuerySet對象.query查看內部的sql語句,那麼返回的不是QuerySet對象時該怎麼作呢?只須要在settings.py中找個地方將下面語句放着便可(運用日誌)。

配置文件配置參數查看全部orm操做內部的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',
                },
            }
        }
View Code

二. ORM單表查詢、雙下劃線查詢

  爲了更好的瞭解ORM對數據庫的操做,咱們選擇鏈接mysql數據庫,畢竟sqlite3仍是有點小瑕疵的。

from django.db import models

# Create your models here.
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_time = models.DateField(auto_now_add=True)
    publish = models.ForeignKey(to='Publish')
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    email = models.EmailField()


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail')


class AuthorDetail(models.Model):
    phone = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
models.py中建立的類

   ORM有不少經常使用字段,其中字段DateField的參數有auto_now跟auto_now_add,若是咱們配置了這兩個參數中的一個,建立數據對象時DateField字段會自動建立,無需咱們傳值。配置auto_now_add=True,建立數據記錄的時候會把當前時間添加到數據庫,以後就不在變化。配置上auto_now=True,每次更新數據記錄的時候會更新該字段。友情提示,DateFIeld和DateTImeField能夠直接接收datetime對象。

  ForeignKey有to_field參數,能夠指定你想跟一張表的什麼字段創建外鍵,不寫默認是id。

  當使用數據對象進行數據的save()和delete()時,實際上找的是類Model中的方法(屬性與方法的查找順序),因此咱們能夠經過重寫save、delete方法來對數據對象保存和刪除的過程加以限制。

1. 單表查詢

  1.1 新增數據

# 第一種:有返回值,而且就是當前被建立的數據對象
modles.Publish.objects.create(name='',addr='',email='')

# 第二種:先實例化產生對象,而後調用save方法保存
book_obj = models.Publish(name='',addr='',email='')
book_obj.save()

  1.2 修改數據

# 基於數據對象
user_obj = models.User.objects.filter(name='jason').first()
user_obj.age = 17
user_obj.save()

# 基於queryset對象
models.User.objects.filter(name='kevin').update(age=66)

  1.3 刪除數據

# 基於數據對象
# user_obj = models.User.objects.filter(name='owen').first()
# user_obj.delete()

# 基於queryset對象
 models.User.objects.filter(name='egon').delete()

  1.4 查詢數據

  以前經常使用的查詢是all(惰性查詢)查全部及filter條件查詢,其中filter條件查詢有多個條件時,各條件是and的關係。

models.User.objects.filter(name='egon', age=18) # name='egon' and 'age'=18

  其實查詢數據能用到的方法一共有13個(objects對象和QuerySet對象方法),最好都記住。而後QuerySet對象可以無限制的點QuerySet方法(後面點方法可能會沒有提示,別慌,繼續手打):

# 在過濾獲得的QuerySet對象的基礎上再進行過濾,依次類推
models.User.objects.filter(過濾條件1).filter(過濾條件2).order_by(排序依據的字段名).reverse()
# reverse方法使用的前提是QuerySet對象被排序過,order_by是排序(排序依據的字段名前加-號便是反向排序)
<1> all():                 #查詢全部結果
<2> filter(**kwargs):      #它包含了與所給篩選條件相匹配的對象
<3> get(**kwargs):         #返回與所給篩選條件相匹配的對象,返回結果有且只有一個,若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。
<4> exclude(**kwargs):     #它包含了與所給篩選條件不匹配的對象
<5> order_by(*field):      #對查詢結果排序('-id')/('price')
                    
<6> reverse():             #對查詢結果反向排序     >>>前面要先有排序才能反向
<7> count():               #返回數據庫中匹配查詢(QuerySet)的對象數量。
<8> first():               #返回第一條記錄
<9> last():                #返回最後一條記錄
<10> exists():             #若是QuerySet包含數據,就返回True,不然返回False
<11> values(*field):       #返回一個ValueQuerySet——一個特殊的QuerySet,運行後獲得的並非一系列model的實例化對象,而是一個可迭代的字典序列
<12> values_list(*field):  #它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列
<13> distinct():           #從返回結果中剔除重複紀錄
查詢用到的13種方法

   着重記住values方法(普通QuerySet對象:<QuerySet [<Author: Author object>]>):

  在上述13種方法中,有些方法執行完返回的不必定是QuerySet對象,多是其餘對象。

# 返回QuerySet對象的方法
all()、filter()、exclude()、order_by()、reverse()、distinct()

# 特殊的QuerySet
values()        #返回一個可迭代的字典序列
values_list()  #返回一個可迭代的元祖序列

# 返回具體數據對象的
get()、first()、last()

# 返回布爾值的方法
exists() #判斷QuerySet對象是否爲空,空返回False,不然返回True

# 返回數字的方法
count()  #返回當前QuerySet對象中的數據對象個數

  舒適提示:all()跟filter()是惰性查詢,即返回QuerySet對象,只有調用QuerySet對象時內部的sql語句纔會執行(這就是惰性的精髓)。objects是管理器對象(objects = Manage()),是Models和數據庫進行查詢的接口。Manage存在於models模塊中,因此咱們實際上是能夠自定義一個類,繼承models.Manage來定義本身的管理器對象的。

2. 雙下劃線查詢

  上述的查詢中,filter的查詢條件都是name='值',age=值之類的,但是咱們查詢時條件確定會有age>18,age<=18等狀況。這個時候就須要用的雙下劃線查詢。

  2.1 大於 小於 大於等於 小於等於

filter(price__gt=90)   # 大於 great than
filter(price__lt=90)    # 小於 less than
filter(price__gte=90) # 大於等於 great than equal
filter(price__lte=90)  # 小於等與 less than equal

  2.2 存在於某幾個條件中

filter(age__in=[11,22,33])

  2.3 在某個範圍內

filter(age__range=[50,90])

  2.4 模糊查詢

filter(title__contains='p')   # 區分大小寫
filter(title__icontains='P')  # 不區分大小寫

  2.5 以什麼開頭,以什麼結尾

# 查詢名字以j開頭的用戶
res = models.User.objects.filter(name__startswith='j')
print(res)

# 查詢名字以n結尾的用戶
res = models.User.objects.filter(name__endswith='n')
print(res)

  2.6 按年查詢(針對DateField和針對DateTImeField

filter(create_time__year='2017')

三. 多表操做

1. 一對多記錄增刪改查

  1.1 新增(主鍵用pk傳比較好,比較穩)

 # 直接寫id
models.Book.objects.create(title='紅樓夢',price=66.66,publish_id=1)

# 傳數據對象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='三國演義',price=199.99,publish=publish_obj)

  1.2 修改

# Queryset修改
models.Book.objects.filter(pk=1).update(publish_id=3)
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)

# 對象修改
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.publish_id = 3  # 點表中真實存在的字段名
book_obj.save()

publish_obj = models.Publish.objects.filter(pk=2).first()
book_obj.publish = publish_obj  # 點orm中字段名 傳該字段對應的表的數據對象
book_obj.save()

  1.3 刪除

# 使用QuerySet對象刪除
models.Book.objects.filter(pk=1).delete()
models.Publish.objects.filter(pk=1).delete()

# 使用數據對象刪除
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.delete()

2. 多對多記錄增刪改查

  2.1 添加(add)

# 拿到id=3的書籍數據對象
book_obj = models.Book.objects.filter(pk=3).first()

# 數據對象.authors能夠直接跳到多對多那張表裏
# add傳值傳做者id,能夠傳多個
book_obj.authors.add(1)
book_obj.authors.add(2,3)

# add傳值支持傳對象,並且能夠傳多個
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj)
book_obj.authors.add(author_obj,autor_obj1)

   2.2 修改(set)

   set接收的參數必須是可迭代對象!!!

# 拿到id=3的書籍數據對象
book_obj = models.Book.objects.filter(pk=3).first()

# 數據對象.authors能夠直接跳到多對多那張表裏
# set傳值傳做者id,能夠傳多個
book_obj.authors.set((1,))
book_obj.authors.set((1,2,3))

# set傳值支持傳對象,並且能夠傳多個
author_list = models.Author.objects.all()
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set(author_list)

   2.3 刪除(remove

# 拿到id=3的書籍數據對象
book_obj = models.Book.objects.filter(pk=3).first()

# 數據對象.authors能夠直接跳到多對多那張表裏
# remove傳值支持傳值,並且能夠傳多個
book_obj.authors.remove(1)
book_obj.authors.remove(2,3)

# remove傳值支持傳對象,並且能夠傳多個
author_obj = models.Author.objects.all().first()
author_list = models.Author.objects.all()
book_obj.authors.remove(author_obj)
book_obj.authors.remove(*author_list)  #須要將Queryset對象打散

   2.4 清空(clear)

   清空的是你當前這個表記錄對應的綁定關係,不會影響其餘表。

book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear()

 3. 正向反向查詢

  正向反向查詢概念及方法:

# 一對一
# 正向:author---關聯字段在author表裏--->authordetail        按字段
# 反向:authordetail---關聯字段在author表裏--->author        按表名小寫
    # 查詢jason做者的手機號   正向查詢
    # 查詢地址是 :山東 的做者名字   反向查詢
  
# 一對多
# 正向:book---關聯字段在book表裏--->publish        按字段
# 反向:publish---關聯字段在book表裏--->book        按表名小寫_set.all() 由於一個出版社對應着多個圖書

# 多對多
# 正向:book---關聯字段在book表裏--->author        按字段
# 反向:author---關聯字段在book表裏--->book        按表名小寫_set.all() 由於一個做者對應着多個圖書
  
  

# 連續跨表
    # 查詢圖書是三國演義的做者的手機號,先查書,再正向查到做者,在正向查手機號

# 總結:基於對象的查詢都是子查詢,這裏能夠用django配置文件自動打印sql語句的配置作演示
View Code  

  3.1 基於對象的表查詢

# 查詢書籍是三國演義的出版社郵箱
book_obj = models.Book.objects.filter(title='三國演義').first()
print(book_obj.publish.email)

# 查詢書籍是水滸傳的做者的姓名
book_obj = models.Book.objects.filter(title='水滸傳').first()
print(book_obj.authors)  # app01.Author.None
print(book_obj.authors.all())

# 查詢做者爲jason電話號碼
user_obj = models.Author.objects.filter(name='jason').first()
print(user_obj.authordetail.phone)
正向查詢示例
# 查詢出版社是東方出版社出版的書籍   一對多字段的反向查詢
publish_obj = models.Publish.objects.filter(name='東方出版社').first()
print(publish_obj.book_set)  # app01.Book.None
print(publish_obj.book_set.all())

# 查詢做者jason寫過的全部的書           多對多字段的反向查詢
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set)  # app01.Book.None
print(author_obj.book_set.all())

# 查詢做者電話號碼是110的做者姓名     一對一字段的反向查詢
authordetail_obj = models.AuthorDetail.objects.filter(phone=110).first()
print(authordetail_obj.author.name)
反向查詢示例

    3.2 基於雙下劃線的查詢

# 查詢書籍爲三國演義的出版社地址
res = models.Book.objects.filter(title='三國演義').values('publish__addr','title')
print(res)

# 查詢書籍爲水滸傳的做者的姓名
res = models.Book.objects.filter(title='水滸傳').values("authors__name",'title')
print(res)

# 查詢做者爲jason的家鄉
res = models.Author.objects.filter(name='jason').values('authordetail__addr')
print(res)
正向示例
# 查詢南方出版社出版的書名
res = models.Publish.objects.filter(name='南方出版社').values('book__title')
print(res)

# 查詢電話號碼爲120的做者姓名
res = models.AuthorDetail.objects.filter(phone=120).values('author__name')
print(res)

# 查詢做者爲jason的寫的書的名字
res = models.Author.objects.filter(name='jason').values('book__title')
print(res)

# 查詢書籍爲三國演義的做者的電話號碼
res = models.Book.objects.filter(title='三國演義').values('authors__authordetail__phone')
print(res)
反向示例

    3.3 小練習

# 查詢jason做者的手機號
# 正向
res = models.Author.objects.filter(name='jason').values('authordetail__phone')
print(res)
# 反向
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone')
print(res)

# 查詢出版社爲東方出版社的全部圖書的名字和價格
# 正向
res = models.Publish.objects.filter(name='東方出版社').values('book__title','book__price')
# print(res)
反向
# res = models.Book.objects.filter(publish__name='東方出版社').values('title','price')
print(res)

# 查詢東方出版社出版的價格大於400的書
# 正向
res = models.Publish.objects.filter(name="東方出版社",book__price__gt=400).values('book__title','book__price')
print(res)
# 反向
res = models.Book.objects.filter(price__gt=400,publish__name='東方出版社').values('title','price')
print(res)
View Code

  在查詢的時候先把orm查詢語句寫出來,再看用到的條件是否在當前表內,在就直接獲取,不在就按照正向按字段反向按表名小寫來查便可。切忌一口吃成胖子。

4 聚合查詢(aggregate)

  須要先導入模塊:

from django.db.models import Max,Min,Count,Sum,Avg
# 查詢全部書籍的做者個數
res = models.Book.objects.filter(pk=3).aggregate(count_num=Count('authors'))
print(res)

# 查詢全部出版社出版的書的平均價格
res = models.Publish.objects.aggregate(avg_price=Avg('book__price'))
print(res)  # 4498.636

# 統計東方出版社出版的書籍的個數
res = models.Publish.objects.filter(name='東方出版社').aggregate(count_num=Count('book__id'))
print(res)
聚合查詢例子

5 分組查詢(annotate)

# 統計每一個出版社出版的書的平均價格
res = models.Publish.objects.annotate(avg_price=Avg('book__price')).values('name','avg_price')
print(res)

# 統計每一本書的做者個數
res = models.Book.objects.annotate(count_num=Count('authors')).values('title','count_num')
print(res)

# 統計出每一個出版社賣的最便宜的書的價格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)

# 查詢每一個做者出的書的總價格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
print(res)
分組查詢例子

 四. 其餘

1. F、Q查詢

  目前爲止咱們filter的條件的值都是咱們本身給的,好比age=18。並且filter多個查詢條件時,它們是and的關係。當咱們查詢條件的值是來自表中某字段的值,或者咱們查詢條件是or的關係,咱們就要引入F、Q查詢。

  首先F、Q使用前要先導入:

from django.db.models import F,Q

  1.1 F查詢

  假若有一張服裝店的銷售表(商品、價格、庫存、銷量),當咱們想要查詢銷量大於50的商品時很好實現:

res = models.Product.objects.filter(sell__gt=50)
print(res)

  當咱們要查詢銷量大於庫存的商品時,就要使用F查詢了:

from django.db.models import F,Q
# F查詢
res = models.Product.objects.filter(sell__gt=F('stock'))
print(res)

  將全部商品的價格提升100:

models.Product.objects.update(price=F('price')+100)

  將全部商品的名字後面都加一個爆款:

# 是否是本能會這麼想?注意,這是錯誤的寫法,由於ORM不支持+拼接字符串
models.Product.objects.update(price=F('name')+'爆款')

  ORM不支持+拼接字符串,mysql中咱們拼接字符串要用concat,此處同理:

# 要進行字符串的拼接須要先導入Concat和Value模塊
from django.db.models.functions import Concat
from django.db.models import Value

models.Product.objects.update(name=Concat(F('name'), Value('爆款')))

  1.2 Q查詢

  Q查詢能夠將filter中的查詢條件變成or的關係,並且還能經過實例化對象的方法實現屬性能夠是字符串的形式(查詢時原本要寫name='xxx',Q查詢能夠實現'name'='xxx')。

  注意:Q對象必須放在普通的過濾條件前面。

# Q查詢
res = models.Product.objects.filter(price=188.88, name='連衣裙爆款')
print(res)
from django.db.models import F, Q
res = models.Product.objects.filter(Q(price=188.88), Q(name='連衣裙爆款'))  # and

res1 = models.Product.objects.filter(Q(price=188.88)|Q(name='連衣裙爆款'))  # or

res2 = models.Product.objects.filter(Q(price=188.88)|~Q(name='連衣裙爆款'))  # not

# 混合使用  須要注意的是Q對象必須放在普通的過濾條件前面
res3 = models.Product.objects.filter(~Q(name='連衣裙爆款'), price=188.88)  # not
上圖代碼

   前面說Q查詢能夠實現查詢條件寫成'name'='xxx'的格式(好比說咱們寫一個函數,根據用戶輸入去數據庫中查詢,而用戶輸入都是字符串的形式)。也許你會進行如下嘗試:

user_input = 'name'
res = models.Author.objects.filter(eval(user_input)='jason')
print(res)

   而後就會立刻迎來喜報:

  要實現該需求,咱們須要使用Q實例化出的對象來實現(先來看一下Q的源碼):

  實現的代碼以下:

from django.db.models import F, Q
q = Q()  # 先實例化出一個Q的對象
q.connector = 'or'  # 經過這個參數能夠將Q對象默認的and關係變成or

q.children.append(('price', 188.88))
q.children.append(('name', '高跟鞋爆款'))

res = models.Product.objects.filter(q)  # Q對象查詢默認是and,上方截圖爲證
print(res)
上圖代碼

 2. 事務

  2.1 事務的四大特性:

  Atomic(原子性):事務中包含的操做被看作一個邏輯單元,這個邏輯單元中的操做要麼所有成 功,要麼所有失敗。

  Consistency(一致性):事務完成時,數據必須處於一致狀態,數據的完整性約束沒有被破壞,事務在執行過程當中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務歷來沒    有執行過同樣。

  Isolation(隔離性):事務容許多個用戶對同一個數據進行併發訪問,而不破壞數據的正確性 和完整性。同時,並行事務的修改必須與其餘並行事務的修改相互獨立。

  Durability(持久性):事務結束後,事務處理的結果必須可以獲得固化。

  2.2 事務的語法

  在sql語句中使用事務時,咱們須要用start開啓事務,又要用end結束事務,這就須要咱們判斷事務什麼時候開始和結束比較好。而在Django的ORM中,直接使用上下文管理便可,省事又省心。

  這裏直接拿上面提到過的銷售表作示例,好比一件商品賣出後,銷量要+1,對應的庫存要-1。

# 導入事務的模塊
from django.db import transaction
from django.db.models import F
with transaction.atomic():  # atomic原子性,要麼全成功,要麼全失敗
    # 在with代碼塊兒寫你的事務操做
    models.Product.objects.filter(id=1).update(stock=F('stock')-1)
    models.Product.objects.filter(id=1).update(sell=F('sell')+1)
# 寫其餘代碼邏輯
print('支付寶到帳~~~')

 3. only和defer

  only和defer是相反的兩個過程,有點相似filter()和exclude()的區別。咱們知道values返回的是列表套字典形式的QuerySet對象,而only和defer返回的是列表套對象的QuerySet對象。

  以Product爲例,當使用only('name')時,返回的對象只攜帶Product對象的name屬性,若是用對象.未攜帶的屬性,那麼它會自動再去數據庫中Product映射的表中查找該屬性,有就帶回來返回給你。defer正好相反,defer('name')表示攜帶除name以外的全部屬性,當對象.name時,會自動去數據庫中Product映射的表中找name屬性帶回來給你。注意:only和defer自動找數據庫找屬性時會走sql語句。

# 輸出一下用values取出來的結果(列表套字典,鍵爲'name')
test = models.Product.objects.values('name')
print(test)


# only取出來的是列表套對象,對象只有name屬性
res = models.Product.objects.only('name')
for i in res:
    print(i.name)


# defer取出來的是列表套對象,對象除了name屬性,其餘屬性都有
res1 = models.Product.objects.defer('name')
for i in res:
    print(i.name)

 4. 自定義字段

  Django支持自定義字段,須要繼承models.Field類。

# 自定義字段,對應數據庫中的Char類型
class MyCharField(models.Field):
    # 數據庫中Char類型須要指定長度,因此要傳max_length
    def __init__(self,max_length,*args,**kwargs):
        self.max_length = max_length
        # 調用父類init,必定要寫關鍵字傳參,由於Field的init參數不少,能夠看一下它的源碼
        super().__init__(max_length=max_length,*args,**kwargs)
    
    # 該方法也不能少
    def db_type(self, connection):
        return 'char(%s)'%self.max_length

   父類models.Field中的db_type方法

 

5. 字段中的choices屬性

  提一下,當咱們給一個表新增一個字段時,若是表中已經有數據了,那麼咱們能夠將models.py中新增的字段名null設置默認值或者是設置爲空。

# 設置默認值
gender = models.IntegerField(default=2)

# 容許爲空
gender = models.IntegerField(null=True)

  choices屬性使用的場景也挺多,好比性別,在數據庫中咱們能夠存儲數字,好比1表明男,2表明女,而顯示給用戶看時,要轉變爲對應的男或女。

  簡單示範一下:

class User(models.Model):
    name = models.CharField(max_length=32)

    choices = ((1,''),(2,''),(3,'其餘'))
    gender = models.IntegerField(choices=choices,default=2)

  這樣數據庫裏存的就是數字了,那麼取出來怎麼變成對應的男、女呢:

res = models.User.objects.filter(id=1).first()
print(res.gender)
# 獲取編號對應的中文註釋,固定寫法get_字段名_display()
print(res.get_gender_display())  


# 建立對象是傳數字便可
models.User.objects.create(...gender=1)

五. 補充

1. MVC與MTV

  Django大體分爲模型層(models)、模板層(templates)、視圖層(views)、路由層(urls)。由於Django中控制器接受用戶輸入的部分由框架自行處理,因此 Django 裏更關注的是模型models、模板templates和視圖views,稱爲 MTV模式。不過本質上也是屬於MVC框架(模型model,視圖view和控制器controller)。

 2. 多對多表建立的三種方式

  以前建立測試ORM的表時,多對多關係的表是經過語句讓ORM自動幫咱們建立的,實際上建立多對多的表有三種方式。

  第一種:Django主動咱們幫咱們建立

  該方法很方便,可是第三張表字段都是固定的,沒法擴展

class Book(models.Model):
    name = models.CharField(max_length=32)
    # 該命令讓Django自動幫咱們建立多對多關係的第三張表
    authors = models.ManyToManyField(to='Author')


class Author(models.Model):
    name = models.CharField(max_length=32)

  第二種:純手動建立第三張表

  該方式能夠在第三張表中增長額外的字段,可是沒法像第一種方式建立的表同樣直接利用ORM正向反向查詢信息(好比book對象要查詢Author表中的內容,只能是反向查詢至Book2Author這張表,而後經過該表正向查詢Author表的內容)。

class Book(models.Model):
    name = models.CharField(max_length=32)

class Author(models.Model):
    name = models.CharField(max_length=32)

class Book2Author(models.Model):
    # 經過建立多對多關係的兩張表的外鍵來實現
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')
    info = models.CharField(max_length=32)

   第三種:半自動建立

  當一個項目中對數據庫擴展性有要求時,雖然第二種的確增長了擴展性,可是沒法充分利用ORM的多表查詢。接下來講的第三種方法結合了前兩種的優勢。建議使用該方式,擴展性高,若是使用第一種,後期須要在多對多關係的表增長字段時,須要改動的地方不少。

  注意:使用第三種方式建立多對多關聯關係時,就沒法使用set、add、remove、clear方法來管理多對多的關係了,只能經過第三張表的model來管理多對多關係。當更改book對應的author信息時,能夠先將book2author中該book對象對應的信息所有刪除,而後再根據author信息從新建立數據,實際上ORM管理多對多表關係的set方法內部也是採用該方式。

class Book(models.Model):
    name = models.CharField(max_length=32)
    # 第三種建立表的方式
    authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))

class Author(models.Model):
    name = models.CharField(max_length=32)
    # 跟上面的ManyToManyField選一個寫便可
    book = models.ManyToManyField(to='Book',through='Book2Author',through_fields=('author','book'))


class Book2Author(models.Model):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')
    info = models.CharField(max_length=32)

  小提示:

  1. 若是數據庫遷移命令一直有問題,能夠選擇刪除migrations下的文件(__init__.py不要刪,否則再次執行數據庫遷移命令時會報錯)。
  2. 千萬不要多個項目共用一個數據庫,否則很容易出各類bug,有時候第1條問題的出現跟多項目共用數據庫也有關係,嚴重的可能須要刪庫重建。
相關文章
相關標籤/搜索