django 2 ORM操做 ORM進階 cookie和session 中間件

ORM操做

ORM概念 

對象關係映射(Object Relational Mapping,簡稱ORM)模式是一種爲了解決面向對象與關係數據庫存在的互不匹配的現象的技術。html

簡單的說,ORM是經過使用描述對象和數據庫之間映射的元數據,將程序中的對象自動持久化到關係數據庫中。前端

ORM在業務邏輯層和數據庫層之間充當了橋樑的做用。python

ORM由來

讓咱們從O/R開始。字母O起源於"對象"(Object),而R則來自於"關係"(Relational)。mysql

幾乎全部的軟件開發過程當中都會涉及到對象和關係數據庫。在用戶層面和業務邏輯層面,咱們是面向對象的。當對象的信息發生變化的時候,咱們就須要把對象的信息保存在關係數據庫中。git

按照以前的方式來進行開發就會出現程序員會在本身的業務邏輯代碼中夾雜不少SQL語句用來增長、讀取、修改、刪除相關數據,而這些代碼一般都是極其類似或者重複的。程序員

ORM的優點

ORM解決的主要問題是對象和關係的映射。它一般將一個類和一張表一一對應,類的每一個實例對應表中的一條記錄,類的每一個屬性對應表中的每一個字段。 redis

ORM提供了對數據庫的映射,不用直接編寫SQL代碼,只需操做對象就能對數據庫操做數據。sql

讓軟件開發人員專一於業務邏輯的處理,提升了開發效率。數據庫

ORM的劣勢

ORM的缺點是會在必定程度上犧牲程序的執行效率。django

ORM的操做是有限的,也就是ORM定義好的操做是能夠完成的,一些複雜的查詢操做是完成不了。

ORM用多了SQL語句就不會寫了,關係數據庫相關技能退化...

ORM總結

ORM只是一種工具,工具確實能解決一些重複,簡單的勞動。這是不能否認的。

但咱們不能期望某個工具能一勞永逸地解決全部問題,一些特殊問題仍是須要特殊處理的。

可是在整個軟件開發過程當中須要特殊處理的狀況應該都是不多的,不然所謂的工具也就失去了它存在的意義。

Django中的ORM

Django項目使用MySQL數據庫

1. 在Django項目的settings.py文件中,配置數據庫鏈接信息:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "你的數據庫名稱",  # 須要本身手動建立數據庫
        "USER": "數據庫用戶名",
        "PASSWORD": "數據庫密碼",
        "HOST": "數據庫IP",
        "POST": 3306
    }
}

2. 在與Django項目同名的目錄下的__init__.py文件中寫以下代碼,告訴Django使用pymysql模塊鏈接MySQL數據庫

import pymysql
 
pymysql.install_as_MySQLdb()

注:數據庫遷移的時候出現一個警告

WARNINGS: 
?: (mysql.W002) MySQL Strict Mode is not set for database connection 'default'
HINT: MySQL's Strict Mode fixes many data integrity problems in MySQL, such as data truncation upon insertion, by escalating warnings into errors. It is strongly recommended you activate it.

在配置中多加一個OPTIONS參數:Django官網解釋

 'OPTIONS': {
    'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"},

Model

字段 

經常使用字段 

AutoField

自增的整形字段,必填參數primary_key=True,則成爲數據庫的主鍵。無該字段時,django自動建立。

一個model不能有兩個AutoField字段。

IntegerField

一個整數類型。數值的範圍是 -2147483648 ~ 2147483647。

CharField

字符類型,必須提供max_length參數。max_length表示字符的長度。

DateField

日期類型,日期格式爲YYYY-MM-DD,至關於Python中的datetime.date的實例。

參數:

  • auto_now:每次修改時修改成當前日期時間。
  • auto_now_add:新建立對象時自動添加當前日期時間。

auto_now和auto_now_add和default參數是互斥的,不能同時設置。

DatetimeField

日期時間字段,格式爲YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],至關於Python中的datetime.datetime的實例。

字段類型,詳情可點擊查詢官網

