Django【進階篇】

目錄

1、Model

2、admin

3、Form組件

4、Cookie

5、Session

6、分頁

7、序列化

1、Model

數據庫的配置

一、django默認支持sqlite,mysql, oracle,postgresql數據庫。javascript

     <1> sqlitephp

            django默認使用sqlite的數據庫,默認自帶sqlite的數據庫驅動 , 引擎名稱:django.db.backends.sqlite3css

     <2> mysqlhtml

            引擎名稱:django.db.backends.mysql前端

二、mysql驅動程序java

  •    MySQLdb(mysql python2中用)
  •    mysqlclient
  •    MySQL
  •    PyMySQL(純python的mysql驅動程序,python3用)

三、在django的項目中會默認使用sqlite數據庫,在settings裏有以下設置:python

建立數據庫表的步驟:mysql

一、建立model;jquery

二、建立生成數據庫的py文件:python manage.py makemigrations ;linux

三、建立數據庫表:python manage.py migrate;

注意:記得在settings裏的INSTALLED_APPS中加入'app01',而後再同步數據庫。

打開pycharm右側的databases,把建立好的db.sqlite3數據庫拖過去,就能夠操做數據庫表了,這裏咱們不用操心數據庫名的問題;

若是咱們想要使用別的數據庫,好比Mysql須要修改以下:

DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.mysql', 

        'NAME': 'books',    #你的數據庫名稱

        'USER': 'root',   #你的數據庫用戶名

        'PASSWORD': '', #你的數據庫密碼

        'HOST': '', #你的數據庫主機,留空默認爲localhost

        'PORT': '3306', #你的數據庫端口

    }

}
View Code

注意:

NAME即數據庫的名字,在mysql鏈接前該數據庫必須已經建立,而上面的sqlite數據庫下的db.sqlite3則是項目自動建立

USER和PASSWORD分別是數據庫的用戶名和密碼。

設置完後,再啓動咱們的Django項目前,咱們須要激活咱們的mysql。

而後,啓動項目,會報錯:no module named MySQLdb

這是由於django默認你導入的驅動是MySQLdb,但是MySQLdb對於py3有很大問題,因此咱們須要的驅動是PyMySQL

因此,咱們只須要找到項目名文件下的__init__,在裏面寫入:

import pymysql
pymysql.install_as_MySQLdb()

問題解決!
View Code

四、ORM(對象關係映射)

關係對象映射(Object Relational Mapping,簡稱ORM),用於實現面向對象編程語言裏不一樣類型系統的數據之間的轉換,換言之,就是用面向對象的方式去操做數據庫的建立表以及增刪改查等操做。

優勢: 一、ORM使得咱們的通用數據庫交互變得簡單易行,並且徹底不用考慮該死的SQL語句。快速開發,由此而來。

            二、能夠避免一些新手程序猿寫sql語句帶來的性能問題。

 缺點:一、性能有所犧牲,不過如今的各類ORM框架都在嘗試各類方法,好比緩存,延遲加載登來減輕這個問題。效果很顯著。

            二、對於個別複雜查詢,ORM仍然力不從心,爲了解決這個問題,ORM通常也支持寫raw sql。

            三、經過QuerySet的query屬性查詢對應操做的sql語句

author_obj=models.Author.objects.filter(id=2)
print(author_obj.query)

ORM語法

一、建立表

  • 基本結構
from django.db import models
class Userinfo(models.Model):
    name = models.CharField(max_length=30)
    email = models.EmailField()
    memo = models.TextField()
  • 模型經常使用的字段類型參數
<1> CharField
        #字符串字段, 用於較短的字符串.
        #CharField 要求必須有一個參數 maxlength, 用於從數據庫層和Django校驗層限制該字段所容許的最大字符數.

<2> IntegerField
       #用於保存一個整數.

<3> FloatField
        # 一個浮點數. 必須 提供兩個參數:
        #
        # 參數    描述
        # max_digits    總位數(不包括小數點和符號)
        # decimal_places    小數位數
                # 舉例來講, 要保存最大值爲 999 (小數點後保存2位),你要這樣定義字段:
                #
                # models.FloatField(..., max_digits=5, decimal_places=2)
                # 要保存最大值一百萬(小數點後保存10位)的話,你要這樣定義:
                #
                # models.FloatField(..., max_digits=19, decimal_places=10)
                # admin 用一個文本框(<input type="text">)表示該字段保存的數據.

<4> AutoField
        # 一個 IntegerField, 添加記錄時它會自動增加. 你一般不須要直接使用這個字段; 
        # 自定義一個主鍵:my_id=models.AutoField(primary_key=True)
        # 若是你不指定主鍵的話,系統會自動添加一個主鍵字段到你的 model.

<5> BooleanField
        # A true/false field. admin 用 checkbox 來表示此類字段.

<6> TextField
        # 一個容量很大的文本字段.
        # admin 用一個 <textarea> (文本區域)表示該字段數據.(一個多行編輯框).

<7> EmailField
        # 一個帶有檢查Email合法性的 CharField,不接受 maxlength 參數.

<8> DateField
        # 一個日期字段. 共有下列額外的可選參數:
        # Argument    描述
        # auto_now    當對象被保存時,自動將該字段的值設置爲當前時間.一般用於表示 "last-modified" 時間戳.
        # auto_now_add    當對象首次被建立時,自動將該字段的值設置爲當前時間.一般用於表示對象建立時間.
        #(僅僅在admin中有意義...)

<9> DateTimeField
        #  一個日期時間字段. 相似 DateField 支持一樣的附加選項.

<10> ImageField
        # 相似 FileField, 不過要校驗上傳對象是不是一個合法圖片.#它有兩個可選參數:height_field和width_field,
        # 若是提供這兩個參數,則圖片將按提供的高度和寬度規格保存.     
<11> FileField
     # 一個文件上傳字段.
     #要求一個必須有的參數: upload_to, 一個用於保存上載文件的本地文件系統路徑. 這個路徑必須包含 strftime #formatting, 
     #該格式將被上載文件的 date/time 
     #替換(so that uploaded files don't fill up the given directory).
     # admin 用一個<input type="file">部件表示該字段保存的數據(一個文件上傳部件) .

     #注意:在一個 model 中使用 FileField 或 ImageField 須要如下步驟:
            #(1)在你的 settings 文件中, 定義一個完整路徑給 MEDIA_ROOT 以便讓 Django在此處保存上傳文件. 
            # (出於性能考慮,這些文件並不保存到數據庫.) 定義MEDIA_URL 做爲該目錄的公共 URL. 要確保該目錄對 
            #  WEB服務器用戶賬號是可寫的.
            #(2) 在你的 model 中添加 FileField 或 ImageField, 並確保定義了 upload_to 選項,以告訴 Django
            # 使用 MEDIA_ROOT 的哪一個子目錄保存上傳文件.你的數據庫中要保存的只是文件的路徑(相對於 MEDIA_ROOT). 
            # 出於習慣你必定很想使用 Django 提供的 get_<#fieldname>_url 函數.舉例來講,若是你的 ImageField 
            # 叫做 mug_shot, 你就能夠在模板中以 {{ object.#get_mug_shot_url }} 這樣的方式獲得圖像的絕對路徑.

<12> URLField
      # 用於保存 URL. 若 verify_exists 參數爲 True (默認), 給定的 URL 會預先檢查是否存在( 即URL是否被有效裝入且
      # 沒有返回404響應).
      # admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框)

<13> NullBooleanField
       # 相似 BooleanField, 不過容許 NULL 做爲其中一個選項. 推薦使用這個字段而不要用 BooleanField 加 null=True 選項
       # admin 用一個選擇框 <select> (三個可選擇的值: "Unknown", "Yes" 和 "No" ) 來表示這種字段數據.

<14> SlugField
       # "Slug" 是一個報紙術語. slug 是某個東西的小小標記(短籤), 只包含字母,數字,下劃線和連字符.#它們一般用於URLs
       # 若你使用 Django 開發版本,你能夠指定 maxlength. 若 maxlength 未指定, Django 會使用默認長度: 50.  #在
       # 之前的 Django 版本,沒有任何辦法改變50 這個長度.
       # 這暗示了 db_index=True.
       # 它接受一個額外的參數: 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.

<13> XMLField
        #一個校驗值是否爲合法XML的 TextField,必須提供參數: schema_path, 它是一個用來校驗文本的 RelaxNG schema #的文件系統路徑.

