【三】模型層ORM

折騰躯肌,是對(duì)夢(mèng)想的尊重缭贡。

一炉擅、ORM簡(jiǎn)介

??MVC或者M(jìn)VC框架中包括一個(gè)重要的部分辉懒,就是ORM,它實(shí)現(xiàn)了數(shù)據(jù)模型與數(shù)據(jù)庫(kù)的解耦谍失,即數(shù)據(jù)模型的設(shè)計(jì)不需要依賴于特定的數(shù)據(jù)庫(kù)眶俩,在業(yè)務(wù)邏輯層和數(shù)據(jù)庫(kù)層之間起了橋梁的作用。
??ORM(Object Relational Mapping)是“對(duì)象—關(guān)系—映射”的簡(jiǎn)稱快鱼。

ORM 對(duì)象-關(guān)系-映射

二颠印、前期準(zhǔn)備

1、在settings中配置數(shù)據(jù)庫(kù)

首先想將模型轉(zhuǎn)為mysql數(shù)據(jù)庫(kù)中的表抹竹,要在settings中配置线罕。Django默認(rèn)使用的是sqlite數(shù)據(jù)庫(kù),這里換成mysql窃判。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db_name',   # 要連接的數(shù)據(jù)庫(kù)钞楼,連接前需要?jiǎng)?chuàng)建好
        'HOST': '127.0.0.1',   # 連接主機(jī),默認(rèn)本機(jī)
        'USER': 'root',    # 連接數(shù)據(jù)庫(kù)的用戶名
        'PASSWORD': ' ',   # 連接數(shù)據(jù)庫(kù)的密碼
        'PORT': 3306   # 端口 默認(rèn)3306
    }
}

?? tips: NAME即數(shù)據(jù)庫(kù)的名字袄琳,在mysql連接前該數(shù)據(jù)庫(kù)必須已經(jīng)創(chuàng)建询件,而上面的sqlite數(shù)據(jù)庫(kù)下的db.sqlite3則是項(xiàng)目自動(dòng)創(chuàng)建 USER和PASSWORD分別是數(shù)據(jù)庫(kù)的用戶名和密碼。
??設(shè)置完后啟動(dòng)項(xiàng)目會(huì)報(bào)錯(cuò):no module named MySQLdb唆樊。這是因?yàn)閐jango默認(rèn)你導(dǎo)入的驅(qū)動(dòng)是MySQLdb雳殊,可是MySQLdb 對(duì)于py3有很大問(wèn)題,所以我們需要的驅(qū)動(dòng)是PyMySQL窗轩,接下來(lái)去激活我們的mysql
我們只需要找到項(xiàng)目名文件下的init,在里面寫入:

import pymysql
pymysql.install_as_MySQLdb()

2夯秃、在settings中配置日志Logging

日志在程序開(kāi)發(fā)中是少不了的,通過(guò)日志我們可以分析到錯(cuò)誤在什么地方痢艺,有什么異常仓洼。在生產(chǎn)環(huán)境下有很大的用途,而且一旦涉及到對(duì)數(shù)據(jù)庫(kù)的操作堤舒,更應(yīng)該記錄下來(lái)色建,數(shù)據(jù)是很重要的。在Java開(kāi)發(fā)中通常用log4j,logback等第三方組件舌缤。那么在django中是怎么處理日志箕戳?django利用的就是Python提供的logging模塊,但django中要用logging国撵,還得有一定的配置規(guī)則陵吸,需要在setting中設(shè)置。

(1) logging 模塊
logging模塊為應(yīng)用程序提供了靈活的手段記錄事件介牙、錯(cuò)誤壮虫、警告和調(diào)試信息。對(duì)這些信息可以進(jìn)行收集环础、篩選囚似、寫入文件剩拢、發(fā)送給系統(tǒng)日志等操作,甚至還可以通過(guò)網(wǎng)絡(luò)發(fā)送給遠(yuǎn)程計(jì)算機(jī)饶唤。

a徐伐、 日志記錄級(jí)別
logging模塊的重點(diǎn)在于生成和處理日志消息。每條消息由一些文本和指示其嚴(yán)重性的相關(guān)級(jí)別組成募狂。級(jí)別包含符號(hào)名稱和數(shù)字值呵晨。

級(jí)別 描述
CRITICAL 50 關(guān)鍵錯(cuò)誤/消息
ERROR 40 錯(cuò)誤
WARNING 30 警告消息
INFO 20 通知消息
DEBUG 10 調(diào)試
NOTSET 0 無(wú)級(jí)別

b、記錄器
記錄器負(fù)責(zé)管理日志消息的默認(rèn)行為熬尺,包括日志記錄級(jí)別、輸出目標(biāo)位置谓罗、消息格式以及其它基本細(xì)節(jié)粱哼。

關(guān)鍵字參數(shù) 描述
filename 將日志消息附加到指定文件名的文件
filemode 指定用于打開(kāi)文件模式
format 用于生成日志消息的格式字符串
datefmt 用于輸出日期和時(shí)間的格式字符串
level 設(shè)置記錄器的級(jí)別
stream 提供打開(kāi)的文件,用于把日志消息發(fā)送到文件

c檩咱、 format日志消息格式

格式 描述
%(name)s 記錄器的名稱
%(levelno)s 數(shù)字形式的日志記錄級(jí)別
%(levelname)s 日志記錄級(jí)別的文本名稱
%(filename)s 執(zhí)行日志記錄調(diào)用的源文件的文件名稱
%(pathname)s 執(zhí)行日志記錄調(diào)用的源文件的路徑名稱
%(funcName)s 執(zhí)行日志記錄調(diào)用的函數(shù)名稱
%(module)s 執(zhí)行日志記錄調(diào)用的模塊名稱
%(lineno)s 執(zhí)行日志記錄調(diào)用的行號(hào)
%(created)s 執(zhí)行日志記錄的時(shí)間
%(asctime)s 日期和時(shí)間
%(msecs)s 毫秒部分
%(thread)d 線程ID
%(threadName)s 線程名稱
%(process)d 進(jìn)程ID
%(message)s 記錄的消息

d揭措、內(nèi)置處理器
logging模塊提供了一些處理器,可以通過(guò)各種方式處理日志消息刻蚯。使用addHandler()方法將這些處理器添加給Logger對(duì)象绊含。另外還可以為每個(gè)處理器配置它自己的篩選和級(jí)別。

  • handlers.DatagramHandler(host炊汹,port)?發(fā)送日志消息給位于制定host和port上的UDP服務(wù)器躬充。
  • handlers.FileHandler(filename)?將日志消息寫入文件filename。
  • handlers.HTTPHandler(host, url)?使用HTTP的GET或POST方法將日志消息上傳到一臺(tái)HTTP 服務(wù)器讨便。
  • handlers.RotatingFileHandler(filename)?將日志消息寫入文件filename充甚。如果文件的大小超出maxBytes制定的值,那么它將被備份為filename1霸褒。
    ps: 由于內(nèi)置處理器還有很多伴找,如果想更深入了解,可以查看官方手冊(cè)废菱。

(2) Django中使用logging記錄日志
a技矮、在settings.py中進(jìn)行配置

  • 完整版
?? 先導(dǎo)入模塊
import logging
import django.utils.log
import logging.handlers

LOGGING = {
    'version': 1,   #  解析配置,目前為止殊轴,這是dictConfig 格式唯一的版本
    'disable_existing_loggers': True,  # 這是一個(gè)布爾型值呵曹,默認(rèn)值為True(為了向后兼容)表示禁用已經(jīng)存在的logger,除非它們或者它們的祖先明確的出現(xiàn)在日志配置中侈咕;如果值為False則對(duì)已存在的loggers保持啟動(dòng)狀態(tài)疙教。
    'formatters': {
       'standard': {
            'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'}  #日志格式 
    },
    'filters': {
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
            'include_html': True,
        },
        'default': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': '/sourceDns/log/all.log',     #日志輸出文件
            'maxBytes': 1024*1024*5,                  #文件大小 
            'backupCount': 5,                         #備份份數(shù)
            'formatter':'standard',                   #使用哪種formatters日志格式
        },
        'error': {
            'level':'ERROR',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': '/sourceDns/log/error.log',
            'maxBytes':1024*1024*5,
            'backupCount': 5,
            'formatter':'standard',
        },
        'console':{
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'standard'
        },
        'request_handler': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename': '/sourceDns/log/script.log', 
            'maxBytes': 1024*1024*5, 
            'backupCount': 5,
            'formatter':'standard',
        },
        'scprits_handler': {
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler',
            'filename':'/sourceDns/log/script.log', 
            'maxBytes': 1024*1024*5, 
            'backupCount': 5,
            'formatter':'standard',
        }
    },
    'loggers': {
        'django': {
            'handlers': ['default', 'console'],
            'level': 'DEBUG',
            'propagate': False 
        },
        'django.request': {
            'handlers': ['request_handler'],
            'level': 'DEBUG',
            'propagate': False,
        },
        'scripts': { 
            'handlers': ['scprits_handler'],
            'level': 'INFO',
            'propagate': False
        },
        'sourceDns.webdns.views': {
            'handlers': ['default', 'error'],
            'level': 'DEBUG',
            'propagate': True
        },
        'sourceDns.webdns.util':{
            'handlers': ['error'],
            'level': 'ERROR',
            'propagate': True
        }
    } 
}