AutoField(Field)
        - int自增列,必須填入參數 primary_key=True

    BigAutoField(AutoField)
        - bigint自增列,必須填入參數 primary_key=True

        注:當model中若是沒有自增列,則自動會建立一個列名爲id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自動建立一個列名爲id的且爲自增的整數列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定義自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整數 -3276832767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整數 032767

    IntegerField(Field)
        - 整數列(有符號的) -21474836482147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整數 02147483647

    BigIntegerField(IntegerField):
        - 長整型(有符號的) -92233720368547758089223372036854775807

    BooleanField(Field)
        - 布爾值類型

    NullBooleanField(Field):
        - 能夠爲空的布爾值

    CharField(Field)
        - 字符類型
        - 必須提供max_length參數, max_length表示字符長度

    TextField(Field)
        - 文本類型

    EmailField(CharField):
        - 字符串類型,Django Admin以及ModelForm中提供驗證機制

    IPAddressField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 IPV4 機制

    GenericIPAddressField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6
        - 參數:
            protocol,用於指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 若是指定爲True,則輸入::ffff:192.0.2.1時候,可解析爲192.0.2.1,開啓此功能,須要protocol="both"

    URLField(CharField)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 URL

    SlugField(CharField)
        - 字符串類型,Django Admin以及ModelForm中提供驗證支持 字母、數字、下劃線、鏈接符(減號)

    CommaSeparatedIntegerField(CharField)
        - 字符串類型,格式必須爲逗號分割的數字

    UUIDField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供對UUID格式的驗證

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供讀取文件夾下文件的功能
        - 參數:
                path,                      文件夾路徑
                match=None,                正則匹配
                recursive=False,           遞歸下面的文件夾
                allow_files=True,          容許文件
                allow_folders=False,       容許文件夾

    FileField(Field)
        - 字符串,路徑保存在數據庫,文件上傳到指定目錄
        - 參數:
            upload_to = ""      上傳文件的保存路徑
            storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路徑保存在數據庫,文件上傳到指定目錄
        - 參數:
            upload_to = ""      上傳文件的保存路徑
            storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage
            width_field=None,   上傳圖片的高度保存的數據庫字段名(字符串)
            height_field=None   上傳圖片的寬度保存的數據庫字段名(字符串)

    DateTimeField(DateField)
        - 日期+時間格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD

    TimeField(DateTimeCheckMixin, Field)
        - 時間格式      HH:MM[:ss[.uuuuuu]]

    DurationField(Field)
        - 長整數,時間間隔,數據庫中按照bigint存儲,ORM中獲取的值爲datetime.timedelta類型

    FloatField(Field)
        - 浮點型

    DecimalField(Field)
        - 10進制小數
        - 參數:
            max_digits,小數總長度
            decimal_places,小數位長度

    BinaryField(Field)
        - 二進制類型

字段類型

建立數據庫

models.py

from django.db import models

# Create your models here.
class Biao(models.Model):
    nid = models.AutoField(primary_key=True)     #設置主鍵
    name = models.CharField(max_length=32)       
    age = models.IntegerField()
    birth = models.DateTimeField(auto_now_add=True)       #時間爲當前時間

 建庫

python3 manage.py makemigrations

python3 manage.py migrate

 字段參數

 null                數據庫中字段是否能夠爲空
    db_column           數據庫中字段的列名
    default             數據庫中字段的默認值
    primary_key         數據庫中字段是否爲主鍵
    db_index            數據庫中字段是否能夠創建索引
    unique              數據庫中字段是否能夠創建惟一索引
    unique_for_date     數據庫中字段【日期】部分是否能夠創建惟一索引
    unique_for_month    數據庫中字段【月】部分是否能夠創建惟一索引
    unique_for_year     數據庫中字段【年】部分是否能夠創建惟一索引
 
    verbose_name        Admin中顯示的字段名稱
    blank               Admin中是否容許用戶輸入爲空
    editable            Admin中是否能夠編輯
    help_text           Admin中該字段的提示信息
    choices             Admin中顯示選擇框的內容,用不變更的數據放在內存中從而避免跨表操做
                        如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
 
    error_messages      自定義錯誤信息(字典類型),從而定製想要顯示的錯誤信息;
                        字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                        如:{'null': "不能爲空.", 'invalid': '格式錯誤'}
 
    validators          自定義錯誤驗證(列表類型),從而定製想要的驗證規則
                        from django.core.validators import RegexValidator
                        from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
                        MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                        如:
                            test = models.CharField(
                                max_length=32,
                                error_messages={
                                    'c1': '優先錯信息1',
                                    'c2': '優先錯信息2',
                                    'c3': '優先錯信息3',
                                },
                                validators=[
                                    RegexValidator(regex='root_\d+', message='錯誤了', code='c1'),
                                    RegexValidator(regex='root_112233\d+', message='又錯誤了', code='c2'),
                                    EmailValidator(message='又錯誤了', code='c3'), ]
                            )
 
字段參數

ORM查詢操做

13種操做方法 

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models