<14> FilePathField
        # 可選項目爲某個特定目錄下的文件名. 支持三個特殊的參數, 其中第一個是必須提供的.
        # 參數    描述
        # path    必需參數. 一個目錄的絕對文件系統路徑. FilePathField 據此獲得可選項目. 
        # Example: "/home/images".
        # match    可選參數. 一個正則表達式, 做爲一個字符串, FilePathField 將使用它過濾文件名.  
        # 注意這個正則表達式只會應用到 base filename 而不是
        # 路徑全名. Example: "foo.*\.txt^", 將匹配文件 foo23.txt 卻不匹配 bar.txt 或 foo23.gif.
        # recursive可選參數.要麼 True 要麼 False. 默認值是 False. 是否包括 path 下面的所有子目錄.
        # 這三個參數能夠同時使用.
        # match 僅應用於 base filename, 而不是路徑全名. 那麼,這個例子:
        # FilePathField(path="/home/images", match="foo.*", recursive=True)
        # ...會匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif

<15> IPAddressField
        # 一個字符串形式的 IP 地址, (i.e. "24.124.1.30").
<16># CommaSeparatedIntegerField
        # 用於存放逗號分隔的整數值. 相似 CharField, 必需要有maxlength參數.
View Code
  • Field重要參數
    <1> null : 數據庫中字段是否能夠爲空

    <2> blank: django的 Admin 中添加數據時是否可容許空值

    <3> default:設定缺省值

    <4> editable:若是爲假,admin模式下將不能改寫。缺省爲真

    <5> primary_key:設置主鍵,若是沒有設置django建立表時會自動加上:
        id = meta.AutoField('ID', primary_key=True)
        primary_key=True implies blank=False, null=False and unique=True. Only one
        primary key is allowed on an object.

    <6> unique:數據惟一

    <7> verbose_name  Admin中字段的顯示名稱

    <8> validator_list:有效性檢查。非有效產生 django.core.validators.ValidationError 錯誤

    <9> db_column,db_index 若是爲真將爲此字段建立索引

    <10>choices:一個用來選擇值的2維元組。第一個值是實際存儲的值,第二個用來方便進行選擇。
                如SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),)
                gender = models.CharField(max_length=2,choices = SEX_CHOICES)

二、操做表

a、基本操做

---------------------增(create  ,  save) ---------------------

from app01.models import *

    #create方式一:   Author.objects.create(name='Alvin')

    #create方式二:   Author.objects.create(**{"name":"alex"})

    #save方式一:     author=Author(name="alvin")
                    author.save()

    #save方式二:     author=Author()
                    author.name="alvin"
                    author.save()

---------------------刪(delete) -------------------------

Book.objects.filter(id=1).delete()

---------------------改(update和save)------------------

# 方法一,get只能獲得一個對象
book = Book.objects.get(author='charlie')
book.price = 200
book.save()
# 方法二,推薦使用
Book.objects.filter(name='python').update(price=100)
注意:

<1> 第二種方式修改不能用get的緣由是:update是QuerySet對象的方法,
get返回的是一個model對象,它沒有update方法,而filter返回的是一個QuerySet對象
(filter裏面的條件可能有多個條件符合,好比name='alvin',可能有兩個name='alvin'的行數據)。

<2>在「插入和更新數據」小節中,咱們有提到模型的save()方法,這個方法會
更新一行裏的全部列。 而某些狀況下,咱們只須要更新行裏的某幾列。

---------------------查(filter,value等) ----------------------

#  <1>filter(**kwargs):      它包含了與所給篩選條件相匹配的對象

#  <2>all():                 查詢全部結果

#  <3>get(**kwargs):         
'''返回與所給篩選條件相匹配的對象,返回結果有且只有一個,若是符合篩選條件的
#對象超過一個或者沒有都會拋出錯誤。'''

#下面的方法都是對查詢的結果再進行處理:好比 objects.filter.values()--------

#  <4>values(*field):        
'''返回一個ValueQuerySet——一個特殊的QuerySet,
運行後獲得的並非一系列 model的實例化對象,而是一個可迭代的字典序列'''
                                     
#  <5>exclude(**kwargs):     它包含了與所給篩選條件不匹配的對象

#  <6>order_by(*field):      對查詢結果排序

#  <7>reverse():             對查詢結果反向排序

#  <8>distinct():            從返回結果中剔除重複紀錄,all()後面跟去重沒用,由於ID沒有重複,只有在values()後面跟去重纔有用,對某一個字段進行去重;

#  <9>values_list(*field):   它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列

#  <10>count():              返回數據庫中匹配查詢(QuerySet)的對象數量。

# <11>first():               返回第一條記錄

# <12>last():                返回最後一條記錄

#  <13>exists():             若是QuerySet包含數據,就返回True,不然返回False。

實例:

  • models文件內容

from django.db import models

class Book(models.Model):
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    pub_data = models.DateField()
    author = models.CharField(max_length=20,null=False)
    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=20)
    age = models.IntegerField()
  • view文件內容

#view.py
def addbook(request):
    # 添加表記錄方法一
    book = Book(name='python',price=99,pub_data='2018-2-8',author='charlie')
    book.save()
    # 方法二
    Book.objects.create(name='php',price=88,pub_data='2018-10-8',author='oldboy')
    # Book.objects.create(**dic)添加字典內容
    return HttpResponse('添加成功')

def update(request):
    # 方法一,get用於只獲得一個對象,若是對象重複就報錯
    # 這種方法會把全部的字段都從新賦值,效率低
    book = Book.objects.get(id=1)
    book.price = 998
    book.save()
    # 方法二,推薦使用,update是Queryset方法,只更新查詢到的記錄
    Book.objects.filter(name='python').update(price=150)
    return HttpResponse('修改爲功')

def select(request):
    book_list = Book.objects.all()
    book_list = Book.objects.all()[:3]#前三個
    book_list = Book.objects.all()[::2]#每兩個取一個
    book_list = Book.objects.all()[::-1]#倒着取
    # value只取指定的字段,獲得一個查詢集,內容是一個字典
    ret = Book.objects.filter(name='charlie').values('name','price')
    # 獲得一個查詢集,內容是一個列表,列表元素是元組
    ret = Book.objects.filter(name='charlie').values_list('name','price')
    return render(request,'index.html',locals())
  • 提示:對於每次建立一個對象,想顯示對應的raw sql,須要在settings加上日誌記錄部分:
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

b、利用雙下劃線方法進行模糊匹配

# 獲取個數
models.Tb1.objects.filter(name='seven').count()

# 大於,小於
#
models.Tb1.objects.filter(id__gt=1)              # 獲取id大於1的值
models.Tb1.objects.filter(id__gte=1)              # 獲取id大於等於1的值
models.Tb1.objects.filter(id__lt=10)             # 獲取id小於10的值
models.Tb1.objects.filter(id__lte=10)             # 獲取id小於10的值
models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 獲取id大於1 且 小於10的值

# in
#
models.Tb1.objects.filter(id__in=[11, 22, 33])   # 獲取id等於十一、2二、33的數據
models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

# isnull
Entry.objects.filter(pub_date__isnull=True)        #某字段是否能夠爲空

# contains
#
models.Tb1.objects.filter(name__contains="ven") #內容裏包含某字符串
models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感
models.Tb1.objects.exclude(name__icontains="ven") #內容裏沒有某字段(不區分大小寫)

# range
#
models.Tb1.objects.filter(id__range=[1, 2])   # 範圍bettwen and

# 其餘相似
#
startswith,istartswith(不區分大小寫), endswith, iendswith,

# order by
#
models.Tb1.objects.filter(name='seven').order_by('id')    # asc
models.Tb1.objects.filter(name='seven').order_by('-id')   # desc,反向

# group by
#
from django.db.models import Count, Min, Max, Sum
models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

# limit 、offset
#
models.Tb1.objects.all()[10:20]

# regex正則匹配,iregex 不區分大小寫
#
Entry.objects.get(title__regex=r'^(An?|The) +')
Entry.objects.get(title__iregex=r'^(an?|the) +')

# date
#
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

# year
#
Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)

# month
#
Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)

# day
#
Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)

# week_day
#
Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)

# hour
#
Event.objects.filter(timestamp__hour=23)
Event.objects.filter(time__hour=5)
Event.objects.filter(timestamp__hour__gte=12)

# minute
#
Event.objects.filter(timestamp__minute=29)
Event.objects.filter(time__minute=46)
Event.objects.filter(timestamp__minute__gte=29)

# second
#
Event.objects.filter(timestamp__second=31)
Event.objects.filter(time__second=2)
Event.objects.filter(timestamp__second__gte=31)

c、連表操做(了不得的雙下劃線)

  • 外鍵參數設置