??tips:
loggers類型為"django"這將處理所有類型日志;
sourceDns.webdns.views 應(yīng)用的py文件

  • 簡(jiǎn)化版
import logging
import django.utils.log
import logging.handlers
import logging.StreamHandler

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
       'standard': {
            'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'}  #日志格式 
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {   # 數(shù)據(jù)庫(kù)引擎
            'handlers': ['console'],
            'propagate': True,
            'level': 'DEBUG',
        },
    }
}

參數(shù)解析:
【a】formatters:配置打印日志格式
【b】handlers:用來(lái)定義具體處理日志的方式韧拒,可以定義多種淹接,"default"就是默認(rèn)方式十性,"console"就是打印到控制臺(tái)方式,"導(dǎo)入StreamHandler",日志信息會(huì)輸出到指定的stream中,如果stream為空則默認(rèn)輸出到sys.stderr塑悼。
【c】loggers:用來(lái)配置用那種handlers來(lái)處理日志劲适,比如你同時(shí)需要輸出日志到文件、控制臺(tái)厢蒜。

(3) 在settings中注冊(cè)APP

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'book'
]

?? tips:如果出現(xiàn)如下報(bào)錯(cuò)

django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None

因?yàn)镸ySQLclient目前只支持到python3.4霞势,如果使用的更高版本的python,需要修改如下:
通過(guò)查找路徑C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql
把這個(gè)路徑文件中下面這兩句話注釋掉:

if version < (1, 3, 3):
     raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)

?? tips: 如果想打印orm轉(zhuǎn)換過(guò)程中的sql斑鸦,需要在settings中進(jìn)行如下配置:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}  

3愕贡、在models中創(chuàng)建表

(1) 創(chuàng)建模型表

from django.db import models

class Book(models.Model):
     id=models.AutoField(primary_key=True)
     title=models.CharField(max_length=32)
     state=models.BooleanField()
     pub_date=models.DateField()
     price=models.DecimalField(max_digits=8,decimal_places=2)
     publish=models.CharField(max_length=32)

(2) ORM字段介紹
Django提供了很多字段類型,比如URL/Email/IP/ 但是mysql數(shù)據(jù)沒(méi)有這些類型巷屿,這類型存儲(chǔ)到數(shù)據(jù)庫(kù)上本質(zhì)是字符串?dāng)?shù)據(jù)類型,其主要目的是為了封裝底層SQL語(yǔ)句固以。
每個(gè)字段有一些特有的參數(shù),例如嘱巾,CharField需要max_length參數(shù)來(lái)指定VARCHAR數(shù)據(jù)庫(kù)字段的大小憨琳。還有一些適用于所有字段的通用參數(shù)。 這些參數(shù)在文檔中有詳細(xì)定義旬昭,這里我們只簡(jiǎn)單介紹一些最常用的:

【常用字段】

  • AutoField
    ?int自增列篙螟,必須填入?yún)?shù) primary_key=True。當(dāng)model中如果沒(méi)有自增列问拘,則自動(dòng)會(huì)創(chuàng)建一個(gè)列名為id的列( 如果你不指定主鍵的話,系統(tǒng)會(huì)自動(dòng)添加一個(gè)主鍵字段到你的 model)遍略。
    ?自定義一個(gè)主鍵:my_id=models.AutoField(primary_key=True)

  • CharField
    ?字符串類型,必須提供max_length參數(shù)骤坐,用于從數(shù)據(jù)庫(kù)層和Django校驗(yàn)層限制該字段所允許的最大字符數(shù)墅冷。

  • IntegerField
    ?整數(shù)類型,范圍在 -2147483648 to 2147483647

  • FloatField
    ?浮點(diǎn)數(shù)類型. 必須提供兩個(gè)參數(shù):
    ??max_digits?總位數(shù)(不包括小數(shù)點(diǎn)和符號(hào))
    ??decimal_places?小數(shù)位數(shù)

??舉例來(lái)說(shuō), 要保存最大值為 999 (小數(shù)點(diǎn)后保存2位),你要這樣定義字段:models.FloatField(..., max_digits=5, decimal_places=2)
??要保存最大值一百萬(wàn)(小數(shù)點(diǎn)后保存10位)的話,你要這樣定義:models.FloatField(..., max_digits=19, decimal_places=10)
??admin 用一個(gè)文本框(<input type="text">)表示該字段保存的數(shù)據(jù).

  • DateField
    ?日期字段或油,日期格式 YYYY-MM-DD 年月日. 共有下列額外的可選參數(shù):
    ??Argument?描述
    ??auto_now?當(dāng)對(duì)象被保存時(shí),自動(dòng)將該字段的值設(shè)置為當(dāng)前時(shí)間寞忿,每次操作該數(shù)據(jù)都會(huì)自動(dòng)更新時(shí)間,通常用于表示 "last-modified" 時(shí)間戳
    ??auto_now_add?當(dāng)對(duì)象首次被創(chuàng)建時(shí),自動(dòng)將該字段的值設(shè)置為當(dāng)前時(shí)間顶岸,此后不再更新腔彰,通常用于表示對(duì)象創(chuàng)建時(shí)間
    ??(僅僅在admin中有意義...)

  • DateTimeField
    ?日期時(shí)間字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 年月日時(shí)分秒辖佣。參數(shù)和datefield一樣霹抛。

  • BooleanField
    ?A true/false field。admin 用 checkbox 來(lái)表示此類字段卷谈。

  • TextField
    ?一個(gè)容量很大的文本字段
    ?admin 用一個(gè) <textarea> (文本區(qū)域)表示該字段數(shù)據(jù)(一個(gè)多行編輯框)杯拐。

  • EmailField
    ?一個(gè)帶有檢查Email合法性的 CharField,不接受 maxlength 參數(shù)

  • FileField
    ?一個(gè)文件上傳字段
    ? 要求一個(gè)必須有的參數(shù): upload_to, 一個(gè)用于保存上載文件的本地文件系統(tǒng)路徑。這個(gè)路徑必須包含 strftime 端逼,formatting,該格式將被上載文件的 date/time替換(so that uploaded files don't fill up the given directory)朗兵。
    ?admin 用一個(gè)<input type="file">部件表示該字段保存的數(shù)據(jù)(一個(gè)文件上傳部件) 。
    ?? 注意:在一個(gè)model中使用FileField或ImageField需要以下步驟:
    ?(1) 在你的settings文件中顶滩,定義一個(gè)完整路徑給MEDIA_ROOT以便讓Django在此處保存上傳文件(出于性能考慮余掖,這些文件并不保存到數(shù)據(jù)庫(kù)),定義MEDIA_URL作為該目錄的公共URL,要確保該目錄對(duì)web服務(wù)器用戶賬號(hào)是可寫的礁鲁。
    ?(2) 在你的model中添加FileField或ImageField盐欺,并確保定義了upload_to選項(xiàng),以告訴Django使用MEDIA_ROOT的哪個(gè)子目錄保存上傳文件仅醇,你的數(shù)據(jù)庫(kù)中要保存的只是文件的路徑(相對(duì)于MEDIA_ROOT)冗美,出于習(xí)慣你一定很想使用Django提供的get_<fieldname>_url函數(shù),舉例來(lái)說(shuō)析二,如果你的ImageField叫做mug_shot粉洼,你就可以在模板中以{{ object.get_mug_shot_url }}這樣的方式得到圖像的絕對(duì)路徑。

  • FilePathField
    ?可選項(xiàng)目為某個(gè)特定目錄下的文件名甲抖。 支持三個(gè)特殊的參數(shù), 其中第一個(gè)是必須提供的。
    參數(shù)?|?描述
    path?|?必需參數(shù)心铃,一個(gè)目錄的絕對(duì)文件系統(tǒng)路徑FilePathField 據(jù)此得到可選項(xiàng)目准谚。例如"/home/images"
    match?|?可選參數(shù),一個(gè)正則表達(dá)式, 作為一個(gè)字符串, FilePathField 將使用它過(guò)濾文件名去扣。注意這個(gè)正則表達(dá)式只會(huì)應(yīng)用到 base filename 而不是路徑全名柱衔。例如"foo.*\.txt^",將匹配文件foo23.txt愉棱,卻不匹配bar.txt或foo23.gif唆铐。
    recursive?|?可選參數(shù),要么True,要么False,默認(rèn)是False,是否包括path下面的全部子目錄奔滑。
    match?|?僅應(yīng)用于 base filename, 而不是路徑全名艾岂。 那么,這個(gè)例子:FilePathField(path="/home/images", match="foo.*", recursive=True)...會(huì)匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif。

  • ImageField
    ?類似 FileField, 不過(guò)要校驗(yàn)上傳對(duì)象是否是一個(gè)合法圖片朋其。它有兩個(gè)可選參數(shù):height_field和width_field,如果提供這兩個(gè)參數(shù),則圖片將按提供的高度和寬度規(guī)格保存王浴。

  • URLField
    ?用于保存 URL, 若 verify_exists 參數(shù)為 True (默認(rèn)), 給定的 URL 會(huì)預(yù)先檢查是否存在( 即URL是否被有效裝入且沒(méi)有返回404響應(yīng))梅猿。
    ?admin 用一個(gè) <input type="text"> 文本框表示該字段保存的數(shù)據(jù)(一個(gè)單行編輯框)氓辣。

  • NullBooleanField
    ?類似 BooleanField, 不過(guò)允許 NULL 作為其中一個(gè)選項(xiàng),推薦使用這個(gè)字段而不要用 BooleanField 加 null=True
    ? admin 用一個(gè)選擇框 <select> (三個(gè)可選擇的值:"Unknown", "Yes" 和 "No" ) 來(lái)表示這種字段數(shù)據(jù)袱蚓。

  • SlugField
    ? "Slug" 是一個(gè)報(bào)紙術(shù)語(yǔ). slug 是某個(gè)東西的小小標(biāo)記(短簽), 只包含字母,數(shù)字,下劃線和連字符钞啸。它們通常用于URLs。
    ? 若你使用 Django 開(kāi)發(fā)版本,你可以指定 maxlength.。若 maxlength 未指定, Django 會(huì)使用默認(rèn)長(zhǎng)度: 50体斩。在以前的 Django 版本,沒(méi)有任何辦法改變50 這個(gè)長(zhǎng)度梭稚。這暗示了 db_index=True。
    ? 它接受一個(gè)額外的參數(shù): prepopulate_from, which is a list of fields from which to auto-#populate the slug, via JavaScript,in the object's admin form: models.SlugField (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受DateTimeFields硕勿。

  • XMLField
    ?一個(gè)校驗(yàn)值是否為合法XML的 TextField,必須提供參數(shù): schema_path, 它是一個(gè)用來(lái)校驗(yàn)文本的 RelaxNG schema 的文件系統(tǒng)路徑哨毁。

  • IPAddressField
    ?一個(gè)字符串形式的 IP 地址, (i.e. "24.124.1.30")

  • CommaSeparatedIntegerField
    ?用于存放逗號(hào)分隔的整數(shù)值,類似 CharField, 必須要有maxlength參數(shù)源武。


詳解
(1) 字符串類(以下在數(shù)據(jù)庫(kù)中本質(zhì)都是字符串?dāng)?shù)據(jù)類型扼褪,此類字段只是在Django自帶的admin中生效)
??models.CharField 對(duì)應(yīng)的是MySQL的varchar數(shù)據(jù)類型
??tips:char 和 varchar的區(qū)別 :
?char和varchar的共同點(diǎn)是存儲(chǔ)數(shù)據(jù)的長(zhǎng)度,不能超過(guò)max_length限制粱栖;
?不同點(diǎn)是varchar根據(jù)數(shù)據(jù)實(shí)際長(zhǎng)度存儲(chǔ)话浇,char按指定max_length( )存儲(chǔ)數(shù)據(jù),所有前者更節(jié)省硬盤空間闹究;