aa
= models.Biao.objects.all() # all()獲取到全部的數據 # get 獲取一個知足條件的對象,獲取不到或者獲取多個就報錯(通常不用) bb = models.Biao.objects.get(age=25) # filter 獲取知足條件的全部對象,獲取不到返回空,不會報錯 cc = models.Biao.objects.filter(name='wk') #first()獲取多個對象的第一個 dd = models.Biao.objects.filter(name='wk').first() #last() 獲取多個對象的最後一個 ee = models.Biao.objects.filter(name='wk').last() #exclude 獲取不知足條件的全部對象,獲取不到返回空,不會報錯 ff = models.Biao.objects.exclude(name='wk') #values() 獲取數據的key以及對應的值,獲取的是字典形式的QuerySet gg = models.Biao.objects.all().values() #以鍵值的形式獲取全部數據 hh = models.Biao.objects.all().values('name','age') #也能夠獲取指定鍵的 #values_list() 以元組的形式獲取數據,根據位置取值 ii = models.Biao.objects.all().values_list() #order_by 排序 可指定字段,也可指定多個字段,多字段先排第一個,加"-"降序 jj = models.Biao.objects.order_by('-nid','age') #reverse()反向排序, 對一個有序列表纔有效 kk = models.Biao.objects.order_by('age').reverse() #distinct() 去重 ll = models.Biao.objects.values('name').distinct() # count() 計數,記錄有多少數據 返回一個整數 mm = models.Biao.objects.count() # exists()判斷數據是否存在 不在返回False 存在True nn = models.Biao.objects.first(name="wk").exists()

單表的雙下劃線方法

    # __gt大於  __lt小於
    cc = models.Biao.objects.filter(nid__gt=1)
    # __gte大於等於    __lte小於等於
    dd = models.Biao.objects.filter(nid__gte=1)
    #__in 取等於1和3的
    ee = models.Biao.objects.filter(nid__in=[1,3])
    #__range 取1到3的
    ff = models.Biao.objects.filter(nid__range=[1, 3])
    #__contains  name字段的值含有字母w的
    gg = models.Biao.objects.filter(name__contains='w')
    # __icontains  name字段的值含有字母w的 忽略大小寫
    jj = models.Biao.objects.filter(name__icontains='w')
#相似的還有:startswith,istartswith 以什麼什麼開頭 加i不區分大小寫 # , endswith, iendswith 以什麼什麼結尾
# date字段還能夠: models.Class.objects.filter(first_day__year=2017)
#__isnull查找爲空的 =False查出不爲空的 (空字符串和null不同,空字符串查不出,null才查的出)
jj = models.Biao.objects.filter(name__isnull=True)

 外鍵的操做

建立兩個關聯表 一對多

class chubanshe(models.Model):
    meme = models.CharField(max_length=32)  

class Book(models.Model):
    title = models.CharField(max_length=32)
    chubanshe = models.ForeignKey('chubanshe',on_delete=models.CASCADE)   #關聯chubanshe表 ,而且級聯刪除

方法

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models

#正向查 多對一 (book對chubanshe)
    book_obj = models.Book.objects.get(pk=1) #取出book表裏主鍵爲1的對象
    print(book_obj.chubanshe)  #點book表裏對應的外鍵的key 獲得對應外鍵chubansh表裏的關聯的對象
#反向查 一對多 (chubanshe對book 如下方法2選一 )
    #models.py裏 不寫 related_name的時候
    chubanshe = models.chubanshe.objects.get(pk=2)  #取出版社表裏的對象
    print(chubanshe.book_set)    #關係對象管理(根據chubanshe表反向關聯Book表 book即Book表的小寫)
    print(chubanshe.book_set.all())
    #models.py裏寫related_name的時候(在 models.py裏的 外鍵表裏related_name = books)
    chubanshe = models.chubanshe.objects.get(pk=2)  # 取出版社表裏的對象
    print(chubanshe.books)  # 關係對象管理(根據chubanshe表反向關聯Book表 book即Book表的小寫)
    print(chubanshe.books.all())
    #基於字段的查詢 根據book表查 chubanshe表的內容 指定book內容的方法(related_name__鍵=值)
     #若是不指定related_name就小寫類名book__title  這種跨表__字段的方法效率更高
    
    ret = models.chubanshe.objects.filter(books__title="呵呵噠")
    rett = models.Book.objects.filter(chubanshe__meme='沙河出版社')
    print(ret)

多對多

class Book(models.Model):
    title = models.CharField(max_length=32)   #related_name 反向查詢時的名字
    chubanshe = models.ForeignKey('chubanshe',related_name='books',on_delete=models.CASCADE)
    def __str__(self):
        return "%s"  % self.title

class zuozhe(models.Model):
    name = models.CharField(max_length=32) 
# ManyToManyField 多對多 zuozhe表關聯Book表 這樣建立在zuozhe表裏不會出現books字段而是在庫裏多一個zuozhe_books的表
books = models.ManyToManyField('Book')

提交數據庫會出現兩個表

 

 

 

多表關聯操做

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models