#Django升級到2版本以後models.ForeignKey()須要填寫on_delect參數
on_delete=models.CASCADE,     # 刪除關聯數據,與之關聯也刪除
on_delete=models.DO_NOTHING,  # 刪除關聯數據,什麼也不作,最好不要;
on_delete=models.PROTECT,     # 刪除關聯數據,引起錯誤ProtectedError
on_delete=models.SET_DEFAULT #設置默認值,前提是ForeignKey必須設置的默認值;
on_delete=models.SET(...)   
'''設置給定值,或者若是傳入了callable,則調用它的結果。
在大多數狀況下,爲了不在導入models.py時執行查詢,必須傳遞callable'''
on_delete=models.SET_NULL,    # 刪除關聯數據,與之關聯的值設置爲null(前提FK字段須要設置爲可空,一對一同理)
# 例如:models.ForeignKey('關聯表', on_delete=models.SET_NULL, blank=True, null=True)
  • 一對多操做
  • models.py
#models.py
from django.db import models

class Book(models.Model):
    '''書籍'''
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    pub_data = models.DateField()
    publish = models.ForeignKey('Publish',on_delete=models.CASCADE)

    def __str__(self):
        return self.name

class Author(models.Model):
    '''做者'''
    name = models.CharField(max_length=20)

class Publish(models.Model):
    '''出版社'''
    #一對多的外鍵要加在多的哪一張表裏,一本書只能有一個出版社
    name = models.CharField(max_length=30)
    city = models.CharField(max_length=30)
  • view.py
from django.shortcuts import render,HttpResponse
from app.models import *

#-----------------------增-----------------------

    # 方式一,給publish_id賦值,django默認給你的外鍵字段添加_id
    Book.objects.create(name='linux運維',price=88,pub_data='2016-10-8',publish_id=1)
    # 方式二,給publish字段直接賦值,須要先獲得一個對象
    pulish_obj = Publish.objects.filter(name='西湖出版社')[0]
    Book.objects.create(name='Java', price=188, pub_data='2016-1-23', publish=pulish_obj)

#-----------------------刪-----------------------

    Book.objects.filter(publish__name='西湖出版社').delete()

#-----------------------改-----------------------

    Book.objects.filter(publish__name='人民出版社').update(price=300)

#-----------------------查-----------------------

    #第一種方式:經過對象(不推薦使用)
    # 正向
    book_obj = Book.objects.get(name='python')
    publish_obj = book_obj.publish
    publish_name = publish_obj.name
    #反向
    pub_obj = Publish.objects.filter(name='西湖出版社')[0]
    #下面的到一個查詢集,內容是字典,取第一個字典
    pub_name = pub_obj.book_set.all().values('name','price')[0]

    #第二種方式:經過filter(__),推薦使用
    #正向查詢,指定出版社的書籍信息;從有外鍵的表查詢爲正向;
    book_name = Book.objects.filter(publish__name='西湖出版社').values('name','price')[0]
    # 反向查詢,指定書籍的出版社信息,經過  類名__字段名
    pub_name = Publish.objects.filter(book__name='python').values('name','city')[0]
    #經過value(__)
    book_name = Book.objects.filter(name='Java').values('publish__name','publish__city')[0]
    # 查詢全部在北京的出版社出版的書籍,獲得一個列表
    book_list = Publish.objects.filter(city='北京').values('book__name')
    book_list2 = Book.objects.filter(publish__city='北京').values('name')
  • 多對多操做、聚合查詢aggregate( )、分組查詢annotate()
from django.db import models

class Book(models.Model):
    '''書籍'''
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    pub_data = models.DateField()
    # 一對多,一本書只能有一個出版社
    publish = models.ForeignKey('Publish',on_delete=models.CASCADE)
    # 多對多,一本書能夠有多個做者,系統會自動建立第三張表:app_book_authors
    #可是不能直接對這張表進行操做,由於沒有它的類
    authors = models.ManyToManyField('Author')
    def __str__(self):
        return self.name

class Publish(models.Model):
    '''出版社'''
    #一對多的外鍵要加在多的哪一張表裏,一本書只能有一個出版社
    name = models.CharField(max_length=30)
    city = models.CharField(max_length=30)
    def __str__(self):
        return self.name

class Author(models.Model):
    '''做者'''
    name = models.CharField(max_length=20)
    age = models.IntegerField(default=20)
    def __str__(self):
        return self.name
models
from django.shortcuts import render,HttpResponse
from app.models import *
from django.db.models import Avg,Min,Max,Count,Sum

#------------------增--------------------

    #方式一
    book_obj = Book.objects.get(id=4)
    book_obj.authors.add(1)
    book_obj.authors.add(*[2,3,])
    #方式二
    book_obj = Book.objects.get(id=4)
    author_objs = Author.objects.all()
    book_obj.authors.add(*author_objs)

#------------------刪--------------------

    # 先獲得一個書籍對象
    book_obj = Book.objects.get(id=4)
    #刪除
    book_obj.authors.remove(3)
    book_obj.authors.remove(*[1,2,])
    #清空
    book_obj.authors.clear()

#------------------改-------------------- 
  
    #重置,以設置的爲準,這裏列表不加*,已經有的不動,新內容裏沒有的就刪除
    book_obj.authors.set([1,2,3])

#------------------查-------------------- 

    #正向,找到id=2的書籍的全部做者
    book_obj = Book.objects.get(id=2)
    book_obj.authors.all()
    #方式二
    Book.objects.filter(id=2).values('authors__name')
    # 反向,找到做者的全部書籍
    author_obj = Author.objects.get(id=1)
    author_obj.book_set.all()
    #全部書籍名稱和對應的做者名
    book_list = Book.objects.all().values('name','authors__name')
    #列舉做者charlie的全部書籍
    Book.objects.filter(authors__name="charlie")
    
#-----------------聚合函數aggregate()------------------

    #求全部書籍的平均價格
    ret = Book.objects.all().aggregate(Avg('price'))#{'price__avg': 122.0}
    # 也能夠自定義名稱,結果:{'avg_price': 122.0}
    ret = Book.objects.all().aggregate(avg_price=Avg('price'))
    #做者Charlie出的全部書,Count參數能夠是book表裏任意個字段,只是查有幾條記錄,查誰都同樣
    ret = Book.objects.filter(authors__name='charlie').aggregate(Count('name'))

#-----------------分組函數annotate---------------------

    #按做者名分組,求每一個做者全部書籍的價格總和
    ret = Book.objects.values('authors__name').annotate(Sum('price'))
    '''
    <QuerySet [{'authors__name': 'charlie', 'price__sum': 366}, 
    {'authors__name': 'alex', 'price__sum': 88}, 
    {'authors__name': 'james', 'price__sum': 188}]>
    '''
    #求每一個出版社的最低價格的書
    ret = Publish.objects.values('name').annotate(Min('book__price'))
  • 對於多對多的操做,咱們也能夠手動建立第三張表,可是這樣查詢起來更加麻煩,因此不推薦;
#去掉authors = models.ManyToManyField('Author')
#建立新類
class Book_Author(models.Model):
    #第三張表
    book = models.ForeignKey('Book',on_delete=models.CASCADE)
    author = models.ForeignKey('Author',on_delete=models.CASCADE)
    def __str__(self):
        return self.name

#添加
Book_Author.objects.create(book_id=3,author_id=1)
# 利用對象查詢
book_obj = Book.objects.get(id=2)
author_name = book_obj.book_author_set.all()[0].author
print(author_name)
#雙下劃線查詢
book_list1 = Author.objects.filter(name='charlie').values('book_author__book')
print(book_list1)
book_list2 = Book.objects.filter(book_author__author__name='charlie').values('name')
print(book_list2)
  • F查詢和Q查詢
from django.db.models import F,Q

#F 使用查詢條件的值,專門取對象中某列值的操做
#給全部的書價格加10
Book.objects.all().update(price=F('price') + 10)

# Q 進行條件或的查詢,| 或,知足任意條件
ret = Book.objects.filter(Q(price=110)|Q(name='GO'))
#~ 非,不知足條件
ret = Book.objects.filter(~Q(name='GO'))
#書籍名稱中包含某個字母
ret = Book.objects.filter(Q(name__contains='J'))
#Q查詢和關鍵字查詢結合使用,Q查詢必定要放前面
ret = Book.objects.filter(Q(price=110),name='GO')
print(ret)
#Q(name__startswith='P')  書籍名稱以字母P開頭