EmailField(CharField):
IPAddressField(Field)
URLField(CharField)
SlugField(CharField)
UUIDField(Field)
FilePathField(Field)
FileField(Field)
ImageField(FileField)
CommaSeparatedIntegerField(CharField)
------- 在模型表中的定義 -------
class Publisher(models.Model):
    id = models.AutoField(primary_key=True)
    publishName = models.CharField(max_length=32)
    address = models.CharField(max_length=32)
    tele = models.CharField(max_length=32)

(2) 時(shí)間字段

models.DateTimeField(null=True)
date=models.DateField()

(3) 數(shù)字字段
(max_digits=30,decimal_places=10) 總長(zhǎng)度30幔崖,小數(shù)位10位

num = models.IntegerField()
num = models.FloatField() 浮點(diǎn)
price=models.DecimalField(max_digits=8,decimal_places=3) 精確浮點(diǎn)

(4) 枚舉字段
?在數(shù)據(jù)庫(kù)存儲(chǔ)枚舉類型,比外鍵有什么優(yōu)勢(shì)渣淤?
?無(wú)需連表查詢性能低赏寇,省硬盤空間(選項(xiàng)不固定時(shí)用外鍵);
?在modle文件里不能動(dòng)態(tài)增加(選項(xiàng)一成不變用Django的choice)

choice=(
        (1,'男人'),
        (2,'女人'),
        (3,'其他')
    )
lover=models.IntegerField(choices=choice) #枚舉類型
# 或者
 gender = models.CharField(verbose_name='性別', max_length=8, choices=(("male", "男"), ("female", "女")),default='female')

(5) 其他字段

db_index = True 表示設(shè)置索引
unique(唯一的意思) = True 設(shè)置唯一索引

------- 聯(lián)合唯一索引 -------
class Meta:
    unique_together = (
          ('email','ctime'),
    )

------- 聯(lián)合索引(不做限制)------
    index_together = (
         ('email','ctime'),
    )

------- 多對(duì)多操作 -------
ManyToManyField(RelatedField)  

(6) 自定義字段

class UnsignedIntegerField(models.IntegerField):
    def db_type(self, connection):
        return 'integer UNSIGNED'

自定義char類型字段:

class FixedCharField(models.Field):
    """
    自定義的char類型的字段類
    """
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs)
 
    def db_type(self, connection):
        """
        限定生成數(shù)據(jù)庫(kù)表的字段類型為char,長(zhǎng)度為max_length指定的值
        """
        return 'char(%s)' % self.max_length
 
 
class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用自定義的char類型的字段
    cname = FixedCharField(max_length=25)

附ORM字段與數(shù)據(jù)庫(kù)實(shí)際字段的對(duì)應(yīng)關(guān)系

'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',

【常用字段參數(shù)】

  • null
    表示某個(gè)字段可以為空价认,如果為True嗅定,Django將用NULL在數(shù)據(jù)庫(kù)中存儲(chǔ)空值,默認(rèn)是False用踩。
  • blank
    如果為True渠退,該字段允許不填,默認(rèn)為False脐彩。
    ??tips:這與null不同碎乃,null純粹是數(shù)據(jù)庫(kù)范疇的,而blank是數(shù)據(jù)驗(yàn)證范疇的惠奸。如果一個(gè)字段的blank=True梅誓,表單的驗(yàn)證將允許該字段是空值,如果字段的blank=False佛南,該字段是必填的
  • default
    字段的默認(rèn)值证九,可以是一個(gè)值或者可調(diào)用對(duì)象,如果可調(diào)用共虑,每有新對(duì)象被創(chuàng)建它都會(huì)被調(diào)用愧怜。
  • primary_key
    如果為True,那么這個(gè)字段就是模型的主鍵妈拌,如果你沒(méi)有指定任何一個(gè)字段的primary_key=True,Django就會(huì)自動(dòng)添加IntegerField字段作為主鍵拥坛,所以除非你想覆蓋默認(rèn)的主鍵行為蓬蝶,否則沒(méi)必要設(shè)置任何一個(gè)字段的primary_key=True。
  • unique
    如果該值設(shè)置為True猜惋,則這個(gè)數(shù)據(jù)字段的值在整張表中必須是唯一的丸氛。
  • required
    如果設(shè)置該值為True,則表示請(qǐng)求不能為空
  • choice
    由二元組組成的一個(gè)可迭代對(duì)象(例如:列表或元組)著摔,用來(lái)給字段提供選擇項(xiàng)缓窜,如果設(shè)置了choices,默認(rèn)的表單將是一個(gè)選擇框而不是標(biāo)準(zhǔn)的文本框谍咆,而且這個(gè)選擇框的選項(xiàng)就是choices中的選項(xiàng)禾锤。