#多對多
    zuozhe_obj = models.zuozhe.objects.get(pk=1)
    print(zuozhe_obj)
    print(zuozhe_obj.name)
    #拿到主鍵爲1的做者寫的全部書籍   books爲related_name起的別名
    print(zuozhe_obj.books.all())
    book_obj = models.Book.objects.get(pk=3)
    #拿到 主鍵爲3的書的全部做者  用小寫類名_set的方法(zuozhe_set)
    print(book_obj.zuozhe_set.all())
    #create 建立新數據並生成對應關係
    #經過作做者對象建立關聯的書籍對象 會在多對多的關聯表生成關係數據
    zuozhe_obj.books.create(title='鬼吹燈',chubanshe_id=1)
    #經過書籍對象建立關聯的做者對象
    book_obj.zuozhe_set.create(name='吳老狗')
    #add添加 書與做者的關係   外鍵的管理對象只能用對象 不餓能用ID
    zuozhe_obj.books.add(2,5) #給 zuozhe_obj這個對象(主鍵爲1的做者)添加主鍵爲2和5的兩本書
    book_obj.zuozhe_set.add(2,4,6) #給book_obj這個對象(主鍵爲1的書)添加主鍵爲2,4,6的三個做者
    zuozhe_obj.books.add(*models.Book.objects.filter(id__in=[1,2,3])) #*爲打散
    #remove 刪除對應關係 和add同樣
    zuozhe_obj.books.remove(2, 5)
    book_obj.zuozhe_set.remove(2, 4, 6)
    #clear 清空
    zuozhe_obj.books.clear()
    book_obj.zuozhe_set.clear()
    #set 從新設置做者對應的書籍爲1,2,3,4 set以後該做者對應的書只有1,2,3,4以前有的其餘的會刪除,以前沒的會添加
    zuozhe_obj.books.set([1,2,3,4])

 聚合和分組

聚合

aggregate()QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。

鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。

用到的內置函數:

from django.db.models import Avg, Sum, Max, Min, Count

 定義表

class Book(models.Model):
    title = models.CharField(max_length=32)   #related_name 反向查詢時的名字
    chubanshe = models.ForeignKey('chubanshe',related_name='books',on_delete=models.CASCADE)
    jiage = models.DecimalField(max_digits=5,decimal_places=2)   #價格 位數最大5位 999.99
    def __str__(self):
        return "%s"  % self.title

聚合函數的使用

#聚合 引用對應的方法
    from django.db.models import Max,Min,Sum,Avg,Count
    #aggregate 聚合函數 Max拿價格最高的 Min最低的 Avg平均價格 Sum總和 Count個數
    ret = models.Book.objects.aggregate(Max('jiage'),Min('jiage'),Avg('jiage'),Sum('jiage'),Count('jiage'))
    #給前端關鍵字傳參   聚合後是個字典
    rett = models.Book.objects.aggregate(Max=Max('jiage'), Min=Min('jiage'), Avg=Avg('jiage'))
    print(ret)

分組

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models
    from django.db.models import Max, Min, Sum, Avg, Count
#分組
    #看每一個出版社的書籍的平均價
    #第一種方法   按出版社分組              外接書籍的平均價格  以字典的形式顯示
    ret = models.chubanshe.objects.annotate(Avg('books__jiage')).values()
    #第二種方法
    #     查詢Book表         之外接出版社的名字劃分字典     給書的價格分組
    ret = models.Book.objects.values('chubanshe__meme').annotate(Avg('jiage'))
    print(ret)

 F查詢和Q查詢

F查詢 用來對倆個字段做比較

在上面全部的例子中,咱們構造的過濾器都只是將字段值與某個常量作比較。若是咱們要對兩個字段的值作比較,那該怎麼作呢?

Django 提供 F() 來作這樣的比較。F() 的實例能夠在查詢中引用字段,來比較同一個 model 實例中兩個不一樣字段的值。

class Book(models.Model):
    title = models.CharField(max_length=32)   #related_name 反向查詢時的名字
    chubanshe = models.ForeignKey('chubanshe',on_delete=models.CASCADE)
    jiage = models.DecimalField(max_digits=5,decimal_places=2)  #最大5位 999.99
    shouchu = models.IntegerField()       #增長書的售出字段
    kucun = models.IntegerField()         #增長書的庫存字段
    def __str__(self):
        return "%s"  % self.title
import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models
    from django.db.models import F, Q  #引用F 和Q
    #查詢庫存大於售出的書
    ret = models.Book.objects.filter(kucun__gt=F('shouchu')).values()
    print(ret)
    #Django支持 F() 對象之間以及F() 對象和常數之間的加減乘除和取模的操做。
    #更新出售的書是庫存書的2倍
    models.Book.objects.update(kucun=F('shouchu') * 2)  #庫存=售出*2