三、補充整理

  • 跨多張表查詢,能夠連續使用雙下劃線

  • ForeignKey()和ManyToManyField()均可以經過related_name參數修改沒有外鍵的那個表裏隱藏的字段
    class Book(models.Model):
        '''書籍'''
        name = models.CharField(max_length=20)
        price = models.IntegerField()
        pub_data = models.DateField()
        authors = models.ForeignKey('Author',on_delete=models.CASCADE)
    class Author(models.Model):
        '''做者'''
        name = models.CharField(max_length=20)
        age = models.IntegerField(default=20)
        country = models.ForeignKey('Country',on_delete=models.CASCADE)
    class Country(models.Model):
        '''國家'''
        name = models.CharField(max_length=20)
    #全部中國籍做者出的全部書籍
    Book.objects.filter(authors__country__name='China')
    #正向查找:Book.objects.filter(authors__name='charlie')
    #反向查找:
        obj = Authors.objects.filter(name='charlie').first()
        obj.book_set.all()
    #沒有外鍵的表裏其實隱藏了一個字段:類名_set,也能夠修改這個字段名
    class Book(models.Model):
        authors = models.ForeignKey('Author',on_delete=models.CASCADE,related_name='book')
    obj = Authors.objects.filter(name='charlie').first()
    book_list = obj.book.all()#做者對應的全部書籍對象查詢集[obj(name,price,..),obj(name,price,..),]

四、惰性機制

所謂惰性機制:Publisher.objects.all()或者.filter()等都只是返回了一個QuerySet(查詢結果集對象),它並不會立刻執行sql,而是當調用QuerySet的時候才執行。

QuerySet特色:<1>  可迭代的;<2>  可切片

QuerySet的高效使用:

<1>Django的queryset是惰性的
   Django的queryset對應於數據庫的若干記錄(row),經過可選的查詢來過濾。例如,下面的代碼會得
   到數據庫中名字爲‘Dave’的全部的人:person_set = Person.objects.filter(first_name="Dave")
   上面的代碼並無運行任何的數據庫查詢。你可使用person_set,給它加上一些過濾條件,或者將它傳給某個函數,
   這些操做都不會發送給數據庫。這是對的,由於數據庫查詢是顯著影響web應用性能的因素之一。

<2>要真正從數據庫得到數據,你能夠遍歷queryset或者使用if queryset,總之你用到數據時就會執行sql.
   爲了驗證這些,須要在settings里加入 LOGGING(驗證方式)
        obj=models.Book.objects.filter(id=3)
        for i in obj:
            print(i)

        if obj:
           print("ok")

<3>queryset是具備cache的
   當你遍歷queryset時,全部匹配的記錄會從數據庫獲取,而後轉換成Django的model。這被稱爲執行
  (evaluation).這些model會保存在queryset內置的cache中,這樣若是你再次遍歷這個queryset,
   你不須要重複運行通用的查詢。可是若是你修改了數據庫,就須要再查詢一次,不然你查到的仍是上次的
  緩存數據。 obj=models.Book.objects.filter(id=3) for i in obj: print(i) models.Book.objects.filter(id=3).update(title="GO") obj_new=models.Book.objects.filter(id=3) for i in obj: print(i) #LOGGING只會打印一次 <4>簡單的使用if語句進行判斷也會徹底執行整個queryset而且把數據放入cache,雖然你並不須要這些 數據!爲了不這個,能夠用exists()方法來檢查是否有數據: obj = Book.objects.filter(id=4) # exists()的檢查能夠避免數據放入queryset的cache。 if obj.exists(): print("hello world!") <5>當queryset很是巨大時,cache會成爲問題 處理成千上萬的記錄時,將它們一次裝入內存是很浪費的。更糟糕的是,巨大的queryset可能會鎖住系統 進程,讓你的程序瀕臨崩潰。要避免在遍歷數據的同時產生queryset cache,可使用iterator()方法 來獲取數據,處理完數據就將其丟棄。 objs = Book.objects.all().iterator() # iterator()能夠一次只從數據庫獲取少許數據,這樣能夠節省內存 for obj in objs: print(obj.name) #BUT,再次遍歷沒有打印,由於迭代器已經在上一次遍歷(next)到最後一次了,沒得遍歷了 for obj in objs: print(obj.name) 固然,使用iterator()方法來防止生成cache,意味着遍歷同一個queryset時會重複執行查詢。因此使 用iterator()的時候要小心,確保你的代碼在操做一個大的queryset時沒有重複執行查詢。 總結: queryset的cache是用於減小程序對數據庫的查詢,在一般的使用下會保證只有在須要的時候纔會查詢數據庫。 使用exists()和iterator()方法能夠優化程序對內存的使用。不過,因爲它們並不會生成queryset cache,可能 會形成額外的數據庫查詢。總之一句話,若是查詢數據量巨大時,使用迭代器;若是數據量很小,並且又須要重複查詢
時,使用查詢集。

2、admin

admin是django強大功能之一,它能從數據庫中讀取數據,呈如今頁面中,進行管理。默認狀況下,它的功能已經很是強大,若是你不須要複雜的功能,它已經夠用,可是有時候,一些特殊的功能還須要定製,好比搜索功能,下面這一系列文章就逐步深刻介紹如何定製適合本身的admin應用。

若是你以爲英文界面很差用,能夠在setting.py 文件中修改如下選項:

LANGUAGE_CODE = 'en-us'  #LANGUAGE_CODE = 'zh-hans'

使用django admin 則須要如下步驟:

  • 建立後臺管理員
  • 配置url
  • 註冊和配置django admin後臺管理頁面

一、建立後臺管理員

python manage.py createsuperuser

二、配置後臺管理url

url(r'^admin/', admin.site.urls)

三、註冊和配置django admin 後臺管理頁面

a、註冊:在admin中執行以下配置

from django.contrib import admin
   
from app01 import  models
   
admin.site.register(models.UserType)
admin.site.register(models.UserInfo)
admin.site.register(models.UserGroup)
admin.site.register(models.Asset)

b、設置數據表字段的顯示名稱

#方式一
class UserType(models.Model):
    name = models.CharField(max_length=50)
   #meta表示數據庫中顯示的信息
    class Meta:
        db_table = 'UserType'            #數據庫表名
        verbose_name = '用戶類型'     #數據庫字段名
        verbose_name_plural = '用戶類型'

#方式二
#修改models
class Book(models.Model):
    '''書籍'''
    name = models.CharField(max_length=20,verbose_name='名稱')
    price = models.IntegerField('價格')

c、自定義頁面展現

from django.contrib import admin
from app.models import *

#自定製admin類
class BookAdmin(admin.ModelAdmin):
    #不能夠顯示多對多的關聯字段,注意這些都是元組,注意末尾的逗號
    list_display = ('id','name','price','pub_date',)
    #可編輯
    # list_editable = ('name','price','pub_date',)
    # 設置字段可垂直搜索
    filter_horizontal = ('authors',)
    #設置每頁顯示的條目
    # list_per_page = 2
    #根據字段搜索,關聯字段加__,三個字段中重複的部分都會被搜索到
    search_fields = ('id','name','publish__name',)
    #根據字段過濾
    list_filter = ('pub_date','publish',)
    #根據字段排序,能夠多個字段,依次排序,‘-id’表示降序排
    ordering = ('id',)
    #添加書籍時隱藏字段,列表裏必須是元組
    fieldsets = [
        #默認顯示name,fieldes是固定的,後面跟列表
        (None,{'fields':['name',]}),
        #將下列字段以摺疊的方式顯示
        ('other information',{'fields':['price','pub_date','publish'],'classes':['collapse',]}),
    ]

#將模型註冊到admin
admin.site.register(Book,BookAdmin)

四、註冊medel類到admin的兩種方式:

  • 使用register的方法
admin.site.register(Book,MyAdmin)
  • 使用register的裝飾器
@admin.register(Book)

五、裝飾器使用方法

from django.contrib import admin
from app01.models import *
# Register your models here.

# @admin.register(Book)#----->單給某個表加一個定製
class MyAdmin(admin.ModelAdmin):
    list_display = ("title","price","publisher")
    search_fields = ("title","publisher")
    list_filter = ("publisher",)
    ordering = ("price",)
    fieldsets =[
        (None,               {'fields': ['title']}),
        ('price information', {'fields': ['price',"publisher"], 'classes': ['collapse']}),
    ]

admin.site.register(Book,MyAdmin)
admin.site.register(Publish)
admin.site.register(Author)

3、Form組件

一、django中的Form通常有一下幾種功能:

  • 生成HTML標籤

  • 驗證用戶輸入:ajax和form表單提交

  • HTML Form提交保留上次提交數據

  • 初始化頁面顯示內容

二、用form表單提交的基本流程

a、在views.py中建立一個類:F(forms.Form),若是正規操做,應該是在APP中新建一個forms.py文件,再導入;

b、類中建立字段(包含正則表達式和HTML插件)

c、用戶以GET方式請求:

  • 把obj=F()發送到前端,這裏不用傳入任何參數

  •  {{ obj.user }}...前端自動生成input標籤

d、用戶以POST方式請求:

  • obj = Form1(request.POST),對象中包含用戶輸入信息和錯誤信息

  • 先驗證用戶輸入是否正確,若是正確就跳轉頁面,若是錯誤就發送obj

  • {{ obj.errors.user.0 }} 前端顯示錯誤信息