class User(models.Model):
       name=models.CharField(max_length=32)
       password=MyCharField(max_length=32)
       choices=((1,'重點(diǎn)本科'),(2,'普通本科'),(3,'屇〔欤科'),(4恩掷,'其他'))
       education= models.IntegerField(choices=choices)  通過(guò)選擇數(shù)字來(lái)選擇相應(yīng)的屬性

    user_obj.education  拿到的是數(shù)字??
    uer_obj.get_education_display()  固定用法,獲取choice字段對(duì)應(yīng)的注釋??
  • db_index
    如果db_index=True供嚎,則表示為此字段設(shè)置索引黄娘。
  • only與defer
    兩者是相反的models.User.objects.only('name')??不走數(shù)據(jù)庫(kù)

[ DateField 和 DateTimeField中的參數(shù) ]

  • auto_now_add
    配置auto_now_add=True,創(chuàng)建數(shù)據(jù)記錄的時(shí)候會(huì)把當(dāng)前時(shí)間添加到數(shù)據(jù)庫(kù)克滴。
  • auto_now
    配置auto_now=True逼争,每次更新數(shù)據(jù)記錄的時(shí)候會(huì)更新該字段。

【關(guān)系字段及參數(shù)】
??ForeignKey
?外鍵類型在ORM中用來(lái)表示外鍵關(guān)聯(lián)關(guān)系劝赔,一般把ForeignKey字段設(shè)置在‘一對(duì)多中多的一方’誓焦。
?ForeignKey可以和其他表做關(guān)聯(lián)關(guān)系同時(shí)也可以和自身做關(guān)聯(lián)關(guān)系。

  • 字段參數(shù)
    to?設(shè)置要關(guān)聯(lián)的表
    to_field?設(shè)置要關(guān)聯(lián)的表的字段
    related_name?反向操作時(shí)使用的字段名望忆,用于代替原反向查詢時(shí)的'表名_set'

舉個(gè)例子??

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

class Student(models.Model):
    name = models.CharField(max_length=32)
    theclass = models.ForeignKey(to="Classes")

當(dāng)我們要查詢某個(gè)班級(jí)關(guān)聯(lián)的所有學(xué)生(反向查詢)時(shí)罩阵,我們會(huì)這么寫models.Classes.objects.first().student_set.all()竿秆,
當(dāng)我們?cè)贔oreignKey字段中添加了參數(shù) related_name 后

class Student(models.Model):
    name = models.CharField(max_length=32)
    theclass = models.ForeignKey(to="Classes", related_name="students")

當(dāng)我們要查詢某個(gè)班級(jí)關(guān)聯(lián)的所有學(xué)生(反向查詢)時(shí)启摄,我們會(huì)這么寫models.Classes.objects.first().students.all()


?related_query_name?反向查詢操作時(shí),使用的連接前綴幽钢,用于替換表名
?on_delete?當(dāng)刪除關(guān)聯(lián)表中的數(shù)據(jù)時(shí)歉备,當(dāng)前表與其關(guān)聯(lián)的行為,即外鍵的刪除


on_delete屬性:
1匪燕、常見(jiàn)的使用方式(設(shè)置為null) >>>?on_delete=models.SET_NULL
2蕾羊、關(guān)于別的屬性的介紹
?models.CASCADE:?這就是默認(rèn)的選項(xiàng),級(jí)聯(lián)刪除帽驯,你無(wú)需顯性指定它龟再,刪除關(guān)聯(lián)數(shù)據(jù),與之關(guān)聯(lián)也刪除尼变。
?models.PROTECT:?保護(hù)模式利凑,如果采用該選項(xiàng)浆劲,刪除關(guān)聯(lián)數(shù)據(jù)的時(shí)候,會(huì)拋出ProtectedError錯(cuò)誤哀澈。
?models.SET_NULL:?置空模式牌借,刪除關(guān)聯(lián)數(shù)據(jù)的時(shí)候,外鍵字段被設(shè)置為空割按,前提就是blank=True, null=True,定義該字段的時(shí)候膨报,允許為空。
?models.SET_DEFAULT:?置默認(rèn)值适荣,刪除關(guān)聯(lián)數(shù)據(jù)的時(shí)候现柠,外鍵字段設(shè)置為默認(rèn)值,所以定義外鍵的時(shí)候注意加上一個(gè)默認(rèn)值束凑。
?models.DO_NOTHING:?刪除關(guān)聯(lián)數(shù)據(jù)晒旅,引發(fā)錯(cuò)誤IntegrityError。
?models.SET:?刪除關(guān)聯(lián)數(shù)據(jù)
??a.與之關(guān)聯(lián)的值設(shè)置為指定值汪诉,設(shè)置:models.SET(值)
??b.與之關(guān)聯(lián)的值設(shè)置為可執(zhí)行對(duì)象的返回值废恋,設(shè)置:models.SET(可執(zhí)行對(duì)象)

def func():
    return 10

class MyModel(models.Model):
    user = models.ForeignKey(
        to="User",
        to_field="id",
        on_delete=models.SET(func)
    )

?db_constraint?是否在數(shù)據(jù)庫(kù)中創(chuàng)建外鍵約束扒寄,默認(rèn)為True

??OneToOneField
一對(duì)一字段
通常一對(duì)一字段用來(lái)擴(kuò)展已有字段

  • 字段參數(shù)
    ?to?設(shè)置要關(guān)聯(lián)的表
    ?to_field?設(shè)置要關(guān)聯(lián)的字段
    ?on_delete?同F(xiàn)oreign字段

??ManyToManyField
??用于表示多對(duì)多的關(guān)聯(lián)關(guān)系鱼鼓,在數(shù)據(jù)庫(kù)中通過(guò)第三張表來(lái)建立關(guān)聯(lián)關(guān)系。

  • 字段參數(shù)
    ?to?設(shè)置要關(guān)聯(lián)的表
    ?related_name?同F(xiàn)oreignKey字段
    ?related_query_name?同F(xiàn)oreignKey字段
    ?symmetrical?僅用于多對(duì)多自關(guān)聯(lián)時(shí)该编,指定內(nèi)部是否創(chuàng)建反向操作的字段迄本,默認(rèn)為True。
    ??舉個(gè)例子??
class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self")

??此時(shí)person對(duì)象就沒(méi)有person_set屬性

class Person(models.Model):
    name = models.CharField(max_length=16)
    friends = models.ManyToManyField("self", symmetrical=False)

?through?在使用ManyToManyField字段時(shí)课竣,Django將自動(dòng)生成一張表來(lái)管理多對(duì)多的關(guān)聯(lián)關(guān)系嘉赎。但我們也可以手動(dòng)創(chuàng)建第三張表來(lái)管理多對(duì)多關(guān)系,此時(shí)就需要通過(guò)through來(lái)指定第三張表的表名于樟。
?through_fields?設(shè)置關(guān)聯(lián)字段
?db_table?默認(rèn)創(chuàng)建第三張表時(shí)公条,數(shù)據(jù)庫(kù)中表的名稱

??元信息
??ORM對(duì)應(yīng)的類里面包含另一個(gè)Meta類,而Meta類封裝了一些數(shù)據(jù)庫(kù)的信息迂曲。主要字段如下:

  • db_table?ORM在數(shù)據(jù)庫(kù)中的表名默認(rèn)是app_類名靶橱,可以通過(guò)db_table重寫表名
  • index_together?聯(lián)合索引
  • ordering?指定默認(rèn)按什么字段排序。只有設(shè)置了該屬性路捧,我們查詢到的結(jié)果才可以被reverse()

三关霸、單表操作

1、前言