Q查詢

filter() 等方法中的關鍵字參數查詢都是一塊兒進行「AND」 的。 若是你須要執行或查詢須要用到Q

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models
    from django.db.models import F, Q  #引用F 和Q
#查詢主鍵大於5 或者主鍵小於3的 (
|是或 &是與 ~是非) ret = models.Book.objects.filter(Q(pk__gt=5)|Q(pk__lt=3)) print(ret)

事務

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diccc.settings")
    import django
    django.setup()
    from app01 import models
    from django.db import transaction   #事務
    try:
        with transaction.atomic():  #一次性這行,若是中間有報錯則回滾.前邊執行成功的所有做廢
            #一系列操做
            models.chubanshe.objects.create(meme='唉呀媽呀')  #給出版社表增長內容
            models.zuozhe.objects.create(name='臥底瑪雅')   #給做者表增長內容
    except Exception as e:
        print(e)

Django ORM執行原生SQL

# extra
# 在QuerySet的基礎上繼續執行子語句
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

# select和select_params是一組,where和params是一組,tables用來設置from哪一個表
# Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
# Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
# Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

舉個例子:
models.UserInfo.objects.extra(
                    select={'newid':'select count(1) from app01_usertype where id>%s'},
                    select_params=[1,],
                    where = ['age>%s'],
                    params=[18,],
                    order_by=['-age'],
                    tables=['app01_usertype']
                )
                """
                select 
                    app01_userinfo.id,
                    (select count(1) from app01_usertype where id>1) as newid
                from app01_userinfo,app01_usertype
                where 
                    app01_userinfo.age > 18
                order by 
                    app01_userinfo.age desc
                """


# 執行原生SQL
# 更高靈活度的方式執行原生SQL語句
# from django.db import connection, connections
# cursor = connection.cursor()  # cursor = connections['default'].cursor()
# cursor.execute("""SELECT * from auth_user where id = %s""", [1])
# row = cursor.fetchone()

Django終端打印SQL語句

在Django項目的settings.py文件中,在最後複製粘貼以下代碼:

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

即爲你的Django項目配置上一個名爲django.db.backends的logger實例便可查看翻譯後的SQL語句

 cookie

https://www.cnblogs.com/maple-shaw/articles/9502602.html

   是服務器發送出來存儲在瀏覽器上的一組組鍵值對,下次訪問服務器時瀏覽器會自動攜帶這些鍵值對,以便服務器提取有用信息。

一個簡單的cookie認證登陸頁

from django.conf.urls import url, include

from app01 import views
urlpatterns = [
    url(r'^login/$', views.login, name='login'),   #登陸頁
    url(r'^zhanshi/$', views.zhanshi, name='zhanshi'),   #展現頁
]

zhanshi.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>OJBK</h1>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    <p>
       用戶名: <input type="text" name="yh">
    </p>
    <p>
        密碼: <input type="password" name="pas">
    </p>
    <button>登陸</button>
    </form>
</body>
</html>

後臺邏輯

from django.shortcuts import render,reverse,redirect

# Create your views here.
#一個簡單的登陸展現頁
def login_required(func):         #cookie 驗證裝飾器
    def inner(request,*args,**kwargs):
        is_login = request.COOKIES.get('is_login')  # 展現頁獲取cookie
        if is_login != '1':       # 若是cookie的值不是咱們給的值則反回一個讓其從新登陸
            return redirect(reverse('login'))
        ret = func(request,*args,**kwargs)
        return ret
    return inner

def login(request):
    if request.method == 'POST':
        username = request.POST.get('yh')
        passwd = request.POST.get('pas')
        if username == 'wk' and passwd == '123456':
            ret = redirect(reverse('zhanshi'))
            ret.set_cookie('is_login', '1')   #給返回的頁面加一個cookie
            return ret
    return render(request,'login.html')
@login_required def zhanshi(request):
return render(request,'zhanshi.html') #若是cookie認證成功 怎返回展現頁

實現 未登錄時,進入登陸頁,登錄後直接跳轉到以前的頁面

from django.shortcuts import render,reverse,redirect

# Create your views here.
#實現 未登錄時,進入登陸頁,登錄後直接跳轉到以前的頁面
def login_required(func):     #cookie 驗證裝飾器
    def inner(request,*args,**kwargs):
        is_login = request.COOKIES.get('is_login')  # 展現頁獲取cookie
        if is_login != '1':  # 若是cookie的值不是咱們給的值則反回一個讓其從新登陸
            url = request.path_info #獲取到 進入登陸頁前的頁面地址
            return redirect('login/?next=%s' % url) #將獲取到的url地址傳遞給登陸頁
        ret = func(request,*args,**kwargs)
        return ret
    return inner