補充:

#mark_safe,不用加safe就能夠直接渲染HTML標籤
from django.utils.safestring import mark_safe
txt = mark_safe("<input type='text'/")

# novalidate忽略瀏覽器的驗證
<form action="/add_user/" method="POST" novalidate>

# 上傳文件記得加上enctype
<form action="/test/" method="POST" enctype="multipart/form-data" novalidate>

# form表單自動生成input標籤,編輯時將數據庫查詢內容做爲input標籤默認值
data = UserInfo.objects.filter(id=nid).first()
obj = F1({'username':data.username,'email':data.email})

實例:

  • views.py中建立Form類和函數
from django.shortcuts import render,redirect,HttpResponse
from django import forms
from django.forms import fields

class Form1(forms.Form):
    user = fields.CharField(
        min_length=6,
        max_length=18,
        required=True,
        error_messages={
            'required':'用戶名不能爲空',
            'min_length':'用戶名過短',
            'max_length':'用戶名太長',
        }
    )
    pwd = fields.CharField(
        min_length=10,
        required=True,
        error_messages = {
            'required': '密碼不能爲空',
            'min_length': '密碼過短',
        }
    )
    age = fields.IntegerField(
        required=True,
        error_messages={
            'required': '年齡不能爲空',
            'invalid': '必須爲數字',
        }
    )
    email = fields.EmailField(
        min_length=8,
        required=True,
        error_messages={
            'required': '郵箱不能爲空',
            'invalid': '格式錯誤'
        }
    )

def f1(request):
    if request.method == 'GET':
        #自動生成input標籤
        obj = Form1()
        return render(request,'f1.html',{'obj': obj})
    if request.method == 'POST':
        obj = Form1(request.POST)
        #驗證是否成功
        if obj.is_valid():
            #若是成功,打印用戶提交的數據,跳轉
            print("驗證成功",obj.cleaned_data)
            return redirect('http://www.baidu.com')
        else:
            print("驗證失敗", obj.errors)
            return render(request, 'f1.html', {'obj': obj})
  • f1.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {% load staticfiles %}
</head>
<body>
    <form action="/f1.html" method="POST" id="fm">
        <p>用戶名{{ obj.user }}{{ obj.errors.user.0 }}</p>
        <p>密碼{{ obj.pwd }}{{ obj.errors.pwd.0 }}</p>
        <p>年齡{{ obj.age }}{{ obj.errors.age.0 }}</p>
        <p>郵箱{{ obj.email }}{{ obj.errors.email.0 }}</p>
        <input type="submit" value="提交"/>
        <input type="button" value="ajax提交" id="ajaxSubmit"/>
    </form>
</body>
</html>

三、建立Form類時,主要涉及到 【字段】 和 【插件】,字段用於對用戶請求數據的驗證,插件用於自動生成HTML;

  • 經常使用的Django內置字段
Field
    required=True,               是否容許爲空
    widget=None,                 HTML插件
    label=None,                  用於生成Label標籤或顯示內容
    initial=None,                初始值
    help_text='',                幫助信息(在標籤旁邊顯示)
    error_messages=None,         錯誤信息 {'required': '不能爲空', 'invalid': '格式錯誤'}
    show_hidden_initial=False,   是否在當前插件後面再加一個隱藏的且具備默認值的插件(可用於檢驗兩次輸入是否一直)
    validators=[],               自定義驗證規則
    localize=False,              是否支持本地化
    disabled=False,              是否能夠編輯
    label_suffix=None            Label內容後綴

#字符串
CharField(Field)
    max_length=None,             最大長度
    min_length=None,             最小長度
    strip=True                   是否移除用戶輸入空白

#整型
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值

#浮點型,十進制小數
DecimalField(IntegerField)
    max_value=None,              最大長度
    min_value=None,              最小長度
    max_digits=None,             總長度
    decimal_places=None,       小數位長度

#下拉框
ChoiceField(Field)
    choices=(),                選項,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默認select插件
    label=None,                Label內容
    initial=None,              初始值
    help_text='',              幫助提示

TypedChoiceField(ChoiceField)
    coerce = lambda val: val   對選中的值進行一次轉換
    empty_value= ''            空值的默認值

#多選框
MultipleChoiceField(ChoiceField)

TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   對選中的每個值進行一次轉換
    empty_value= ''            空值的默認值

#時間
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12

#郵箱
EmailField(CharField)

#自定製正則表達式
RegexField(CharField)
    regex,                            自定製正則表達式
    max_length=None,            最大長度
    min_length=None,            最小長度
    error_message=None,         忽略,錯誤信息使用 error_messages={'invalid': '...'}

#上傳文件
FileField(Field)
    allow_empty_file=False     是否容許空文件

ImageField(FileField)      
    ...
    注:須要PIL模塊,pip3 install Pillow
    以上兩個字典使用時,須要注意兩點:
        - form表單中 enctype="multipart/form-data"
        - view函數中 obj = MyForm(request.POST, request.FILES)

#IP
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,若是是::ffff:192.0.2.1時候,可解析爲192.0.2.1, PS:protocol必須爲both才能啓用

#數據庫操做相關字段
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查詢數據庫中的數據
    empty_label="---------",   # 默認空顯示內容
    to_field_name=None,        # HTML中value的值對應的字段
    limit_choices_to=None      # ModelForm中對queryset二次篩選
     
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
  • 實例:
#views.py

from django.shortcuts import render
from django import forms
from django.forms import fields,widgets

class TestForm(forms.Form):
    user = fields.CharField(
        required=True,
        min_length=3,
        max_length=12,
        error_messages={'required':'不能爲空',},
        #widgets定製HTML插件
        # widget=widgets.Select,
        label = '用戶名',
        label_suffix='->',
    )
    age = fields.IntegerField(max_value=100,min_value=18)
    email = fields.EmailField()
    #FileField和ImageField功能同樣
    img = fields.FileField()
    #生成下拉框,initial默認選中,或者obj = TestForm({'city':2})也能夠默認選擇
    city = fields.ChoiceField(
        choices=[(1,'北京'),(2,'上海'),],
        initial=2,
    )
    #多選框
    hobby = fields.MultipleChoiceField(
        choices=[(1,'足球'),(2,'籃球'),],
        initial=[1,2],
    )
    country = fields.TypedChoiceField(
        choices=[(1, '中國'), (2, '美國'), ],
        initial=2,
        #將傳入的參數作一個數據類型轉換 'country': 2
        coerce=lambda x:int(x),
        empty_value='null',#空值的默認值
    )

def test(request):
    if request.method == 'GET':
        obj = TestForm()
        return render(request, 'test.html',locals())
    else:
        #上傳的文件在request.FILES中
        obj = TestForm(request.POST,request.FILES)
        obj.is_valid()
        print(obj.cleaned_data)
        return render(request,'test.html',locals())