??orm使用方式:orm操作可以使用類實(shí)例化杰扫,obj.save的方式队寇,也可以使用create( )的形式。
QuerySet數(shù)據(jù)類型介紹
???只要是queryset對(duì)象就可以無(wú)限的點(diǎn)queryset方法章姓,比如```models.User.object.filter( ).filter( ).count( )
?QuerySet與惰性機(jī)制
?所謂惰性機(jī)制:publish.obj,all( )或者.filter( )等都只是返回了一個(gè)QuerySet(查詢結(jié)果集對(duì)象)佳遣,它并不會(huì)馬上執(zhí)行sql炭序,而是當(dāng)調(diào)用QuerySet的時(shí)候才執(zhí)行。
?QuerySet特點(diǎn):可迭代的苍日;可切片惭聂;惰性計(jì)算和緩存機(jī)制

def queryset(request):
    books=models.Book.objects.all()[:10]  #切片 應(yīng)用分頁(yè)
    books = models.Book.objects.all()[::2]
    book= models.Book.objects.all()[6]    #索引
    print(book.title)
    for obj in books:  #可迭代
        print(obj.title)
    books=models.Book.objects.all().  #惰性計(jì)算--->等于一個(gè)生成器,不應(yīng)用books不會(huì)執(zhí)行任何SQL操作
    # query_set緩存機(jī)制1次數(shù)據(jù)庫(kù)查詢結(jié)果query_set都會(huì)對(duì)應(yīng)一塊緩存相恃,再次使用該query_set時(shí)辜纲,不會(huì)發(fā)生新的SQL操作;
    #這樣減小了頻繁操作數(shù)據(jù)庫(kù)給數(shù)據(jù)庫(kù)帶來(lái)的壓力;
    authors=models.Author.objects.all()
    for author in  authors:
        print(author.name)
    print('-------------------------------------')
    models.Author.objects.filter(id=1).update(name='陳某')
    for author in  authors:
        print(author.name)
    #但是有時(shí)候取出來(lái)的數(shù)據(jù)量太大會(huì)撐爆緩存拦耐,可以使用迭代器優(yōu)雅得解決這個(gè)問(wèn)題耕腾;
    models.Publish.objects.all().iterator()
    return HttpResponse('OK')

拿出前面準(zhǔn)備好的表:

from django.db import models

class Book(models.Model):
     id=models.AutoField(primary_key=True)
     title=models.CharField(max_length=32)
     state=models.BooleanField()
     pub_date=models.DateField()
     price=models.DecimalField(max_digits=8,decimal_places=2)
     publish=models.CharField(max_length=32)

?? 接下來(lái)使用單獨(dú)的py文件測(cè)試ORM操作,在test.py中需要配置的參數(shù)如下:

import os
import sys

if __name__ == "__main__":
      os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test.settings")
      import django
      django.setup()
      from app import models  ??這句話必須在下面

2杀糯、添加表記錄(新增數(shù)據(jù)的三種方式)

  • 方法一
    基于create創(chuàng)建(create)
    表.objects.create()
??create方法的返回值book_obj就是插入book表中python葵花寶典這本書(shū)籍記錄對(duì)象
book_obj = Book.objects.create(title="python葵花寶典",state=True,price=100,publish="蘋果出版社"扫俺,pub_date="2019-12-12")
  • 方法二
    基于對(duì)象的綁定方法創(chuàng)建(save)也就是類實(shí)例化
    obj=類(屬性=xx)?obj.save()
book_obj = Book(title="python葵花寶典",state=True,price=100,publish="蘋果出版社",pub_date="2012-12-12")
book_obj.save()
  • 方法三
    自動(dòng)識(shí)別時(shí)間格式字符串進(jìn)行傳參
from datetime import datetime 
ctime = datetime.now()
models.Book.objects.create(title="python葵花寶典",state=True,price=100,publish="蘋果出版社",pub_date=ctime)

3、查詢表記錄

  • 基本查詢
查詢API 作用
all( ) 查詢所有結(jié)果
filter(**kwargs) 包含了與所給篩選條件相匹配的對(duì)象
get(**kwargs) 返回與所給篩選條件相匹配的對(duì)象固翰,返回結(jié)果有且只有一個(gè)狼纬,如果符合篩選條件的對(duì)象超過(guò)一個(gè)或者沒(méi)有都會(huì)拋出錯(cuò)誤
exclude(**kwargs) 包含了與所給篩選條件不匹配的對(duì)象
order_by(*field) 對(duì)查詢結(jié)果排序
reverse( ) 對(duì)查詢結(jié)果反向排序,前面要先有排序才能反向
count( ) 返回?cái)?shù)據(jù)庫(kù)中匹配查詢(QuerySet)的對(duì)象數(shù)量
first( ) 返回第一條記錄
last( ) 返回最后一條記錄
exists( ) 如果QuerySet包含數(shù)據(jù)骂际,就返回True疗琉,否則返回False
values(*field) 返回一個(gè)ValueQuerySet—一個(gè)特殊的QuerySet,運(yùn)行后得到的并不是一系列model的實(shí)例化對(duì)象歉铝,而是一個(gè)可迭代的字典序列
values_list(*field) 它與values( )非常相似盈简,它返回的是一個(gè)元組序列,values返回的是一個(gè)字典序列
distinct( ) 從返回結(jié)果中剔除重復(fù)記錄,去重的對(duì)象必須是完全相同的數(shù)據(jù)才能去重

例子們??:

?? filter(**kwargs):
      models.User.objects.filter(name='wpr',age=18)  ——查出來(lái)是<QuerySet []>
      models.User.objects.filter(name='wpr',age=23)  ——查出來(lái)是<QuerySet [<User: User object>, <User: User object>]>
  ????:filter里面可以放多個(gè)限制條件太示,但多個(gè)條件之間是and

?? exclude(**kwargs):
      models.User.objects.exclude(name='wpr')

?? order_by(*field):
      models.User.objects.order_by('-age')  ——可以在排序的字段前面加一個(gè)減號(hào)柠贤,就是降序
      models.User.objects.order_by('age')  ——默認(rèn)是升序

?? reverse():  
      models.User.objects.order_by('age').reverse()  ——要先有排序才能反向

?? count():
      models.User.objects.count()
      models.User.objects.all().count()

?? first():
      models.User.objects.all().first()
      models.User.objects.all()[0]  ——不支持負(fù)數(shù)的索引取值

?? last():
      models.User.objects.all().last()

?? exists():
      models.User.objects.all().exists()
      models.User.objects.filter(name='wpr',age=3).exists()

?? values(*field):
      models.User.objects.values('name')  ——??列表套字典[{},{},{}]

?? values_list(*field):
      models.User.objects.value_list('name','age')  ——??列表套元組[(),(),()]

?? distinct():
      models.User.objects.values('name','age').distinct()  ——??去重的對(duì)象必須是完全相同的數(shù)據(jù)才能去重
      必須完全一樣才可以去重(意味著帶了id就沒(méi)有意義了)
      models.Book.objects.all().values('name').distinct()  ——先查一個(gè)重復(fù)的值再去重
  • 基于雙下劃線的模糊查詢
------- 數(shù)字查詢 -------
查詢價(jià)格為100或200或300的書(shū)籍  models.Book.objects.filter(price__in=[100,200,300])
查詢年齡大于44歲的用戶   models.User.objects.filter(age__gt=44)
查詢價(jià)格小于100的書(shū)籍   models.Book.objects.filter(price__lt=100)
查詢年齡大于等于44的用戶   models.User.objects.filter(age__gte=44)
查詢年齡小于等于44的用戶   models.User.objects.filter(age__lte=44)
查詢價(jià)格再100到200之間的書(shū)籍   models.Book.objects.filter(price__range=[100,200])
查詢年齡是否為空(0:is not null | 1:is null)   models.User.objects.filter(age__isnull=0|1)

------- 字符串查詢 -------
查詢名字中包含字母n的用戶   models.User.objects.filter(name__contains='n')
查詢名字中包含字母n的用戶并且忽略大小寫   models.User.objects.filter(name__icontains='n')
查詢書(shū)籍中以py開(kāi)頭的書(shū)籍   models.Book.objects.filter(title__startswith='py')
查詢書(shū)籍中以n結(jié)尾的用戶   models.User.objects.filter(name__endswith='n')
查詢名字滿足某個(gè)正則表達(dá)式的用戶   models.User.objects.filter(name__regex='正則表達(dá)式')

------- 日期 -------
查詢注冊(cè)時(shí)間是在2017年的用戶   models.User.objects.filter(register_time__year=2017)

?? tips:返回queryset對(duì)象的方法
??all ( )
??filter ( )
??exclude ( )
??order_by ( )
??reverse ( )
??distinct ( )
??values ( )?返回一個(gè)可迭代的字典序列
??values_list ( )?返回一個(gè)可迭代的元祖序列

4、刪除表記錄

  • 基于對(duì)象
user_obj = models.User.objects.filter(name='json').first()
user_obj.delete()

??刪除方法就是delete(),它運(yùn)行時(shí)立即刪除對(duì)象而不返回任何值类缤,例如user_obj.delete()臼勉。

  • 基于queryset
models.User.objects.filter(name='wpr').delete()

??一次性刪除多個(gè)對(duì)象,每個(gè)QuerySet都有一個(gè)delete()方法呀非,它一次性刪除QuerySet中所有的對(duì)象坚俗。例如镜盯,下面的代碼將刪除pub_date是2005年的Entry對(duì)象:
Entry.objects.filter(pub_date__year=2005).delete()
在Django刪除對(duì)象時(shí)岸裙,會(huì)模仿SQL約束ON DELETE CASCADE的行為,換句話說(shuō)速缆,刪除一個(gè)對(duì)象時(shí)也會(huì)刪除與他相關(guān)聯(lián)的外鍵對(duì)象降允,例如:

b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()

要注意的是:delete()方法是QuerySet上的方法,但并不適用于Manager本身艺糜。這是一種保護(hù)機(jī)制剧董,是為了避免意外地調(diào)用Entry.objects.delete()方法導(dǎo)致所有的記錄被誤刪除幢尚。如果你確認(rèn)要?jiǎng)h除所有的對(duì)象,那么你必須顯式地調(diào)用Entry.objects.all().delete()
如果不想級(jí)聯(lián)刪除翅楼,可以設(shè)置為:

pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)

5尉剩、修改表記錄

  • 基于對(duì)象,先獲取用戶對(duì)象毅臊,然后賦值理茎,最后保存
user_obj = models.User.objects.filter(name='wpr').first()
user_obj.age = 17
user_obj.save()
  • 基于queryset,queryset直接更新
    ??update()方法對(duì)于任何結(jié)果集(QuerySet)均有效,這意味著你可以同時(shí)更新多條記錄管嬉,update()方法會(huì)返回一個(gè)整型值皂林,表示受影響的記錄的條數(shù)。
 models.User.objects.filter(name='wpr').update(age=77)

三蚯撩、多表操作

1础倍、Django多表ORM設(shè)計(jì)規(guī)則

(1)關(guān)聯(lián)的表之間建議建立外鍵,但可以取消關(guān)聯(lián)關(guān)系(db_constraint=False)
(2)關(guān)聯(lián)表之間的外鍵字段建議采用對(duì)應(yīng)類名的全小寫
(3) 采用關(guān)聯(lián)表的主鍵或?qū)ο缶苓M(jìn)行操作

2胎挎、表與表之間的關(guān)系

  • 一對(duì)一(OneToOneField):一對(duì)一字段無(wú)論建在哪張關(guān)系表里都可以沟启,但是推薦建在查詢頻率比較高的那張表中

  • 一對(duì)多(ForeignKey):一對(duì)多字段建在多的那一方

  • 多對(duì)多(ManyToManyField):多對(duì)多字段無(wú)論建在哪張關(guān)系表里都可以,但是推薦建在查詢頻率比較高的那張表中
    多對(duì)多:add() 添加??|??set() 修改??|??remove() 不能接收可迭代對(duì)象??|??clear() 清空犹菇,不用傳參

  • 外鍵關(guān)系

publish = models.ForeignKey(to='Publish',to_field='title')

to?是設(shè)置要關(guān)聯(lián)的表
to_field?是設(shè)置要關(guān)聯(lián)的表的字段美浦,不設(shè)置默認(rèn)關(guān)聯(lián)id
on_delete?是當(dāng)刪除關(guān)聯(lián)表中的數(shù)據(jù)時(shí),當(dāng)前表與其關(guān)聯(lián)的行的行為项栏。在django2.0中要加models.CASCADE
db_constraint?是否在數(shù)據(jù)庫(kù)中創(chuàng)建外鍵約束浦辨,默認(rèn)為True

3、創(chuàng)建模型

【作者Author】:一個(gè)作者有姓名沼沈,作者詳情和作者之間是一對(duì)一關(guān)系(one-to-one)
【作者詳情AuthorDetail】:包含年齡流酬、電話和信息
【出版社Publish】:出版商有名稱,地址
【書(shū)籍Book】:書(shū)籍有書(shū)名列另、價(jià)格和出版日期芽腾,一本書(shū)可能會(huì)有多個(gè)作者,一個(gè)作者也可以寫多本書(shū)页衙,所以作者和書(shū)籍的關(guān)系就是多對(duì)多的關(guān)聯(lián)關(guān)系(many-to-many)摊滔;一本書(shū)只應(yīng)該由一個(gè)出版商出版,所以出版商和書(shū)籍是一對(duì)多關(guān)聯(lián)關(guān)系(one-to-many)

from django.db import models

class Author(models.Model):
    name=models.CharField( max_length=32)
    # 與AuthorDetail建立一對(duì)一的關(guān)系
    author_detail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)

class AuthorDetail(models.Model):
    age=models.IntegerField()
    telephone=models.BigIntegerField()
    info=models.CharField( max_length=64)

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

class Book(models.Model):
    name = models.CharField( max_length=32)
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publish_date=models.DateField()    
    # 與Publish建立一對(duì)多的關(guān)系,外鍵字段建立在多的一方
    publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
    # 與Author表建立多對(duì)多的關(guān)系,ManyToManyField可以建在兩個(gè)模型中的任意一個(gè)店乐,自動(dòng)創(chuàng)建第三張表
    authors=models.ManyToManyField(to='Author',)

4艰躺、一對(duì)多關(guān)系

  • 規(guī)則
    (1)關(guān)系中“多”依賴于“一”;
    (2)Django 1.x外鍵關(guān)聯(lián)默認(rèn)有級(jí)聯(lián)刪除眨八,2.x需要手動(dòng)明確外鍵的級(jí)聯(lián)刪除(on_delete=models.CASCADE)

① 增

??方式一:外鍵為關(guān)聯(lián)對(duì)象,這里外鍵是實(shí)際表的字段
先有出版社离钝,才有書(shū)籍
publish = Publish.objects.create(name="南方出版社",address="上海)
Book.objects.create(name='人間失格',price=88.88,publish_date='2018-8-8',publish=publish)

?? 方式二:外鍵字段為關(guān)聯(lián)對(duì)象主鍵彭沼,這里外鍵不是實(shí)際表的字段
id = Publish.objects.create(name="北方出版社", address="北京").id
Book.objects.create(name='活著', price=37.70, publish_date='2043-8-8', publish_id=id)

② 刪

刪除出版社章贞,默認(rèn)有級(jí)聯(lián)刪除,出版社出版的數(shù)據(jù)全會(huì)被刪除
Publish.objects.first().delete()

③ 改

??方式一:save()
book_obj=models.Book.objects.filter(pk=1).first()
book_obj.publish=new_publish_obj
book_obj.save()

??方式二:update()
models.Book.objects.filter(pk=1).update(publish_id=2)
或 models.Book.objects.filter(pk=1).update(publish_id=publish_obj)

5篓足、一對(duì)一關(guān)系

  • 規(guī)則
    通過(guò)外鍵所在表決定依賴關(guān)系

① 增

??遵循操作順序
author_detail = AuthorDetail.objects.create(age=18,telephone=1232331231,ingo="真帥)
Author.objects.create(name='wpr',author_detail=author_detail)

② 刪

??擁有級(jí)聯(lián)刪除
AuthorDetail.objects.first().delete()

③ 改
??一般不考慮該關(guān)聯(lián)字段

6、多對(duì)多關(guān)系

  • 規(guī)則
    (1) 多對(duì)多關(guān)系存在關(guān)系表闰蚕,關(guān)系表建議采用ManyToManyField字段處理
    (2) 需要手動(dòng)創(chuàng)建關(guān)系表時(shí)栈拖,在字段中明確through與through_field值

① 增 add( )

為書(shū)籍添加作者的主鍵或?qū)ο髠?book.author.add(*args)

② 刪 remove( )

刪除書(shū)籍已有作者的主鍵或?qū)ο髠?book.author.remove(*args)

③ 改 set( ) | 清空clear( )

清空并添加作者的主鍵或?qū)ο?設(shè)置作者的主鍵或?qū)ο笮问降牧斜?book.authoe.clear()
book.author.set([*args])

??tips:add(),set()没陡,remove()都可以支持傳多個(gè)數(shù)字或?qū)ο笕杩瑂et必須接收一個(gè)可迭代對(duì)象

7、跨表查詢規(guī)則

  • 正向反向的概念
    關(guān)聯(lián)字段在你當(dāng)前這張表查詢另一張表叫正向
    關(guān)聯(lián)字段不在你當(dāng)前這張表查詢另一張表叫反向

  • 正向查詢按字段名進(jìn)行跨表查詢

  • 反向查詢按表名小寫進(jìn)行跨表查詢

8诗鸭、基于對(duì)象的跨表查詢(子查詢)

??在跨表查詢的規(guī)則上染簇,跨表查詢的結(jié)果為多條數(shù)據(jù)時(shí)需要在字段后添加_set;還有如果返回的是None,只要加個(gè)all( )强岸。

  • 一對(duì)一查詢
    (1) 查詢得到作者對(duì)象 author_obj=Author.object.first()
    (2) 基于作者對(duì)象跨表獲取作者詳情對(duì)象锻弓,正向通過(guò)字段名author_detail author_detail_obj=author_obj.author_detail
    (3) 基于作者詳情對(duì)象跨表獲取作者對(duì)象,反向通過(guò)表名小寫author author_obj=author_detail_obj.author

  • 一對(duì)多查詢
    (1) 查詢得到書(shū)籍對(duì)象 book_obj=Book.objects.first()
    (2) 獲取出版社對(duì)象蝌箍,正向通過(guò)字段名publish publish_obj=book_obj.publish
    (3) 獲取書(shū)籍對(duì)象們青灼,反向通過(guò)表名小寫book,多條數(shù)據(jù)添加_set,如果返回的結(jié)果是None,加個(gè)all() book_list_obj=publish_obj.book_set.all()

  • 多對(duì)多查詢
    (1) 查詢得到書(shū)籍對(duì)象 book_obj=Book.objects.first()
    (2) 獲取作者對(duì)象們,正向通過(guò)字段名author author_list_obj=book_obj.author
    (3) 基于作者對(duì)象獲取書(shū)籍對(duì)象們妓盲,反向通過(guò)表名小寫book杂拨,多條數(shù)據(jù)添加_set

author = Author.objects.first()
book_list_obj=author.book_set

????tips:如果在ForeignKey()和ManyToManyField的定義中設(shè)置related_name的值來(lái)復(fù)寫FOO_set的名稱。
例如:在Article model中做一下更改

publish = ForeignKey(Book,related_name='bookList')

那么在“查詢?nèi)嗣癯霭嫔绯霭孢^(guò)的所有書(shū)籍”時(shí)結(jié)果如下:

publish=Publish.objects.get(name="人民出版社")
book_list=publish.bookList.all()  ??與人民出版社關(guān)聯(lián)的所有書(shū)籍對(duì)象集合悯衬,這里使用all代替了_set
  • 多級(jí)跨表查詢
某作者出版的第一本書(shū)的出版社名字
author.book_set.first().publish.name

9弹沽、基于雙下劃線的跨表查詢(聯(lián)表查詢)

??Django還提供了一種直觀而高效的方式在查詢(lookups)中表示關(guān)聯(lián)關(guān)系,他能自動(dòng)確認(rèn)SQL JOIN聯(lián)系筋粗。要做跨關(guān)系查詢策橘,就使用兩個(gè)下劃線來(lái)連接模型(model)間關(guān)聯(lián)字段的名稱,直到最終連接到想要的model為止娜亿。
??正向查詢按字段丽已,反向查詢按表名小寫用來(lái)告訴ORM引擎join哪張表。
??滿足跨表查詢規(guī)則买决。
??filter方法與values方法支持__查詢規(guī)則沛婴。

  • 一對(duì)多查詢
    (1) 正向查詢,按字段名:publish
queryResult=Book.objects.filter(publish__name="南方出版社").values_list("title","price")

??(2)反向查詢督赤,按表名小寫:book

queryResult=Publish.objects.filter(name="北方出版社").values_list("book__title","book__price")
  • 多對(duì)多查詢
    (1) 正向查詢嘁灯,按字段名:authors
queryResult=Book.objects.filter(authors__name="wpr").values_list("title)

??(2)反向查詢,按表名小寫:book

queryResult=Author.objects.filter(name="wpr").values_list("book_list","book_price")
  • 一對(duì)一查詢
    (1) 正向查詢
Author.objects.filter(name="wpr").values("author detail__telephone")

??(2) 反向查詢

AuthorDetail.objects.filter(author__name="wpr").values("telephone")
  • 兩表關(guān)聯(lián)
    查詢所有小于18歲作者的名字與實(shí)際年齡
authors_dic=Author.objects.filter(author_detail__id__gt=0).values('name','author_detail__age')
  • 多表關(guān)聯(lián)(連續(xù)跨表)
    查詢出版社在上海的出版過(guò)的所有書(shū)的作者姓名够挂、作者電話旁仿、具體出版社名的相關(guān)信息
info_dic=Book.objects.filter(publish__address__contains="上海").value('author__name','author__author_detail__telephone','publish__name')

queryset對(duì)象.query能夠查看內(nèi)部對(duì)應(yīng)的sql語(yǔ)句
??all()
??filter()
??values()
??value_list()
??order_by()
??reverse()
??distinct()
??exclude()?取反
??count()?計(jì)數(shù)
??exists()?布爾值
??get()?數(shù)據(jù)對(duì)象本身
??first()
??last()

?? related_name
反向查詢時(shí)藕夫,如果定義了related_name孽糖,則用related_name替換表名枯冈,例如 publish=ForeignKey(Blog,related_name='bookList')
那么在“查詢?nèi)嗣癯霭嫔绯霭孢^(guò)的所有書(shū)籍的名字與價(jià)格(一對(duì)多)”,反向查詢办悟,不再按表名:book,而是related_name:bookList

queryResult=Publish.objects.filter(name="人民出版社").values_list("boolList__title","bookList__price")

??tips:配置文件配置參數(shù)查看所有orm操作內(nèi)部的sql語(yǔ)句

    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'console':{
                'level':'DEBUG',
                'class':'logging.StreamHandler',
            },
        },
        'loggers': {
            'django.db.backends': {
                'handlers': ['console'],
                'propagate': True,
                'level':'DEBUG',
            },
        }
    }

10尘奏、聚合查詢與分組查詢

(1)聚合(aggregate)
??from django.db.models import Max,Min,Sum,Count,Avg
??aggregate(*args,**kwargs)
舉個(gè)例子??:計(jì)算所有圖書(shū)的平均價(jià)格

from django.db.models import Avg
Book.objects.all().aggregate(Avg('price'))
>>> {'price__avg': 34.35}  # 自動(dòng)生成

aggregate()是QuerySet的一個(gè)終止子句,意思是說(shuō)病蛉,它返回一個(gè)包含一些鍵值對(duì)的字典炫加。鍵的名字是聚合值的標(biāo)識(shí)符,值是計(jì)算出來(lái)的聚合值铺然。鍵的名字是按照字段和聚合函數(shù)的名稱自動(dòng)生成出來(lái)的俗孝。如果想要為聚合值指定一個(gè)名稱,可以向聚合子句提供它魄健。

Book.objects.aggregate(average_price=Avg('price'))
>>> {'average_price':34.35}

如果想生成不止一個(gè)聚合赋铝,你可以想aggregate()子句中添加另一個(gè)參數(shù),所以如果想知道所有圖書(shū)價(jià)格的最大值和最小值沽瘦,可以這樣查詢:

from django.db.models import Avg, Max, Min
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
>>> {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

(2)分組(group by)
??annotate():依據(jù)前面的基表
??group_concat():特地用在分組
??concat():所有分組
??tips:字符串拼接要用到concat和value

  • 單表分組查詢

emp表:

id name age salary depends
1 wpr 27 1000 銷售部
2 wpp 30 3000 人事部
3 www all( ) 5000 人事部

查詢每一個(gè)部門名稱以及對(duì)應(yīng)的員工數(shù)
sql語(yǔ)句:select dep,Count(*) from emp group by dep;
ORM語(yǔ)句:emp.objects.values("dep").annotate(c=Count("id")

  • 多表分組查詢

emp表:

id name age salary dep_id
1 wpr 27 1000 1
2 wpp 30 3000 2
3 www all( ) 5000 2

dep表:

id name
1 銷售部
2 人事部

emp-dep表:

id name age salary dep_id id name
1 wpr 27 1000 1 1 銷售部
2 wpp 30 3000 2 2 人事部
3 www all( ) 5000 2 2 人事部

查詢每一個(gè)部門名稱以及對(duì)應(yīng)的員工數(shù)
sql語(yǔ)句:select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id
ORM語(yǔ)句dep.objetcs.values("id").annotate(c=Count("emp")).values("name","c")

class Emp(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    salary=models.DecimalField(max_digits=8,decimal_places=2)
    dep=models.CharField(max_length=32)
    province=models.CharField(max_length=32)

??annotate()為調(diào)用的QuerySet中每一個(gè)對(duì)象都生成一個(gè)獨(dú)立的統(tǒng)計(jì)值(統(tǒng)計(jì)方法用聚合函數(shù))革骨。
總結(jié):跨表分組查詢本質(zhì)就是將關(guān)聯(lián)表join成一張表,再按單表的思路進(jìn)行分組查詢析恋。

11良哲、F查詢與Q查詢

from django.db.models import F,Q
(1)F查詢
??對(duì)兩個(gè)字段的值做比較,Django提供F()來(lái)做這樣的比較助隧。F()的實(shí)例可以在查詢中引用字段筑凫,來(lái)比較同一個(gè)model實(shí)例中兩個(gè)不同字段的值。
??F可以取到表中某個(gè)字段對(duì)應(yīng)的值來(lái)當(dāng)作我的篩選條件并村,而不是自定義常量的條件漏健,實(shí)現(xiàn)了動(dòng)態(tài)比較的效果。
??Django支持F()對(duì)象之間以及F()對(duì)象和常數(shù)之間的加減乘除和取模的操作橘霎。

?? 比較兩個(gè)不同字段的值
  查詢?cè)u(píng)論數(shù)大于收藏?cái)?shù)的書(shū)籍
  from django.db.models import F
  Book.objects.filter(commnetNum__lt=F('keepNum'))
?? 取到某個(gè)字段的值當(dāng)作篩選條件
字符串拼接要用到  concat和value
將所有商品的名字后面加一個(gè)爆款后綴
from django.db.models.functions import Concat
from django.db.models import Value
models.Product.objects.update(name=Concat(F('name'),Value('爆款')))
?? 通過(guò)字段名獲取可以直接做運(yùn)算的查詢結(jié)果
from Django.db.models import F
案例一:將id為1的結(jié)果年齡增加1
models.User.objects.filter(id=1).update(age=F('age')+1)
案例二:查詢id是年齡1/4的結(jié)果
models.User.objects.filter(id=F('age')/4)

(2)Q查詢

  • filter()等方法中的關(guān)鍵字參數(shù)查詢都是一起進(jìn)行'AND'與的關(guān)系蔫浆,如果要執(zhí)行更復(fù)雜的查詢(例如or或者not)的關(guān)系查詢數(shù)據(jù),可以使用Q對(duì)象姐叁。
?? 默認(rèn)是and關(guān)系
models.Product.objects.filter(Q(name='變形金剛'),Q(price=999.999))
?? 與普通過(guò)濾條件混合使用瓦盛,完成邏輯運(yùn)算方式的查詢
from Django.db.models import Q
與[&]   models.User.objects.filter(Q(id=1)&Q(age=10))
或[|]   models.User.objects.filter(Q(id=1)|Q(id=2))
非[~]   models.User.objects.filter(~Q(id=1))
當(dāng)一個(gè)操作符在兩個(gè)Q對(duì)象上使用時(shí),它產(chǎn)生一個(gè)新的Q對(duì)象
bookList=Book.objects.filter(Q(authors__name="wpr")|Q(authors__name="wpp"))
等同于下面的SQL WHERE子句
where name="wpr" OR name="wpp"
同時(shí)外潜,Q 對(duì)象可以使用~ 操作符取反原环,這允許組合正常的查詢和取反(NOT) 查詢:
bookList=Book.objects.filter(Q(authors__name="wpr") & ~Q(publishDate__year=2017)).values_list("title")
  • 查詢函數(shù)可以混合使用Q對(duì)象和關(guān)鍵字參數(shù),所有提供給查詢函數(shù)的參數(shù)(關(guān)鍵字參數(shù)或Q對(duì)象)都將'AND'在一起处窥,但是嘱吗,如果出現(xiàn)Q對(duì)象,它必須位于所有關(guān)鍵字參數(shù)的前面,例如:
bookList=Book.objects.filter(Q(publishDate__year=2019) | Q(publishDate__year=2020),title__icontains="python" )
  • Q查詢進(jìn)階操作——可以傳入字符串
先實(shí)例化一個(gè)Q對(duì)象
q=Q()
q.connector='or'
q.children.append(('name','wpr'))

Q對(duì)象支持直接放在filter括號(hào)內(nèi)
models.User.objects.filter(q)   q對(duì)象默認(rèn)也是and關(guān)系

12谒麦、事務(wù)(上下文管理)

??定義:將多個(gè)sql語(yǔ)句操作變成原子性操作俄讹,要么同時(shí)成功,有一個(gè)失敗則里面回滾到原來(lái)的狀態(tài)绕德,保證數(shù)據(jù)的完整性和一致性(NoSQL數(shù)據(jù)庫(kù)對(duì)于事務(wù)則是部分支持)
??事務(wù)的ACID:A(原子性)?C(一致性)?I(隔離性)?D(持久性)

from django.db import transaction
with transaction.atomic():
        這里寫多個(gè)數(shù)據(jù)庫(kù)操作
    print('其他邏輯代碼')

13患膛、淺談ORM查詢性能

  • 普通查詢
obj_list=models.Love.objects.all() 
    for row in obj_list:   #for循環(huán)10次發(fā)送10次數(shù)據(jù)庫(kù)查詢請(qǐng)求
        print(row.b.name)

這種查詢方式第一次發(fā)送查詢請(qǐng)求,每for循環(huán)一次也會(huì)發(fā)送查詢請(qǐng)求耻蛇,頻繁進(jìn)行IO操作踪蹬。

  • select_related:結(jié)果為對(duì)象,注意query_set類型的對(duì)象都有該方法
    原理:查詢時(shí)主動(dòng)完成連表形成一張大表臣咖,for循環(huán)時(shí)不用額外發(fā)請(qǐng)求跃捣。
    應(yīng)用場(chǎng)景:節(jié)省硬盤空間,數(shù)據(jù)量少的時(shí)候使用夺蛇,相當(dāng)于只做了一次數(shù)據(jù)庫(kù)查詢
obj_list=models.Love.objects.all().select_related('b')
    for row in obj_list:
        print(row.b.name)
  • prefetch_related:結(jié)果都是對(duì)象
    原理:select_related雖然好枝缔,但是做連表操作依然會(huì)影響查詢性能,所以出現(xiàn)了prefetch_related蚊惯,prefetch_related不做連表查詢愿卸,多次單表查詢外鍵表,去重之后顯示截型,2次單表查詢(有幾個(gè)外鍵做1+N次單表查詢)趴荸。
    應(yīng)用場(chǎng)景:效率高,數(shù)據(jù)量大的時(shí)候適用
obj_list=models.Love.objects.all().prefetch_related('b')
    for obj in obj_list:
        print(obj.b.name)
  • update()和對(duì)象.save()修改方式的性能比較
    update():
models.Book.objects.filter(id=1).update(price=3)

??執(zhí)行結(jié)果:

(0.000) BEGIN; args=None
(0.000) UPDATE "app01_book" SET "price" = '3.000' WHERE "app01_book"."id" = 1; args=('3.000', 1)
(0.000) SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price", "app01_book"."date", "app01_book"."publish_id", "app01_book"."classify_id" FROM "app01_book" WHERE "app01_book"."id" = 1; args=(1,)

??save():

book_obj=models.Book.objects.get(id=1)
book_obj.price=5
book_obj.save()

??執(zhí)行結(jié)果:

(0.000) BEGIN; args=None
(0.000) UPDATE "app01_book" SET "title" = '人間失格', "price" = '5.000', "date" = '1370-09-09', "publish_id" = 4, "classify_id" = 3 WHERE "app01_book"."id" = 1; args=('人間失格', '5.000', '1370-09-09', 4, 3, 1)

總結(jié):update()比obj.save()性能好宦焦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末发钝,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子波闹,更是在濱河造成了極大的恐慌酝豪,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件精堕,死亡現(xiàn)場(chǎng)離奇詭異孵淘,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)歹篓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門瘫证,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人庄撮,你說(shuō)我怎么就攤上這事背捌。” “怎么了洞斯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵毡庆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)么抗,這世上最難降的妖魔是什么毅否? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮乖坠,結(jié)果婚禮上搀突,老公的妹妹穿的比我還像新娘刀闷。我一直安慰自己熊泵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布甸昏。 她就那樣靜靜地躺著顽分,像睡著了一般。 火紅的嫁衣襯著肌膚如雪施蜜。 梳的紋絲不亂的頭發(fā)上卒蘸,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音翻默,去河邊找鬼缸沃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛修械,可吹牛的內(nèi)容都是我干的趾牧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肯污,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼翘单!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蹦渣,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤哄芜,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后柬唯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體认臊,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年锄奢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了美尸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡斟薇,死狀恐怖师坎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情堪滨,我是刑警寧澤胯陋,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響遏乔,放射性物質(zhì)發(fā)生泄漏义矛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一盟萨、第九天 我趴在偏房一處隱蔽的房頂上張望凉翻。 院中可真熱鬧,春花似錦捻激、人聲如沸制轰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)垃杖。三九已至,卻和暖如春丈屹,著一層夾襖步出監(jiān)牢的瞬間调俘,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工旺垒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彩库,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓先蒋,卻偏偏與公主長(zhǎng)得像骇钦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鞭达,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355