def login(request):
    if request.method == 'POST':
        username = request.POST.get('yh')
        passwd = request.POST.get('pas')
        if username == 'wk' and passwd == '123456':
            url = request.GET.get('next')  #獲取進入到登陸頁時裝飾器傳進來的url地址
            if url :        #若是url不爲空
                ret = redirect(url)   #則重定向到獲取到的url地址
            else:          #若是爲空
                ret = redirect(reverse('zhanshi'))  #則重定向到展現頁
            ret.set_cookie('is_login', '1')  # 給返回的頁面加一個cookie
            return ret
    return render(request,'login.html')
@login_required
def zhanshi(request):
    return render(request,'zhanshi.html')  #若是cookie認證成功 怎返回展現頁

 加密加鹽的cookie (加密和沒加密同樣因此沒啥需求不用)

ret.set_cookie('is_login', '1')  # 普通的cookie
ret.set_signed_cookie('is_login', '1',salt='yan')  #加鹽的cookie
is_login = request.COOKIES.get('is_login')  #獲取普通cookie
is_login = request.get_signed_cookie('is_login',salt='yan',default='')  #獲取加密的cookie,default若是獲取不到值則報錯因此給他一個空

刪除cookie(註銷功能)

邏輯:
def logout(request):
    ret = redirect('/login/')  #註銷後返回登陸頁面
    ret.delete_cookie('is_login')  #而且根據鍵刪除對應的cookle return ret

路由
    url(r'^logout/$', views.logout, name='logout'),

前端
   <a href="{% url 'logout' %}">註銷</a>

設置Cookie

參數:

  • key, 鍵          *
  • value='', 值             *
  • max_age=None, 超時時間  #設置超時時間通常用這個          *
  • expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)
  • path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie能夠被任何url的頁面訪問    *
  • domain=None, Cookie生效的域名
  • secure=False,  https傳輸             True是http傳輸
  • httponly=False 只能http協議傳輸,沒法被JavaScript獲取(不是絕對,底層抓包能夠獲取到也能夠被覆蓋 

session

Cookie雖然在必定程度上解決了「保持狀態」的需求,可是因爲Cookie自己最大支持4096字節,以及Cookie自己保存在客戶端,可能被攔截或竊取,所以就須要有一種新的東西,它能支持更多的字節,而且他保存在服務器,有較高的安全性。這就是Session。

基於HTTP協議的無狀態特徵,服務器根本就不知道訪問者是「誰」。那麼上述的Cookie就起到橋接的做用。

咱們能夠給每一個客戶端的Cookie分配一個惟一的id,這樣用戶在訪問時,經過Cookie,服務器就知道來的人是「誰」。而後咱們再根據不一樣的Cookie的id,在服務器上保存一段時間的私密資料,如「帳號密碼」等等。

總結而言:Cookie彌補了HTTP無狀態的不足,讓服務器知道來的人是「誰」;可是Cookie以文本的形式保存在本地,自身安全性較差;因此咱們就經過Cookie識別不一樣的用戶,對應的在Session裏保存私密的信息以及超過4096字節的文本。

另外,上述所說的Cookie和Session實際上是共通性的東西,不限於語言和框架

session的使用

設置與獲取

request.session['is_login'] = 1
is_login = request.session.get('is_login')

更多方法

# 獲取、設置、刪除Session中數據
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在則不設置 若是不存在則設置k1=123
del request.session['k1']


# 全部 鍵、值、鍵值對
request.session.keys()
request.session.values()
request.session.items()  #拿全部鍵值對 和字典同樣
request.session.iterkeys()   #返回一個鍵的迭代器
request.session.itervalues()
request.session.iteritems()

# 會話session的key
request.session.session_key    #獲取數據庫裏session表裏的session_key字段(session_data的鍵)

# 將全部Session失效日期小於當前日期的數據刪除
request.session.clear_expired()

# 檢查會話session的key在數據庫中是否存在
request.session.exists("session_key")  #返回True 和Filus

# 刪除當前會話的全部Session數據
request.session.delete()   #用它能夠作註銷 他只刪數據庫的session 不刪瀏覽器以前保存的
  
# 刪除當前的會話數據並刪除會話的Cookie。
request.session.flush()        #若是用註銷用這個比較好
    這用於確保前面的會話數據不能夠再次被用戶的瀏覽器訪問
    例如,django.contrib.auth.logout() 函數中就會調用它。

# 設置會話Session和Cookie的超時時間 request.session.set_expiry(value) * 若是value是個整數,session會在些秒數後失效。  
    * 若是value是個datatime或timedelta,session就會在這個時間後失效。 
    * 若是value是0,用戶關閉瀏覽器session就會失效。 
    * 若是value是None,session會依賴全局session失效策略。

 在後臺的使用

from django.shortcuts import render,reverse,redirect

# Create your views here.
#實現 未登錄時,進入登陸頁,登錄後直接跳轉到以前的頁面
def login_required(func):     #session 驗證裝飾器
    def inner(request,*args,**kwargs):
        #is_login = request.COOKIES.get('is_login')  #獲取普通cookie
        is_login = request.session.get('is_login')   #session的獲取 驗證是否已登錄
        if is_login != 1:  # 若是session的值不是咱們給的值則反回一個讓其從新登陸
            url = request.path_info #獲取到 進入登陸頁前的頁面地址
            print(url)
            return redirect('/login/?next=%s' % url) #將獲取到的url地址傳遞給登陸頁
        ret = func(request,*args,**kwargs)
        return ret
    return inner

def login(request):
    if request.method == 'POST':
        username = request.POST.get('yh')
        passwd = request.POST.get('pas')
        if username == 'wk' and passwd == '123456':
            url = request.GET.get('next')  #獲取進入到登陸頁時裝飾器傳進來的url地址
            if url :        #若是url不爲空
                ret = redirect(url)   #則重定向到獲取到的url地址
            else:          #若是爲空
                ret = redirect(reverse('zhanshi'))  #則重定向到展現頁
            #ret.set_cookie('is_login', '1',max_age=5,path='/zhanshi/')
            request.session['is_login'] = 1      #session的使用
            request.session.set_expiry(0)     #關閉瀏覽器則session超時
            return ret
    return render(request,'login.html')
@login_required
def zhanshi(request):
    return render(request,'zhanshi.html')  #若是session認證成功 怎返回展現頁
@login_required
def xixi(request):
    return render(request,'xixi.html')
def logout(request):             #註銷
    request.session.flush()
    return redirect('/login/')

Django中的Session配置

Django中默認支持Session,其內部提供了5種類型的Session供開發者使用。

from django.conf import global_settings     #打開裏面是django的全部配置 session的也在
1. 數據庫Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'     # 引擎(默認)

2. 緩存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎 可用redis和memacach  通常都存在緩存裏
SESSION_CACHE_ALIAS = 'default'                            # 使用的緩存別名(默認內存緩存,也能夠是memcache),此處別名依賴緩存的設置

3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 緩存文件路徑,若是爲None,則使用tempfile模塊獲取一個臨時地址tempfile.gettempdir() 

4. 緩存+數據庫
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

其餘公用設置項:
SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串(默認)
SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路徑(默認)
SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默認)
SESSION_COOKIE_SECURE = False                            # 是否Https傳輸cookie(默認)
SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http傳輸(默認)
SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默認)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否關閉瀏覽器使得Session過時(默認)
SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次請求都保存Session,默認修改以後才保存(默認)