#test.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {#上傳文件記得加上enctype#}
    <form action="/test/" method="POST" enctype="multipart/form-data" novalidate>
        {% csrf_token %}
        <p>{{ obj.user.label }}{{ obj.user }}</p>
        <p>{{ obj.age.label }}{{ obj.age }}</p>
        <p>{{ obj.email.label }}{{ obj.email }}</p>
        <p>{{ obj.img.label }}{{ obj.img }}</p>
        <p>{{ obj.city.label }}{{ obj.city }}</p>
        <p>{{ obj.hobby.label }}{{ obj.hobby }}</p>
        <p>{{ obj.country.label }}{{ obj.country }}</p>
        <input type="submit" value="提交"/>
    </form>
{#    能夠所有自動生成一個標籤,可是頁面排版沒法控制,不建議使用#}
{#    {{ obj.as_p }}#}
</body>
</html>

四、Django內置插件

  • 調用方法
#widgets定製HTML插件,每個字段都有本身的默認插件,還能夠定製屬性
widget=widgets.TextInput(attrs={'class':'c1'}),
  • Django內置插件
TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget

五、經常使用的選擇插件

# 單radio,值爲字符串
user = fields.CharField(
    initial=2,
    widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
)

# 單radio,值爲字符串
user = fields.ChoiceField(
    choices=((1, '上海'), (2, '北京'),),
    initial=2,
    widget=widgets.RadioSelect
)

# 單select,值爲字符串
user = fields.CharField(
    initial=2,
    widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
)

# 單select,值爲字符串
user = fields.ChoiceField(
    choices=((1, '上海'), (2, '北京'),),
    initial=2,
    widget=widgets.Select
)

# 多選select,值爲列表
user = fields.MultipleChoiceField(
    choices=((1,'上海'),(2,'北京'),),
    initial=[1,],
    widget=widgets.SelectMultiple
)

# 單checkbox
user = fields.CharField(
    widget=widgets.CheckboxInput()
)

# 多選checkbox,值爲列表
user = fields.MultipleChoiceField(
    initial=[2, ],
    choices=((1, '上海'), (2, '北京'),),
    widget=widgets.CheckboxSelectMultiple
)

六、在使用選擇標籤時,須要注意choices的選項是須要從數據庫中獲取,可是因爲是靜態字段 ***獲取的值沒法實時更新***,那麼須要自定義__init__構造方法,方法給widget.choices從新從數據庫中取值,這樣每次頁面一刷新就實例化一次form對象,就會執行一次init函數,也就是會從數據庫中取一次值。

方式一:

from django.shortcuts import render
from django import forms
from django.forms import fields,widgets
from app.models import *#models.py中的類
class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    email = models.EmailField(max_length=32)
    def __str__(self):
        return self.username

#建立自定義form
class LoveForm(forms.Form):
    price = fields.IntegerField()
    user_id = fields.IntegerField(widget=widgets.Select)
    #靜態字段 獲取的值沒法實時更新,須要自定義構造方法
    def __init__(self,*args,**kwargs):
        # super必須在上面,它拷貝了全部的靜態字段,下面才能去內部取字段
        super(LoveForm,self).__init__(*args,**kwargs)
        self.fields['user_id'].widget.choices = UserInfo.objects.values_list('id','username')

def love(request):
    obj = LoveForm()
    return render(request,'love.html',locals())

方式二:

#使用django提供的ModelChoiceField和ModelMultipleChoiceField字段來實現

from django.forms.models import ModelChoiceField

class LoveForm(forms.Form):
    user_id2 = ModelChoiceField(
        #這裏沒法顯示數據庫表中的ID之外的字段,須要models中的類加上個__str__方法,返回某個字段
        #雖然同樣能夠實時更新,可是與數據庫的格式關係太大,不能靈活顯示每一個字段,不建議使用
        queryset=UserInfo.objects.all()
    )

七、使用ajax提交

  • 沒法自動跳轉頁面,就是在views函數中設置redirect,ajax也不會遵從,須要在前端ajax的回調函數本身使用js代碼跳轉,window.location.href = 'http://www.baidu.com'

  • 錯誤信息須要本身顯示到頁面,obj.errors 類型<class 'django.forms.utils.ErrorDict'>繼承dict,因此用json.dumps()不會報錯。

class AjaxForm(forms.Form):
    price = fields.IntegerField()
    user_id = fields.IntegerField(
        widget=widgets.Select(choices=[(1, '中國'), (2, '美國'), ],)
    )

def ajax(request):
    if request.method == 'GET':
        #自動生成input標籤,並無開始作驗證
        obj = AjaxForm()
        return render(request,'ajax.html',{'obj': obj})
    if request.method == 'POST':
        import json
        response = {'status':True,'msg':None}
        #並無開始作驗證
        obj = AjaxForm(request.POST)
        #is_valid作的驗證
        if obj.is_valid():
            print("驗證成功",obj.cleaned_data)
            #ajax沒法自動跳轉,須要在前端手動設置
            return HttpResponse(json.dumps(response))
        else:
            #<class 'django.forms.utils.ErrorDict'>繼承dict
            print("驗證失敗", obj.errors,type(obj.errors))
            response['status'] = False
            response['msg'] = obj.errors
            return HttpResponse(json.dumps(response))
  • ajax.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {% load staticfiles %}
</head>
<body>
    <form action="/ajax/" method="POST" novalidate id="fm">
        {% csrf_token %}
        {{ obj.as_p }}
        <input type="button" value="ajax提交" id="ajaxSubmit"/>
    </form>
    <script src="{% static 'js/jquery-1.12.4.js' %}"></script>
    <script>
        $(function () {
            $('#ajaxSubmit').click(function () {
                $.ajax({
                    url:'/ajax/',
                    type:'POST',
                    data:$('#fm').serialize(),
                    dataType:'JSON',
                    success:function (arg) {
                        if(arg.status){
                            window.location.href = 'http://www.baidu.com';
                        }
                        console.log(arg);
                    }
                })
            })
        })
    </script>
</body>
</html>

八、在根據源碼流程自定義方法

  • clean_字段名,只能驗證該字段本身的內容;
from django.core.exceptions import ValidationError

class AjaxForm(forms.Form):
    username = fields.CharField()
    user_id = fields.IntegerField(
        widget=widgets.Select(choices=[(1, '中國'), (2, '美國'), ],)
    )
    #自定義方法clean_字段名,
    #必須返回值self.cleaned_data['username']
    #若是出錯:raise ValidationError('用戶名已存在')
    def clean_username(self):
        v = self.cleaned_data['username']
        if UserInfo.objects.filter(username=v).count():
            #若是用戶信息中有一個字段錯了,總體就錯誤,顯示具體錯誤的詳細信息
            raise ValidationError('用戶名已存在')
        return v
    def clean_user_id(self):
        return self.cleaned_data['user_id']
  • 自定義self.clean()方法,對總體錯誤驗證,錯誤信息放在__all__中,前端調用方法arg.msg.__all__
    def clean(self):
        '''
        obj.errors錯誤信息
        {
            __all__:[],
            username:[],
            user_id:[],
        }
        '''
        # Django的總體錯誤信息放在__all__中
        v1 = self.cleaned_data.get('username')
        v2 = self.cleaned_data.get('user_id')
        if v1=='charlie' and v2==1:
            raise ValidationError('總體錯誤信息')
        return self.cleaned_data
  • __all__源碼分析
from django.core.exceptions import NON_FIELD_ERRORS

#這裏添加錯誤的時候k就是None
self.add_error(None, e)
#可是None又被替換成__all__,因此前端須要經過.all來取
NON_FIELD_ERRORS = '__all__'

# js代碼
console.log(arg.msg.__all__);

#HTML模板
#模板語言裏不支持雙下劃線格式,因此用obj.non_field_errors.0 

九、is_valid()驗證表單時,一共給咱們留了三個鉤子

# 自定義方法名必須是clean_字段名
    def _clean_fields(self):
        for name, field in self.fields.items():
            # value_from_datadict() gets the data from the data dictionaries.
            # Each widget type knows how to retrieve its own data, because some
            # widgets split data over several HTML fields.
            if field.disabled:
                value = self.get_initial_for_field(field, name)
            else:
                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError as e:
                self.add_error(name, e)
# 同時驗證多個字段或者是聯合字段,返回總體錯誤信息,放在__all__中
    def _clean_form(self):
        try:
            cleaned_data = self.clean()
        except ValidationError as e:
            self.add_error(None, e)
        else:
            if cleaned_data is not None:
                self.cleaned_data = cleaned_data
# 沒有捕捉異常的代碼,因此這個方法不能出錯誤
    def _post_clean(self):
        """
        An internal hook for performing additional cleaning after form cleaning
        is complete. Used for model validation in model forms.
        """
        pass

 

十、擴展:ModelForm

在使用Model和Form時,都須要對字段進行定義並指定類型,經過ModelForm則能夠省去From中字段的定義

from django import forms
from crm import models

class CustomerForm(forms.ModelForm):
    class Meta:
        model = models.CustomerInfo
        # fields = ['name','consultant','status']
        # 全部字段
        fields = '__all__'
           
        widgets = {
            'email' : forms.PasswordInput(attrs={'class':"alex"}),
        }

  實例:

# forms.py

class EnrollmentForm(forms.ModelForm):
    """審覈學員報名信息"""
    def __new__(cls, *args, **kwargs):
        # cls.base_fields獲取數據庫表裏的全部字段名
        for field_name in cls.base_fields:
            field_obj = cls.base_fields.get(field_name)
            # 定製生成的標籤的class屬性
            field_obj.widget.attrs.update({'class': 'form-control'})
            # 只讀標籤不可編輯
            if field_name in cls.Meta.readonly_fields:
                field_obj.widget.attrs.update({'disabled': 'true'})
        return forms.ModelForm.__new__(cls)

    class Meta:
        model = models.StudentEnrollment
        fields = '__all__'
        exclude = ['contract_approved_date']
        readonly_fields = ['contract_agreed',]

    def clean(self):
        """負責驗證disabled字段的值是否被改動了"""
        if self.errors:
            # 全局的錯誤信息,經過customer_form.errors調用
            raise forms.ValidationError(("Please fix errors before re-submit."))
        if self.instance.id is not None:
            # 說明這是一個被修改過的表單,須要驗證disabled字段
            for field in self.Meta.readonly_fields:
                old_field_val = getattr(self.instance,field)  # 數據庫裏的數據
                form_val = self.cleaned_data.get(field)   # 表單裏提交的數據
                if old_field_val != form_val:
                    # 給單個字段添加錯誤信息
                    self.add_error(field,"Readonly Field:field should be '{value}',not'{new_value}'".
                                   format(**{'value':old_field_val,'new_value':form_val}))
  • views.py
from django.shortcuts import render,HttpResponse,redirect
from django.contrib.auth.decorators import login_required  # 必須登錄才能看到頁面
from crm import forms

@login_required
def contract_audit(request,enrollment_id):
    '''學員報名審覈頁'''
    enrollment_obj = models.StudentEnrollment.objects.filter(id=enrollment_id).first()
    if request.method == 'POST':
        enrollment_form = forms.EnrollmentForm(instance=enrollment_obj,data=request.POST)
        if enrollment_form.is_valid():
            enrollment_form.save()
            # 報名成功後,把學員加入數據庫,stu_obj = (<Student: 鐵錘>, True)
            stu_obj = models.Student.objects.get_or_create(customer=enrollment_obj.customer)[0] # 加入學生表
            stu_obj.class_grades.add(enrollment_obj.class_grade_id)  # 加入班級表
            stu_obj.customer.status = 1   # 修改狀態
            stu_obj.save()
            # 修改報名表,是否審覈經過,和審覈經過時間
            enrollment_obj.contract_approved = True
            enrollment_obj.contract_approved_date = datetime.now()
            enrollment_obj.save()
            # 給用戶發郵件
            return redirect('/kingadmin/crm/customerinfo/%s/change/'%enrollment_obj.customer.id)
    else:
        # 建立form表單,發到前端
        customer_form = forms.CustomerForm(instance=enrollment_obj.customer)
        enrollment_form = forms.EnrollmentForm(instance=enrollment_obj)
    return render(request,'crm/contract_audit.html',locals())

 

4、Cookie

cookie的工做原理是:由服務器產生內容,瀏覽器收到請求後保存在本地;當瀏覽器再次訪問時,瀏覽器會自動帶上cookie,這樣服務器就能經過cookie的內容來判斷這個是「誰」了。

一、獲取Cookie:

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    參數:
        default: 默認值
           salt: 加密鹽
        max_age: 後臺控制過時時間

二、設置Cookie:

rep = HttpResponse(...) 或 rep = render(request, ...)
 
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密鹽',...)
    參數:
        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傳輸
        httponly=False    只能http協議傳輸,沒法被JavaScript獲取(不是絕對,底層抓包能夠獲取到也能夠被覆蓋)
  • 因爲cookie保存在客戶端的電腦上,因此,JavaScript和jquery也能夠操做cookie。
<script src='/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });

三、應用

from django.shortcuts import render,redirect
import datetime

def login(request):
    if request.method == 'POST':
        name = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if name == 'charlie' and pwd == '123':
            # redirect render 都有返回值,是一個字典,裏面有鍵值對'sessionid':'5225rffg5hh5'
            ret = redirect('/index/')
            #給cookie添加一個鍵值對,下次再來發送請求的時候會帶着它來,
            #若是和這個同樣,就不用從新登錄了,前提是在同一個客戶端登錄
            #設置cookie有效時間5秒
            ret.set_cookie('username',name,max_age=5)
            #設置有效期三天
            ret.set_cookie('username',name,expires=datetime.datetime.utcnow()
                                                   +datetime.timedelta(days=3))
            return ret
    return render(request,'login.html')

def index(request):
    if request.COOKIES.get('username',None):
        name = request.COOKIES.get('username',None)
        return render(request,'index.html',locals())
    else:
        return redirect('/login/')

 

5、Session

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

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

  • 數據庫(默認)
  • 緩存
  • 文件
  • 緩存+數據庫
  • 加密cookie

一、數據庫Session

Django默認支持Session,而且默認是將Session數據存儲在數據庫中,即:django_session 表中。
 
a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默認)
     
    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,默認修改以後才保存(默認)
 
 
 
b. 使用
 
    def index(request):
        # 獲取、設置、刪除Session中數據
        request.session['k1']
        request.session.get('k1',None)
        request.session['k1'] = 123
        request.session.setdefault('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的隨機字符串
        request.session.session_key
 
        # 將全部Session失效日期小於當前日期的數據刪除
        request.session.clear_expired()
 
        # 檢查 用戶session的隨機字符串 在數據庫中是否
        request.session.exists("session_key")
 
        # 刪除當前用戶的全部Session數據
        request.session.delete("session_key")
 
        request.session.set_expiry(value)
            * 若是value是個整數,session會在些秒數後失效。
            * 若是value是個datatime或timedelta,session就會在這個時間後失效。
            * 若是value是0,用戶關閉瀏覽器session就會失效。
            * 若是value是None,session會依賴全局session失效策略。
View Code

二、緩存Session

a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的緩存別名(默認內存緩存,也能夠是memcache),此處別名依賴緩存的設置
 
 
    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,默認修改以後才保存
 
b. 使用同上

三、文件Session

a. 配置 settings.py
    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    SESSION_FILE_PATH = None                                    # 緩存文件路徑,若是爲None,則使用tempfile模塊獲取一個臨時地址tempfile.gettempdir()                                                            # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
    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,默認修改以後才保存
 
b. 使用同上

四、緩存+數據庫Session

數據庫用於作持久化,緩存用於提升效率
a. 配置 settings.py
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'      # 引擎
b. 使用同上

五、加密cookie Session

a. 配置 settings.py 
    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
b. 使用同上

更多參考:猛擊這裏 和 猛擊這裏

六、cookie 和Session的結合使用的兩種方式

  • 存儲在服務端:經過cookie存儲一個session_id,而後具體的數據則是保存在session中。若是用戶已經登陸,則服務器會在cookie中保存一個session_id,下次再次請求的時候,會把該session_id攜帶上來,服務器根據session_idsession庫中獲取用戶的session數據。就能知道該用戶究竟是誰,以及以前保存的一些狀態信息。這種專業術語叫作server side session

  • session數據加密,而後存儲在cookie中。這種專業術語叫作client side sessionflask採用的就是這種方式,可是也能夠替換成其餘形式。

七、應用

數據庫session,須要執行python manage.py makemigrations命令,須要Django幫咱們建立session表,用來保存session內容,不然會報錯;

cookie是一個字典,裏面默認有兩個鍵值對:‘sessionid’,‘csrftoken’,session是一個字典對象,兩個均可以用request.session['username']=name方法來添加鍵值對;

刪除session:del request.session[key]

from django.shortcuts import render,redirect
import datetime

def login(request):
    if request.method == 'POST':
        name = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if name == 'charlie' and pwd == '123':
            #cookie + session
            request.session['is_login']=True
            request.session['user']=name
            return redirect('/index/')
    return render(request,'login.html')

def index(request):
    #cookie + session,加上None防止找不到報錯
    if request.session.get('is_login',None):
        name = request.session.get('user',None)
        return render(request,'index.html',locals())
    else:
        return redirect('/login/')

6、分頁

一、代碼實現簡單的上一頁、下一頁

#index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <ul>
        {% for row in user_list %}
            <li>{{ row.name }} - {{ row.age }}</li>
        {% endfor %}
    </ul>
    <a href="/index.html?p={{ prev_page }}">上一頁</a>
    <a href="/index.html?p={{ next_page }}">下一頁</a>
</body>
</html>

#views.py

from django.shortcuts import render

USER_LIST = []
for i in range(1,1000):
    temp = {'name':'root' + str(i),'age':i}
    USER_LIST.append(temp)

def index(request):
    #簡單的分頁
    per_page_count = 10
    current_page = int(request.GET.get('p'))
    #p=1  索引0-10
    #p=2  索引10-20
    #http://127.0.0.1:8005/index.html?p=1
    start = (current_page-1)*per_page_count
    end = current_page*per_page_count
    user_list = USER_LIST[start:end]
    if current_page <= 1:
        prev_page = 1
        next_page = current_page + 1
    else:
        prev_page = current_page - 1
        next_page = current_page + 1
    return render(request,'index.html',locals())

二、Django內置分頁+擴展

  -- Django自帶的內置分頁只能顯示上一頁和下一頁按鈕,不能顯示中間的頁碼,因此這裏須要擴展一下

  • views.py
from django.shortcuts import render
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger

USER_LIST = []
for i in range(1,230):
    temp = {'name':'root' + str(i),'age':i}
    USER_LIST.append(temp)

#擴展自帶分頁
class CustomPaginator(Paginator):
    def __init__(self,current_page,per_pager_num,*args,**kwargs):
        #當前頁
        self.current_page = int(current_page)
        #每頁最多顯示多少頁碼
        self.per_pager_num = int(per_pager_num)
        super(CustomPaginator,self).__init__(*args,**kwargs)
    def page_num_range(self):
        #若是總頁數小於每頁最多顯示頁碼數量,就顯示1-總頁碼
        if self.num_pages < self.per_pager_num:
            return range(1,self.num_pages+1)
        #若是總頁數有不少
        part = int(self.per_pager_num/2)
        if self.current_page <= part:
            return range(1,self.per_pager_num+1)
        #最後一頁只顯示最後10個頁碼便可
        if (self.current_page+part) > self.num_pages:
            return range(self.num_pages-self.per_pager_num+1,self.num_pages+1)
        return range(self.current_page-part,self.current_page+part+1)

def index1(request):
    #django自帶分頁
    current_page = request.GET.get('p')
    #Paginator對象,每頁顯示10個頁碼,10條數據
    paginator = CustomPaginator(current_page,11,USER_LIST,10)
    try:
        #Page對象
        posts = paginator.page(current_page)
    except PageNotAnInteger:
        #若是輸入不是數字,就顯示第一頁
        posts = paginator.page(1)
    except EmptyPage:
        #若是輸入爲空或數字過大,就顯示最後一頁
        posts = paginator.page(paginator.num_pages)
    return render(request,'index1.html',locals())
  • index1.html,在templates下新建include/pager.html,把全部關於分頁的代碼單獨放到一個文件中,之後能夠隨時調用
#注意:這裏使用include引用了文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <ul>
        {% for row in posts %}
            <li>{{ row.name }} - {{ row.age }}</li>
        {% endfor %}
    </ul>
    {% include 'include/pager.html' %}
</body>
</html>
  • 引用pager.html文件
<a href="/index1.html?p=1">首頁</a>
{% if posts.has_previous %}
    <a href="/index1.html?p={{ posts.previous_page_number }}">上一頁</a>
{% endif %}
{% for i in paginator.page_num_range %}
    {% if i == posts.number %}
        <a style="font-size: 20px;color: blue;" href="/index1.html?p={{ i }}">{{ i }}</a>
    {% else %}
        <a href="/index1.html?p={{ i }}">{{ i }}</a>
    {% endif %}
{% endfor %}
{% if posts.has_next %}
    <a href="/index1.html?p={{ posts.next_page_number }}">下一頁</a>
{% endif %}
<span>
    [{{ posts.number }}/{{ paginator.num_pages }}]
</span>
<a href="/index1.html?p={{ paginator.num_pages }}">尾頁</a>

二、自定義分頁

  —   分頁功能在每一個網站都是必要的,對於分頁來講,其實就是根據用戶的輸入計算出應該在數據庫表中的起始位置,而且須要在頁面上顯示分頁的頁面。如:[上一頁][1][2][3][4][5][下一頁]

  • 一、設定每頁顯示數據條數

  • 二、用戶輸入頁碼(第一頁、第二頁...)

  • 三、設定顯示多少頁號

  • 四、獲取當前數據總條數

  • 五、根據設定顯示多少頁號和數據總條數計算出,總頁數

  • 六、根據設定的每頁顯示條數和當前頁碼,計算出須要取數據表的起始位置

  • 七、在數據表中根據起始位置取值,頁面上輸出數據

  • 八、輸出分頁html,如:[上一頁][1][2][3][4][5][下一頁]

實例:在APP中建立pager.py

#在APP中建立pager.py

class Pagination(object):
    def __init__(self,total_count,current_page,per_page_item_num=20,max_page_num=7):
        #數據總條目
        self.total_count = total_count
        #當前頁
        try:
            v = int(current_page)
            if v <= 0:
                v = 1
            self.current_page = v
        except Exception as e:
            self.current_page = 1
        #每頁顯示條目
        self.per_page_item_num = per_page_item_num
        #每頁最多顯示頁碼
        self.max_page_num = max_page_num

    def start(self):
        return (self.current_page-1)*self.per_page_item_num

    def end(self):
        return self.current_page*self.per_page_item_num

    @property
    def num_pages(self):
        # 總頁數
        a,b = divmod(self.total_count,self.per_page_item_num)
        if b == 0:
            return a
        return a+1

    def page_num_range(self):
        #若是總頁數小於每頁最多顯示頁碼數量,就顯示1-總頁碼
        if self.num_pages < self.max_page_num:
            return range(1,self.num_pages+1)
        #若是總頁數有不少
        part = int(self.max_page_num/2)
        if self.current_page <= part:
            return range(1,self.max_page_num+1)
        #最後一頁只顯示最後10個頁碼便可
        if (self.current_page+part) > self.num_pages:
            return range(self.num_pages-self.max_page_num+1,self.num_pages+1)
        return range(self.current_page-part,self.current_page+part+1)

    def page_str(self):
        page_list = []
        first_page = '<li><a href="/index2.html?p=1">首頁</a></li>'
        page_list.append(first_page)
        if self.current_page == 1:
            prev_page = '<li><a href="#">上一頁</a>'
        else:
            prev_page = '<li><a href="/index2.html?p=%s">上一頁</a></li>'%(self.current_page-1)
        page_list.append(prev_page)
        for i in self.page_num_range():
            if i == self.current_page:
                temp = '<li class="active"><a href="/index2.html?p=%s">%s</a></li>' % (i, i)
            else:
                temp = '<li><a href="/index2.html?p=%s">%s</a></li>'%(i,i)
            page_list.append(temp)
        if self.current_page == self.num_pages:
            next_page = '<li><a href="#">下一頁</a></li>'
        else:
            next_page = '<li><a href="/index2.html?p=%s">下一頁</a></li>' % (self.current_page + 1)
        page_list.append(next_page)
        last_page = '<li><a href="/index2.html?p=%s">尾頁</a></li>'%self.num_pages
        page_list.append(last_page)
        return ''.join(page_list)
  • 建立views函數
from django.shortcuts import render
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger

USER_LIST = []
for i in range(1,600):
    temp = {'name':'root' + str(i),'age':i}
    USER_LIST.append(temp)

def index2(request):
    from app.pager import Pagination
    current_page = request.GET.get('p')
    page_obj = Pagination(600,current_page)
    data = USER_LIST[page_obj.start():page_obj.end()]
    return render(request,'index2.html',locals())
  • 建立index2.html,這裏須要引入bootstrap的一個組件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"/>
</head>
<body>
    <ul>
        {% for row in data %}
            <li>{{ row.name }} - {{ row.age }}</li>
        {% endfor %}
    </ul>
    <ul class="pagination">
        {{ page_obj.page_str|safe }}
    </ul>
</body>
</html>

總結,分頁時須要作三件事:

  • 建立處理分頁數據的類

  • 根據分頁數據獲取數據

  • 輸出分頁HTML,即:[上一頁][1][2][3][4][5][下一頁]

7、序列化

  序列化就是把對象轉換成能夠保存在本地文件中的數據類型,關於Django中的序列化主要應用在將數據庫中檢索的數據返回給客戶端用戶,特別的Ajax請求通常返回的爲Json格式。

一、Python中dumps就是序列化,loads就是反序列化;

二、JavaScript中

     對象轉換成字符串  - -  str  =  JSON.stringify({ 'k' : 'v' })
     字符串轉換成對象  - -  dict  =  JSON.parse( str )

三、三種狀況

QuerySet內部是:

  • 對象(用all,filter獲取數據):serializers.serializer('json',QuerySet)

  • 字典(用values獲取):list(QuerySet)

  • 元組(用values_list獲取):list(QuerySet)

from django.core import serializers
import json def get_data(request): response = {'status': True, 'data': None} try: user_list = UserInfo.objects.all() # serializers只能序列化queryset對象,轉換爲字符串 response['data'] = serializers.serialize('json', user_list) #若是queryset內部是字典或元組,values和values_list獲取,道理同樣 user_list = UserInfo.objects.values('id','username') #只須要把外部的queryset變成列表形式,就能夠直接json序列化了 response['data'] = list(user_list) except Exception as e: response['status'] = False ret = json.dumps(response) return HttpResponse(ret)

 四、因爲json.dumps時沒法處理datetime日期,因此能夠經過自定義處理器來作擴展,如:

import json
from datetime import date
from datetime import datetime

class JsonCustomEncoder(json.JSONEncoder):
    def default(self, field):
        if isinstance(field, datetime):
            return field.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(field, date):
            return field.strftime('%Y-%m-%d')
        elif isinstance(field, Response):
            return field.__dict__    # 對象以字典的方式輸出
        else:
            return json.JSONEncoder.default(self, field)

class Response:
    def __init__(self):
        self.status = True
        self.data = 'charlie'

data = {
    'k1': datetime.now(),
    'k2': Response(),
}
ds = json.dumps(data, cls=JsonCustomEncoder)
print(ds)
# {"k1": "2019-03-08 16:18:28", "k2": {"status": true, "data": "charlie"}}
相關文章
相關標籤/搜索