Django中Session相關設置

中間件

什麼是中間件?

官方的說法:中間件是一個用來處理Django的請求和響應的框架級別的鉤子。它是一個輕量、低級別的插件系統,用於在全局範圍內改變Django的輸入和輸出。每一箇中間件組件都負責作一些特定的功能。

可是因爲其影響的是全局,因此須要謹慎使用,使用不當會影響性能。

說的直白一點中間件是幫助咱們在視圖函數執行以前和執行以後均可以作一些額外的操做,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在處理請求的特定的時間去執行這些方法。

咱們一直都在使用中間件,只是沒有注意到而已,打開Django項目的Settings.py文件,看到下圖的MIDDLEWARE配置項。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

MIDDLEWARE配置項是一個列表,列表中是一個個字符串,這些字符串實際上是一個個類,也就是一個個中間件。

咱們以前已經接觸過一個csrf相關的中間件了?咱們一開始讓你們把他註釋掉,再提交post請求的時候,就不會被forbidden了,後來學會使用csrf_token以後就再也不註釋這個中間件了。

自定義中間件

中間件能夠定義五個方法,分別是:(主要的是process_request和process_response)

  • process_request(self,request)
  • process_view(self, request, view_func, view_args, view_kwargs)
  • process_template_response(self,request,response)    #須要後臺有TemplateResponse() 才觸發
  • process_exception(self, request, exception)         #須要後臺有錯誤才觸發
  • process_response(self, request, response)      

以上方法的返回值能夠是None或一個HttpResponse對象,若是是None,則繼續按照django定義的規則向後繼續執行,若是是HttpResponse對象,則直接將該對象返回給用戶。

 

1.首先在項目目錄下建立存放中間件文件的目錄 

中間件文件

2.在項目裏的settings.py裏註冊中間件

 

process_request(self,request)  方法 

    參數: request  請求的對象和視圖中是同一個

    執行時間 :在視圖以前(就是以前的後臺,views.py)

    執行順序:按照註冊的順序執行

    return返回值:

              None:  走正常流程

    HttpResponse ,不執行下邊的中間件,不走視圖,若是此類中有process_response方法,則走process_response方法中的return,若是有process_view則執行

             當前函數中的process_view,再執行當前函數中的process_response 其餘不執行

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,reverse,redirect,HttpResponse
class MD1(MiddlewareMixin):
     def process_request(self,request):
         print('MD1 process_request' )
         return HttpResponse("MD1 process_request") 

 

process_response(self, request, response)  方法

     參數:  request  請求的對象 和視圖中的同樣    response響應對象,就是後臺執行完後return 的返回值

     執行時間: 在視圖函數以後

     執行順序: 按註冊的順序   倒序執行

     返回值:    必 須 有 返回值,不然報錯 返回response

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,reverse,redirect,HttpResponse
class MD1(MiddlewareMixin):
     def process_request(self,request):
         print('MD1 process_request')
         # 執行完視圖函數後才執行process_response(self,request,response)  必須有return   response就是視圖執行完後return的結果
     def process_response(self,request,response):
         print('MD1 process_response')
         return response

class MD2(MiddlewareMixin):
    def process_request(self,request):
         print('MD2 process_request')
    def process_response(self, request, response):
        print('MD2 process_response')
        return response

註冊順序是MD1在前MD2在後   process_response的執行順序是倒序先執行MD2再執行MD1 

此時的執行流程

 

process_view(self,request,view_func,view_args,view_kwargs) 方法

     參數:  request  請求的對象 和視圖中的同樣     view_func view_args view_kwargs分別是   urls.py裏的

 # view_func = views.index    view_args = url位置參數(\d+)   view_kwargs =  關鍵字參數(?P<num>[0-9]+)
 url(r'^index/(\d+)/(?P<num>[0-9]+)', views.index, name='index'),  

     執行時間: 在process_request以後 ,在視圖函數以前執行

     執行順序: 按註冊的順序執行

  return返回值:

              None:  走正常流程

    HttpResponse :  下一個中間件的process_view和視圖不執行,直接執行最後一箇中間件的process_response 在往上執行其餘的process_response

process_exception(self,request,exception):

  參數:  request  請求的對象 和視圖中的同樣  exception 錯誤對象

     執行時間: 有異常才執行

     執行順序: ,在視圖出現異常後 按註冊的順序  倒序執行,可以處理掉異常後, 再執行process_response

     返回值:    

    None:  交給下一個中間件的process_exception方法處理異常

    HttpResponse : 下一個中間件的process_exception方法就不執行了,直接走最後一箇中間件的process_response

使用方法 

     def process_exception(self, request, exception):   #後臺報錯則觸發
         return HttpResponse('2322')

process_template_response(self,request,response)

  參數:  request  請求的對象 和視圖中的同樣   response響應對象,就是後臺執行完後return 的返回值

     執行時間:  視圖函數返回一個template_response對象

     執行順序: ,按註冊表倒序執行

     返回值:    

    HttpResponse :  返回response

使用方法:

視圖:執行index 返回    return TemplateResponse()  而且裏邊帶有一個字典

from django.template.response import TemplateResponse

def index(request):
    return TemplateResponse(request,'xixi.html',{'ret':'呵呵噠'})

中間件:

   def process_template_response(self,request,response):
         print('MD1 process_template_response ')
         response.context_data = {'ret':'臥槽'}   #用context_data把ret的值替換掉 並返回  response
         return response                

前端:展現

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>嘻嘻</h1>
<h1> {{ ret }}</h1>
</body>
</html>

 

五種方法合集

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,reverse,redirect,HttpResponse
class MD1(MiddlewareMixin):
     def process_request(self,request):
         print('MD1 process_request')
     def process_response(self,request,response):
         print('MD1 process_response')
         return response
     def process_view(self,request,view_func,view_args,view_kwargs):
         print('MD1 process_view')
         #return HttpResponse('MD1 process_view')
     def process_exception(self, request, exception):
         return HttpResponse('2322')
     def process_template_response(self,request,response):
         print('MD1 process_template_response ')
         response.context_data = {'ret':'臥槽'}
         return response




class MD2(MiddlewareMixin):
    def process_request(self,request):
         print('MD2 process_request')

    def process_response(self, request, response):
        print('MD2 process_response')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('MD1 process_view')
        # return HttpResponse('MD2 process_view')

    def process_exception(self, request, exception):
        print('MD1 process_exception')
        print(exception)
        return HttpResponse('2322')

    def process_template_response(self, request, response):
        return response
相關文章
相關標籤/搜索