目錄css
pip3 install django
1.建立django程序html
django-admin startproject <proecjt名> 例如: django-admin startproject mysite
2.建立project中的app文件目錄前端
python manage.py startapp <app名> 例如: python manage.py startapp app01
3.進入程序目錄python
cd mysite
4.啓動socket服務端,等待用戶發送請求(不寫端口默認8000)mysql
python manage.py runserver 127.0.0.1:8080
5.其餘命令jquery
python manage.py createsuperuser # 用於django admin建立超級用戶 python manage.py makemigrations # 記錄對models.py文件的改動 python manage.py migrate # 將改動映射到數據庫
經過IDE(pycharm等)建立django project,以上命令均自動執行git
目錄結構介紹web
- mysite(~/PycharmProjects/mysite) |- app01/ |- admin.py # Django自帶管理配置後臺 |- models.py # 寫類,根據類建立數據庫表 |- test.py # 單元測試 |- views.py # 視圖函數 |- apps.py # 配置文件 |- mysite/ |- __init__.py |- setting.py |- urls.py |- wsgi.py |- static/ |- templates/ |- utils/ |- manage.py
在settings.py 文件中,指定template文件路徑ajax
TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], ... },
在settings.py 文件中,添加STATICFILES_DIRS,注意要加逗號正則表達式
STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), # 逗號 )
1.建立mysql數據庫
2.修改配置settings.py
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'dbname', 'USER': 'root', 'PASSWORD': '', 'HOST': 'localhost', 'PORT': 3306, } }
3.修改項目同名文件下__init__.py文件
pymsql替換內部的myqldb,修改django默認鏈接mysql第三方工具
import pymysql pymysql.install_as_MySQLdb()
路由就是在urls.py文件中的url和函數的對應關係
# urls.py文件 from django.conf.urls import url from django.contrib import admin # 對應關係 urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^login/', login), # 對應關係 ]
其中,
案例
from django.conf.urls import url from django.contrib import admin from django.shortcuts import HttpResponse,render,redirect def login(request): print(request.GET) if request.method == "GET": return render(request,'login.html') else: # 用戶POST提交的數據(請求體) u = request.POST.get('user') p = request.POST.get('pwd') if u == 'root' and p == '123': # 登陸成功 return redirect('/index/') else: # 登陸失敗 return render(request,'login.html',{'msg': '用戶名或密碼錯誤'}) urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^login/', login), url(r'^index/', index), ]
url(r'^index$', views.index),
基於正則的路由能夠傳遞參數,根據傳遞方式能夠分爲,靜態路由和動態路由兩種
咱們都知道url能夠經過get方式傳參數,這種方式被稱做靜態路由
url(r'^index/?id=12', views.index), # 對應views.py中的def index(request),能夠根據request.GET.get('id')獲取參數值
路由中的url部分其實就是正則表達式,因此可使用正則表達式替代get傳參數,傳遞方式有兩種 傳遞位置參數 和 傳遞命名參數
位置參數對參數順序有要求
url(r'^index/(\w+)/', views.index), # (\w+)能夠匹配任意字母或數字或下劃線或漢字做爲位置參數,對參數順序有要求 # 匹配的參數會被def index(request,a)中的參數a接收
參數經過參數名對應,因此對順序沒要求
url(r'^index/(?P<a2>\d+)/(?P<a1>\d+)/', views.index), # (?P<a1>\d+)能夠指定匹配任意數字而且參數名爲a1 # 匹配的參數被def index(request,a1,a2)中的參數a1接收,對參數順序沒有要求
注意:位置傳遞和命名傳遞不可混用,因此使用要統一
由於url就是爲正則表達式,因此url前部匹配成功後就會調用函數,因此爲了完美匹配能夠在url結尾跟/或者$
url(r'^index$', views.index), # 只會匹配index url(r'^index', views.index), # 會所有匹配index開頭字符串
SEO(Search Engine Optimization,搜索引擎優化)靜態網頁的搜索權重高於動態網頁,因此經過僞造動態網頁爲靜態網頁後綴達到權重增長的目的
url(r'^index/(/w+).html$', views.index), # 表示頁面以.html結尾
因此,url正則表達式方式 + 僞靜態 = 動態路由
不出現‘?id=..’get傳參的寫法,能夠提升SEO的搜索權重
若是用戶輸入url不存在,能夠制定到默認頁面
url(r'^', <指定函數名>),
url(r'^manage/(?P<title>\w*)', views.manage,{'name':'kirs'}),
根據據app對路由規則進行分類
一個Django項目中可能會有衆多業務線(衆多app),若是一都使用同一個路由系統可能會致使url重名問題,因此能夠根據每一個app創建本身的urls.py文件,經過項目的路由系統來找到各自app的路由對應關係
1.在app01中新建urls.py,並import本身的views
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index.html$', views.index), ]
2.在項目的urls.py中import包include,
在項目的urls.py中寫入app01url和include內容
from django.conf.urls import url,include urlpatterns = [ url(r'^app01/', include('app01.urls')), ]
這樣,url來了,項目路由會先找對應app,找到後,去對應app中的urls中找對應關係
給url與函數關係命名
未來在視圖函數中能夠根據別名反生成url
- 1.首先,urls.py中 url(r'^index/', views.index, name='name1'), - 2.在views.py中import包reverse from django.urls import reverse url = reverse('name1') # 獲取到/index/
反生成帶特定 位置參數的url
- 1.urls.py中 url(r'^index/(\w+)/', views.index, name='name1'), - 2.在views.py中 from django.urls import reverse url = reverse('name1',args=(abc,)) # 獲取到/index/abc/
反生成帶特定 命名參數的url
- 1.urls.py中 url(r'^index/(?P<b1>\w+)/', views.index, name='name1'), - 2.在views.py中 from django.urls import reverse url = reverse('name1',kwargs={'b1':abcd,}) # 獲取到/index/abcd/
未來在html頁面中能夠根據別名取得url地址
- 1.urls.py中 url(r'^index/', views.index, name='name1'), - 2.a標籤取url - 原來寫法 <a href="/index/"></a> - 如今寫法 <a href="{% url "name1" %}"></a> # 效果就是/index/
動態傳遞參數怎麼辦?
- 1.urls.py中 url(r'^index/(\w+)/(\w+)/', views.index, name='name1'), - 2.a標籤取url - 原來寫法: <a href="/index/(\w+)/(\w+)/"></a> - 如今寫法: <a href="{% url "name1" 參數1 參數2 %}"></a> # 效果就是/index/參數1/參數2 # <a href="{% url "name1" x y %}"></a> # 效果就是/index/peter/alex
PS:url與函數關係命名能夠用於權限管理簡化存儲內容:數據庫存儲別名便可,就不用存儲很長的url地址
模版的建立過程,對於模版,其實就是讀取模版(其中嵌套着模版標籤),而後將 Model 中獲取的數據插入到模版中,最後將信息返回給用戶。
其中,返回方式:HttpResponse\render\redirect
#views.py from django.shortcuts import HttpResponse, render, redirect # 視圖函數 def login(request): # return HttpResponse('massage') # 直接獲取內容返回用戶 return render(request, 'login.html', {'key':'value'}) # 自動找到templates路徑下的login.html文件,讀取內容並返回給用戶 # return redirect('/login/') # 重定向文件
返回參數能夠是
字符串:'name': 'alex', 列表:'users':['tom','kirs'], 字典:'user_dict':{'k1': '123','k2':'234'}, 列表套字典: 'user_list_dict': [ {'id':1, 'name': 'alex', 'email': 'alex3714@163.com'}, {'id':2, 'name': 'alex2', 'email': 'alex3714@1632.com'}, {'id':3, 'name': 'alex3', 'email': 'alex3713@1632.com'}, ]
html 中取值,直接經過.取值
{{users.0}} {{user.1}} {{user_dict.k1}} {{user_dict.k2}}
案例
def index(request): # return HttpResponse('Index') return render( request, 'index.html', { 'name': 'alex', 'users':['李志','李傑'], 'user_dict':{'k1': 'v1','k2':'v2'}, 'user_list_dict': [ {'id':1, 'name': 'alex', 'email': 'alex3714@163.com'}, {'id':2, 'name': 'alex2', 'email': 'alex3714@1632.com'}, {'id':3, 'name': 'alex3', 'email': 'alex3713@1632.com'}, ] } )
{{ }} {% for i in [列表|字典] %} {{i.列1}} {{i.列2}} ... {% endfor%} {% if 條件%} ... {% else %} ... {% endif %}
以前說過的母版就是將公用內容存到html,其餘子板extends便可
{% extends "base.html" %}
相似於python中的import
能夠自定義html小組件,將小組件倒入其餘頁面應用,而且一個頁面呢能夠屢次導入
{% include 'pub.html' %} # pub.html爲組件
#pub.html <div> <div class='...'>{{ name }}<> <div>
PS:而且組件中能夠接受後臺參數
在模版引擎中傳入函數名,自動執行函數,不用加括號
經過後臺傳來的字典具備方法
keys
- 遍歷key {% for i in user_list.keys%} {{i}} {% endfor%}
values
- 遍歷value {% for i in user_list.values%} {{i}} {% endfor%}
items
- 遍歷key和value {% for k,v in user_list.items%} {{k}} {{v}} {% endfor%}
內置方法經過|方法名執行
- 將字符串大寫 {{ name|upper }}
1.在app建立一個名爲templatetags文件(名稱不可改)
2.建立任意templatetags/xx.py文件,函數增長裝飾器@register.filter
#xx.py from django import template register = temlate.Library() @register.filter def my_upper(value): return value.upper()
3.導入文件:在html文件中導入以前建立的 xx.py 文件名
{% load xxx %}
4.使用:
{{name|my_upper}}
5.settings.py註冊app
INSTALLED_APPS = ( ... 'app', )
建立模版自定義函數simple_filter最多隻能有兩個參數,可是能夠作條件判斷
- 最多兩個參數,方式: {{第一個參數|函數名稱:"第二個參數"}} def my_upper(value,arg): return value+arg {{name|my_upper:'abc'}} - 能夠作條件判斷
在以上建立過程當中,若是函數增長裝飾器@register.simple_tag
@register.simple_tag def my_lower(value): return value.lower()
使用時參數個數無限制
- 參數無限制:{% 函數名 參數 參數 ...%} def my_lower(value,arg1,arg2,...): return value + arg1 + arg2 +... {% my_lower name 'abc' 'bcd' ...%}
request.method - 用戶請求方法,GET或者POST
request.GET - 獲取GET方式提交的數據
request.POST — 獲取POST方式提交的數據
request.GET.get()
request.POST.get()
request.POST.getlist() - 獲取列表
其中,客戶端POST方式提交,request一樣能夠獲取GET數據,反之不能夠
路由系統經過反射方式匹配method(post、get、put等)。
用戶發來請求,url匹配到類,將method看成參數傳入類,經過getattr找到對應方法。
路由系統中,url對應函數就是FBV
urls.py
- 直接跟方法名 url(r'^login.html$',views.login),
views.py
- 類名須要繼承View,能夠定義get、post等方法 def login(request): return render(request, "login.html")
一樣,url也能夠對應類,那麼這就是CBV
urls.py
- 類名後跟.as_view() url(r'^login.html$',views.Login.as_view()),
views.py
- 類名須要繼承View,能夠定義get、post等方法 from django.views import View class Login(View): def get(self, request): return render(request, "login.html") def post(self, request): print(request.POST.get("user")) return HttpResponse("login.post")
from表單只有get、post方法,ajax不只有post和get方法,還有不少其它方法,如put、delete等
PS:約定俗成:get查 post建立 put更新 delete刪除
在CBV的get、post方法執行以前View內部會先執行dispatch方法
def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
因此咱們能夠重寫dispatch方法,起到相似裝飾器功能
裝飾get、post等方法
def dispatch(self,request,*args,**kwargs): #執行get、post前代碼 func = super(Login,self).dispatch(request,*args,**kwargs) #執行get、post後代碼 return func
django 中的中間件(middleware),在django中,中間件其實就是一個類,在請求到來和結束後,django會根據本身的規則在合適的時機執行中間件中相應的方法。
請求--> 中間件類1方法1--> 中間件類2方法1--> ...--> 路由繫系統and視圖函數 ...<-- 中間件類2方法2<-- 中間件類1方法2<-- 響應<-- 其中, 方法1都是 process_request(), 方法二都是 process_response()
在django項目的settings模塊中,有一個 MIDDLEWARE_CLASSES 變量,其中每個元素就是一箇中間件
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中的中間件順序執行,若是其中一箇中間件在process_request()出現錯誤,就會直接執行這個中間件的process_response(),請求中止繼續執行,返回錯誤信息
請求--> 中間件類1方法1--> 中間件類2方法1-出錯 | 中間件類2方法2<-- 中間件類1方法2<-- 響應<--
應用環境:於用戶認證,替代auth裝飾器,省去給所有視圖函數添加裝飾器
1.新建py文件,倒入入MiddlewareMixin
2.類中函數:process_request、process_response
3.註冊中間件py文件
# m1.py from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class M1(MiddlewareMixin): def process_request(self,request): print('m1.process_request') def process_view(self, request, callback, callback_args, callback_kwargs): print('m1.process_view') # response = callback(request,*callback_args,**callback_kwargs) # return response def process_response(self,request,response): print('m1.process_response') return response def process_exception(self, request, exception): print('m1.process_exception') def process_template_response(self,request,response): """ 視圖函數的返回值中,若是有render方法,才被調用 :param request: :param response: :return: """ print('m1.process_template_response') return response
其中,
1.process_request 不能有return,若是return,中間件再也不繼續執行,直接返回
2.process_response 必須須要return 返回值
3.proccess_view 匹配到對應函數,並不會執行,
4.proccess_exception 異常處理,只有視圖函數錯誤纔會執行,中間件會從最後一個類中的proccess_exception開始向前執行,proccess_exception所有執行結束後,再從最後一個類中的process_response開始向前返回
5.proccess_template_view 視圖函數有render,才執行,對全部請求,或一部分請求作批量處理,應用:記錄日誌功能
# settings.py MIDDLEWARE = [ ... m1.Middle1, ]
Ajax請求返回須要後臺經過HttpResponse返回json.dumps字典數據,這個功能能夠封裝成另外一個類調用render,利用中間件中的proccess_template_view實現
# views.py from django.shortcuts import render,HttpResponse,redirect class JSONResponse: def __init__(self,req,status,msg): self.req = req self.status = status self.msg = msg def render(self): import json ret = { 'status': self.status, 'msg':self.msg } return HttpResponse(json.dumps(ret)) def test(request): # print('test') # return HttpResponse('...') ret = {} return JSONResponse(request,True,"錯誤信息")
PS:在django1.10中,若是process_request中有異常或者return,中間件會從當前類的process_response開始返回
在django1.7或者1.8中,若是process_request中有異常或者return,中間件會從最後一個類中的process_response開始向前返回
Django對數據庫操做使用關係對象映射(Object Relational Mapping,簡稱ORM),通常不使用原生sql(除非遇到複雜sql)
Django中經過ORM操做數據庫
ORM操做表:能夠建立表、修改表、刪除表(SQLAlchemy卻沒法修改表)
ORM操做行:增、刪、改、查
Django的ORM默認使用的數據庫是sqllite3,咱們要使用mysql就須要利用第三方工具鏈接數據庫
Django默認鏈接mysql的第三方工具是MySQLDB,可是python3再也不支持MySQLDB
因此得修改django默認鏈接mysql方式爲pymysql
1.建立數據庫
2.修改配置settings.py
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'dbname', 'USER': 'root', 'PASSWORD': '', 'HOST': 'localhost', 'PORT': 3306, } }
3.修改項目同名文件下__init__.py文件
pymsql替換內部的myqldb,修改django默認鏈接mysql第三方工具
import pymysql pymysql.install_as_MySQLdb()
1.寫model:
models.py文件
from diango.db import models class UserInfo(models.Model): uid = models.BigAutoField(primary_key=True) # int自增主鍵,默認不寫django自動增長 username = models.CharField(max_length=32) #char(32) password = models.CharField(max_length=64) age = models.IntegerField(default=1) # int,默認值爲1,也能夠null=True
2.app寫入配置
settings.py,將app寫入
INSTALLED_APPS = [ ... 'app01', ]
3.執行命令
python3 manage.py makemigrations python3 manage.py migrate
PS:每次修改表結構和表結構後經過命令更新便可
4.更新表
models.py文件
class UserGroup(Models.Model): title = models.CharField(max_length=32) class UserInfo(models.Model): ... ug = models.ForeignKey("UserGroup", null=True) # 制定外鍵表名,能夠爲空,建立外鍵
CharField(Field) - 字符類型 - 必須提供max_length參數, max_length表示字符長度
EmailField(CharField) - email IPAddressField(Field) - ip URLField(CharField) - url SlugField(CharField) - url並提供驗證支持 字母、數字、下劃線、鏈接符(減號) UUIDField(Field) - 提供對UUID格式的驗證 FilePathField(Field) - file並提供讀取文件夾下文件的功能 - 參數: 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 上傳圖片的寬度保存的數據庫字段名(字符串) CommaSeparatedIntegerField(CharField) - 字符串類型,格式必須爲逗號分割的數字
DateTimeField(DateField) - 日期+時間格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] DateField(DateTimeCheckMixin, Field) - 日期格式 YYYY-MM-DD
IntegerField() - 整數列(有符號的) -2147483648 ~ 2147483647 SmallIntegerField(IntegerField) - 小整數 -32768 ~ 32767 BigIntegerField(IntegerField) - 長整型(有符號的) -9223372036854775808 ~ 9223372036854775807 FloatField(Field) - 浮點型 DecimalField(Field) - 10進制小數 - 參數: max_digits,小數總長度 decimal_places,小數位長度
BooleanField(Field) - 布爾值類型 NullBooleanField(Field): - 能夠爲空的布爾值
枚舉沒有本身的字段,是經過利用其它字段實現枚舉功能
color_list = ( (1,'黑色'), (2,'白色'), (3,'藍色') ) color = models.IntegerField(choices=color_list)
null = True - 爲空 default = '123' - 默認值 primary_key = True - 主鍵 max_length = 12 - 最大長度 unique_for_date時間建立索引 db_index = True - 單列索引 unique = True - 單列惟一索引 class Meta: # 聯合惟一索引 unique_together = ( ('email','ctime'), ) # 聯合索引 index_together = ( ('email','ctime'), )
多對多以及如何創建聯合惟一索引
1.關係表Love中寫Class Meta
Class Love(models.Model): b = models.ForeignKey('Boy') # b = models.ForeignKey(to='Boy',to_field='id') #關聯哪一個表哪一個字段 g = models.ForeignKey('Girl') Class Meta: # 創建惟一索引 unique_together = [ ('b','g'), ]
2.經過在Boy或Girl表中寫 ManyToManyField(另外一張表名)
class Boy(models.Model): name = models.CharField(max_length=32) m = models.ManyToManyField('Girl')
這樣django會自動生成兩表的關係表boy_m,
可是boy_m表中只有三個字段:id、boy_id、girl_id,由於關係表boy_m是自動生成,沒有對應類,因此不能直接經過獲取關係表對象操做,那麼要如何操做呢?
對關係表boy_m的操做須要先獲取一個Boy對象或者Girl對象
- 獲取對象 obj = models.Boy.objects.filter(name='alex').first() - 增長 obj.m.add(2) # 給alex增長girl_id=2的數據 - 批量增長 obj.m.add(1,2,...) - 列表增長 l = [1,2,...] obj.m.add(*l) - 刪除 obj.m.remove(1) # 給alex刪除girl_id=1這條數據 - 批量刪除,同添加 obj.m.remove(1,2,...) - 列表刪除,同添加 l = [1,2,...] obj.m.remove(*l) - 重製 obj.m.set([1,2,...]) # 將本來的關係所有刪除,插入心的關係 - 查詢 girl_list = obj.m.all() # 查詢到的是 QuerySet列,其中是Girl object - 條件查詢 girl_list = obj.m.filter(nick='alice') - 清空 obj.m.clear() # 清空與alex有關所有數據
那麼對於Girl對象如何操做關係表呢?經過 小寫表名_set實現
- 獲取對象 obj = models.Girl.objects.filter(nick='alice').first() - 查詢 boy_list = obj.boy_set.all()
方式1、方式二能夠連用,ManyToManyField中須要though和through_fields,可是方式二中的用法只有查詢和清空有效
#models.py class Boy(models.Model): name = models.CharField(max_length=32) m = models.ManyToManyField('Girl',through="Love",through_fields=('b','g',)) # 查詢和清空 class Girl(models.Model): nick = models.CharField(max_length=32) # m = models.ManyToManyField('Boy') class Love(models.Model): b = models.ForeignKey('Boy') g = models.ForeignKey('Girl') class Meta: unique_together = [ ('b','g'), ]
數據庫中一個表中能夠根據id兩兩關聯
例如,有UserInfo表,讓其中id進行關聯,如何實現自關聯?
新建關係表 + ForeignKey
# models.py文件 class U2U(models.Model): # 關係表 b = models.ForeignKey('UserInfo', related_name='boys') g = models.ForeignKey('UserInfo', related_name='girls') # b = models.ForeignKey('UserInfo', related_query_name='boys') # g = models.ForeignKey('UserInfo', related_query_name='girls')
其中,要用到反向查找別名替換,如何讓設置別名?這裏涉及知識點
related_query_name = 'b' related_name = 'a'
這樣從關係表中查詢UserInfo表中的信息時,使用別名便可
obj.a_set.all() # 經過related_query_name起名 obj.a.all() # 經過related_name起名
以上,經過ForeignKey + 關係表實現了 自關聯
查詢到與id爲1的男生有關係的女生nickname
obj = models.UserInfo.objects.filter(id=1).first() # 找到id爲1的UserInfo對象obj result = obj.boys.all() # 與對象obj有關的全部信息:Querset列表[U2Ud對象] for i in result: print(i.g.nickname) # 從U2U對象正向查找g的nickname
經過ManyToManyField實現
#models.py class UserInfo(models.Model): ... m = models.ManyToManyField('UserInfo')
建立後會生成關係表,其中字段:id、from_userinfo_id、to_userinfo_id,其中,from_userinfo_id存放男生id,to_userinfo_id存放女生id
查詢與id=1的男生有關係的女生姓名
obj = models.UserInfo.objects.filter(id=1).first() result = obj.m.all() for i in result: print(i.nickname)
其中,obj.m是將obj做爲from_userinfo_id,查詢所有的to_userinfo_id
查詢到與id爲4的女生有關係的男生nickname
obj = models.UserInfo.objects.filter(id=4).first() result = obj.userinfo_set.all() for i in result: print(i.nickname)
其中,userinfo_set是將obj做爲to_userinfo_id,查詢所有的from_userinfo_id
應用於評論信息存儲表
id | news_id | conent | user | reply_id |
---|---|---|---|---|
1 | 1 | 什麼鬼 | alex | null |
2 | 1 | 什麼什麼鬼 | egon | 1 |
3 | 1 | sb | eric | 2 |
#models.py class Comment(models.Model): news_id = models.IntegerField() conent = models.CharField(max_length=100) user = models.CharField(max_length=32) reply_id = models.ForeignKey('Comment', null=True, blank=True)
views.py
def sql(request): from app01 import models # 增長數據 models.UserGroup.objects.create(title="銷售部") models.UserInfo.objects.create(username='root', password='123', age="20", ug_id=1) # 查詢 user_list = models.UserInfo.objects.all() # 拿到QuerySet列表,其中存儲的對像(一行就是一個對象) #models.UserInfo.objects.all().first() # 取到QuerySet中的第一個對象 group_list = models.UserGroup.objects.filter(id=1, title="銷售部") # 條件查詢 group_list = models.UserGroup.objects.filter(id__gt=1) # id>1(_lt小於) print(user_list, group_list) for i in user_list: # 顯示字段 print(i.username, i.age) # 刪除 models.UserInfo.objects.filter(id=2).delete() # 刪除 # 更新 models.UserInfo.objects.filter(id=1).update(title="人事部") # 更新 return HttpResponse("...")
models.UserInfo.objects.all().count()
models.UserInfo.objects.filter(id__gt=1) # id>1 models.UserInfo.objects.filter(id__gte=1) # id>=1 models.UserInfo.objects.filter(id__lt=2) # id<2 models.UserInfo.objects.filter(id__lte=2) # id<=2 models.UserInfo.objects.filter(id__gte=1, id__lt=2) # id>=1,id<2
models.UserInfo.objects.filter(id__in=[1, 2, 4]) # 獲取id在1,2,4中數據 models.UserInfo.objects.exclude(id__in=[1, 2, 4]) # 獲取除id在1,2,4中的數據
查詢字段爲空的數據 ... where ut_id IS NULL
models.UserInfo.objects.filter(ut__isnull=True)
字段是否包含內容 ... where name like '%a%' /... where not (name like '%a%' )
models.UserInfo.objects.filter(name__contains="a") models.UserInfo.objects.filter(name__icontains="a") # 大小寫不敏感 models.UserInfo.objects.exclude(name__icontains="a")
bettwen ... and ...
models.UserInfo.objects.filter(id_range=[1, 3])
... where name like 'a%'
models.UserInfo.objects.filter(name__startswith='a') models.UserInfo.objects.filter(name__istartswith='a')
- 篩選結果按照id ASC排序 models.UserInfo.objects.filter(id__gt=1).order_by("id") - DESC排序 models.UserInfo.objects.filter(id__gt=1).order_by("-id")
PS:order_by("id","name") 先按照id再按照name ASC排序
- import聚合函數 from django.db.models import Count, Sum, Max, Min, Avg - annotate()必須配合values(),values()中是要分組的列 models.UserInfo.objects.values("ut_id").annotate(x=Count('id')) 對應sql: "SELECT ut_id, COUNT(id) AS x FROM UserInfo GROUP BY ut_id"
PS:分組後結果再filter(),相似於group by後的having操做
models.UserInfo.objects.values("ut_id").annotate(x=Count('id')).filter(ut_id__gt=1)
查詢時,主動作連表操做,相似於inner join操做
- select_related(要鏈接表的外鍵) q = models.UserInfo.objects.all().select_related('ut') - 執行原理 " select * from UserInfo inner join UserType on UserInfo.ut = UserType.id " - 取值 for row in q: print(row.name,row.ut.title)
查詢時,不作連表,做屢次單表查詢,實現連表操做目的
- prefetch_related(外鍵) q = models.UserInfo.objects.all().perfetch_related('ut') - 執行原理 " select * from UserInfo " 獲取UserInfo所有ut組成列表[1,2,...] " select * from UserType where id in [1,2,...] " - 取值 for row in q: print(row.id,row.ut.title)
PS:單表查詢比連表操做查詢性能高
經過.query 能夠查看sql語句
result = models.UserInfo.objects.all() print(result.query)
獲取全部的數據對象
條件查詢 條件能夠是:參數,字典,Q
條件查詢 條件能夠是:參數,字典,Q
實現聚合group by 查詢
去重複 models.UserInfo.objects.values('nid').distinct()
排序
構造額外查詢條件或者映射
倒序 models.UserInfo.objects.all().order_by('-id').reverse()
PS:若是存在order_by,reverse則是倒序,若是多個排序則一一倒序
映射中排除某些列 models.UserInfo.objects.defer('name','id') 或者 models.UserInfo.objects.filter(...).defer('name','id')
僅取某個表中的數據 models.UserInfo.objects.only('name','id') 或 models.UserInfo.objects.filter(...).only('name','id')
PS:使用only效率比all高,可是使用only也能夠點出未包括在only中的列,可是效率會比all第,因此,only哪些列就使用哪些列
制定使用的數據庫,參數爲別名,setting.py中設置的
執行原生sql models.UserInfo.objects.raw('select * from userinfo') 若是SQL是其餘表時,必須將名字設置爲當前UserInfo對象的主鍵列名 models.UserInfo.objects.raw('select pid as id from 其餘表') 爲原生SQL設置參數 models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,]) 將獲取的到列名轉換爲指定列名 name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'} Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) 指定數據庫 models.UserInfo.objects.raw('select * from userinfo', using="default")
獲取每行數據爲字典格式
獲取每行數據爲元組
def dates(self, field_name, kind, order='ASC'): # 根據時間進行某一部分進行去重查找並截取指定內容 # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日) # order只能是:"ASC" "DESC" # 並獲取轉換後的時間 - year : 年-01-01 - month: 年-月-01 - day : 年-月-日 models.DatePlus.objects.dates('ctime','day','DESC')
def datetimes(self, field_name, kind, order='ASC', tzinfo=None): # 根據時間進行某一部分進行去重查找並截取指定內容,將時間轉換爲指定時區時間 # kind只能是 "year", "month", "day", "hour", "minute", "second" # order只能是:"ASC" "DESC" # tzinfo時區對象 models.UserInfo.objects.datetimes('ctime','hour',tzinfo=pytz.UTC) models.UserInfo.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai')) """ pip3 install pytz import pytz pytz.all_timezones pytz.timezone(‘Asia/Shanghai’) """
def none(self): # 空QuerySet對象
def aggregate(self, *args, **kwargs): # 聚合函數,獲取字典類型聚合結果 from django.db.models import Count, Avg, Max, Min, Sum result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid')) ===> {'k': 3, 'n': 4}
def count(self): # 獲取個數
def get(self, *args, **kwargs): # 獲取單個對象
def create(self, **kwargs): # 建立對象
def bulk_create(self, objs, batch_size=None): # 批量插入 # batch_size表示一次插入的個數 objs = [ models.DDD(name='r11'), models.DDD(name='r22') ] models.DDD.objects.bulk_create(objs, 10)
def get_or_create(self, defaults=None, **kwargs): # 若是存在,則獲取,不然,建立 # defaults 指定建立時,其餘字段的值 obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})
def update_or_create(self, defaults=None, **kwargs): # 若是存在,則更新,不然,建立 # defaults 指定建立時或更新時的其餘字段 obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})
def first(self): # 獲取第一個
def last(self): # 獲取最後一個
def in_bulk(self, id_list=None): # 根據主鍵ID進行查找 id_list = [11,21,31] models.DDD.objects.in_bulk(id_list)
def delete(self): # 刪除
def update(self, **kwargs): # 更新
def exists(self): # 是否有結果
獲取數據表中列字段進行操做
- import F from django.db.models import F - F("age")+1 age列自加1,更新時取到原來的值 v = models.UserInfo.objects.update(age=F('age') + 1)
PS:v取到被修改的行數
用於構造複雜的查詢條件,兩種用法,對象和方法
- import Q from django.db.models import Q
對象 方式多用於通常條件查詢,相似filter,因此不經常使用
- Q對象,表示一個條件 models.UserInfo.objects.filter(Q(id=1)) - 多條件查詢 models.UserInfo.objects.filter(Q(id=1) | Q(id=2))
方法 方式多用於組合條件查詢,動態查詢
同條件進行or,不一樣條件進行and,循環拼接查詢條件
q1 = Q() q1.connector = "OR" q1.children.append(("id", 1)) q1.children.append(("id", 2)) q1.children.append(("id", 3)) q2 = Q() q2.connector = "OR" q2.children.append(("name", "alex")) q2.children.append(("name", "tom")) q2.children.append(("name", "peter")) con = Q() con.add(q1, "AND") con.add(q2, "AND") result = models.UserInfo.objects.filter(con) print(result) for i in result: print(i.name, i.ut.title)
相似查詢:
select * from UserInfo where (id=1 or id=2 or id =3)AND (name="alex" or name ="tom" or name = "peter")
在model基礎上進行額外的查詢條件以及相關表,排序
extra的參數有select,select_params,where,params,tables,order_by
- select能夠做爲臨時表的查詢結果放在select映射部分,其中x爲別名,select_params配合select使用 models.UserInfo.objects.extra(select={"x": "select count(id) from app01_usertype where id >%s"},select_params=[1, ]) - where能夠做爲where條件部分,params配合where使用 models.UserInfo.objects.extra(where=['id>%s'], params=[1, ]) - tables能夠做爲原錶鏈接的新表,組成笛卡爾積 models.UserInfo.objects.extra(tables=['app01_usertype']) - order_by能夠對原表排序 models.UserInfo.objects.extra(order_by=['-id'])
django原生sql已幫咱們鏈接數據庫,獲取遊標
- 導入connection利用現成連接 from django.db import connection, connections - 經過鏈接獲取遊標 cursor = connection.cursor() # 默認鏈接setting.py中的default DATABASES - 能夠選擇其它數據庫 # cursor = connections['default'].cursor() # default是選擇setting.py中的DATABASES,能夠根據須要變換數據庫 - sql語句 cursor.execute("select * from app01_userinfo where id = %s", [1, ]) - fetchone或者fetchall v = cursor.fetchone()
對象如何實現連表查詢?
答:經過外鍵實現
經過操做表數據的介紹可知,通常查詢方法有all(),filter(),如此這般查詢到的結果是QuerySet列表,列表中存放的是對象(一行數據就是一個對象)
經過values()查詢到的結果是QuerySet列表,列表中存放的是字典
經過values_list()查詢到的結果是QuerySet列表,列表中存放的是元組
- 正向操做 本表中有外鍵的,直接經過 外鍵列名.字段便可 result = models.UserInfo.objects.all() # 獲取UserInfo表中所有QuerySet列表 for i in result: print(i.id, i.name, i.age, i.ut_id, i.ut.title) # 外鍵ut.title - 反向操做 本表主鍵是其它表的外鍵,直接經過 對象.小寫被外鍵的表名_set.all() 便可 obj = models.UserType.objects.all().first() # 獲取UserType表中的第一行QuerySet對象 list = obj.userinfo_set.all() # 小寫被外鍵的表名_set.all() print('用戶類型', obj.id, obj.title) for i in list: print(i.name, i.age)
字典能夠是直接經過values()獲得,也能夠是all().values()或者filter().values()獲得
- 正向操做 - 獲取字典 result = models.UserInfo.objects.all().values('id', 'name', 'ut__title') 或者 result = models.UserInfo.objects.values('id', 'name', 'ut__title') - 經過鍵取值 print(result) for obj in result: print(obj['id'], obj['name'], obj['ut__title']) 其中,ut__title經過外鍵加雙下劃線列名,能夠實現字典跨表查詢 - 反向操做 想要經過字典反向跨表操做,經過 小寫表名__列名 便可(只寫小寫表名爲id) # 字典反向操做 result = models.UserType.objects.values("id", "title", "userinfo", "userinfo__age", "userinfo__name") print(result) for obj in result: print(obj["id"], obj["title"], obj["userinfo"], obj["userinfo__age"], obj["userinfo__name"])
元組能夠是直接經過values_list()獲得,也能夠是all().values_list()或者filter().values_list()獲得
- 正向操做 - 獲取元組 obj = models.UserInfo.objects.all().values_list('id','name','ut_title') 或者 obj = models.UserInfo.objects.values_list('id','name','ut_title') - 經過索引取值 obj[0],obj[1],obj[3] 其中,ut__title經過外鍵加雙下劃線列名,能夠實現元組的跨表查詢 - 反向操做 想要經過元組反向跨表操做,經過 小寫表名__列名 便可(只寫小寫表名爲id) # 元組反向操做 result = models.UserType.objects.values_list("id", "title", "userinfo", "userinfo__age", "userinfo__name") print(result) for obj in result: print(obj[0], obj[1], obj[2], obj[3], obj[4])
PS:
已知:表Boy、表Girl、關係表Love
要求:查詢到與alex有關係的girl
有四種查詢方式
# 方式一:從Boy表獲取alex對象,反向操做Love表獲得所有與alex有關係list,循環list正向取得girl名字,查詢2n+2次 obj = models.Boy.objects.filter(name="alex").first() # 查詢1次 love_list = obj.love_set.all() # 查詢1次 for i in love_list: # 查詢 2n次 print(i.g.nick) # 方式二:Love表字典正向操做取得有alex的list,循環list正向取得girl名字,查詢2n+1次 love_list = models.Love.objects.filter(b__name="alex") # 查詢 1次 for obj in love_list: # 查詢 2n次 print(obj.g.nick) # 方式三:Love表left join Girl表正向操做取的有alex的字典list,循環list取得girl名字,查詢n+1次 love_list = models.Love.objects.filter(b__name='alex').values("g__nick") # 查詢1次 for item in love_list: # 查詢n次 print(item["g__nick"]) # 方式四:Love表inner join Girl表正向操做取的有alex的字典list,循環list取得girl名字,查詢n+1次 obj = models.Love.objects.filter(b__name='alex').select_related("g") # 查詢 1次 for i in obj: # 查詢n次 print(i.g.nick)
PS:推薦後兩種,查詢效率稍高
Django中Form組件主要功能:
1.建立Form類(肯定字段約束)
from django.forms import Form,fields class LoginForm(Form): username = fields.CharField( # html標籤的name屬性 就是 Form類字段名 max_length=18, min_length=6, required=True, error_messages={ 'required': '用戶名不能爲空', 'min_length': '過短了', 'max_length': '太長了', } )
其中,
字段不可爲空: required=True
字段長度: min_length~max_length
錯誤信息:error_messages
2.view函數(使用提交的數據)
# 獲取POST對象 obj = LoginForm(request.POST) # 是否校驗成功 True或False v = obj.is_valid() # 全部錯誤信息 , 對象 obj.errors # 獲取字典格式數據,經過**dic能夠直接操做數據表的增改查詢 obj.cleaned_data
3.html(前端生成標籤)
{{ obj.username }} {{ obj.errors.username.0 }}
PS:
1.cleaned_data字典
對數據表中的某行數據的增、改、查操做可使用字典做爲傳入參數,精簡代碼,因此對數據表的操做能夠直接將obj.cleaned_data字典數據看成參數傳入
dic = {'name':'alex','age':12,...} - 增長 models.Table.objects.create(**dic) - 更新 models.Table.objects.filter(**dic) - 查詢 查詢條件間是and關係 models.Table.objects.filter(**dic)
2.is_valid()原理
1.實例化 LoginForm self.fields{ 'user':正則表達式, 'pwd':正則表達式, ... } 2.循環 self.fields flag = True for k, v self.fields.items(): # k:user,v:正則表達式 if request.POST.get(k)不符合v的正則表達式: flag = False return flag
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, 最小值 FloatField(IntegerField) ... RegexField(CharField) regex, 自定製正則表達式 max_length=None, 最大長度 min_length=None, 最小長度 error_message=None, 忽略,錯誤信息使用 error_messages={'invalid': '...'} DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 總長度 decimal_places=None, 小數位長度 BaseTemporalField(Field) input_formats=None 時間格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 時間間隔:%d %H:%M:%S.%f ... EmailField(CharField) ... FileField(Field) allow_empty_file=False 是否容許空文件 ImageField(FileField) ... 注:須要PIL模塊,pip3 install Pillow 以上兩個字典使用時,須要注意兩點: - form表單中 enctype="multipart/form-data" - view函數中 obj = MyForm(request.POST, request.FILES) URLField(Field) ... BooleanField(Field) ... NullBooleanField(BooleanField) ... ChoiceField(Field) ... choices=(), 選項,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默認select插件 label=None, Label內容 initial=None, 初始值 help_text='', 幫助提示 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 TypedChoiceField(ChoiceField) coerce = lambda val: val 對選中的值進行一次轉換 empty_value= '' 空值的默認值 MultipleChoiceField(ChoiceField) ... TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 對選中的每個值進行一次轉換 empty_value= '' 空值的默認值 ComboField(Field) fields=() 使用多個驗證,以下:即驗證最大長度20,又驗證郵箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field) PS: 抽象類,子類中能夠實現聚合多個字典去匹配一個值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField) input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件選項,目錄下文件顯示在頁面中 path, 文件夾路徑 match=None, 正則匹配 recursive=False, 遞歸下面的文件夾 allow_files=True, 容許文件 allow_folders=False, 容許文件夾 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,若是是::ffff:192.0.2.1時候,可解析爲192.0.2.1, PS:protocol必須爲both才能啓用 SlugField(CharField) 數字,字母,下劃線,減號(連字符) ... UUIDField(CharField) uuid類型 ...
檢測內置字段自己錯誤信息,例如,EmailField檢測的就是字段不符合郵箱地址的格式
fields.CharField(error_messages={'inalid','格式錯誤'})
針對IntegerField字段,字段範圍
number = fields.IntegerField(max_value=100,min_value=15)
生成標籤有關字段
t1 = fields.CharField( label='用戶名', # 標籤的label命名 lable_suffix=':' # label內容後綴 help_text=‘請入電話號碼’, # 標籤幫助信息 initital='username', # input輸入框顯示默認值 disabled=True, # 輸入框是否爲能夠編輯 import django.forms import widgets widget=widgets.Select, # 選擇字段標籤爲select,默認是input ) html 調用 {{ obj.t1.label }}{{ obj.t1.lable_suffix }} {{ obj.t1 }}{{ obj.t1.help_text }} 其實,html 調用 只須要寫以下就能夠了 {{ obj.as_p }}
PS:
有些瀏覽器會自動幫咱們增長校驗,卻掉瀏覽器的校驗給標籤加 novalidate 屬性
<form action='...' method='...' movalidate >...</form>
爲了實現form提交不會刷新頁面,咱們以前使用ajax方法,一樣的咱們能夠經過使用Django的form組件實現
經過ajax不刷新頁面驗證form,而且保留上次輸入內容
views.py
def ajax_login(request): import json ret = {'status': True, 'message': None} obj = loginForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: ret['status'] = False ret['message'] = obj.errors return HttpResponse(json.dumps(ret))
login.html
<script src="/static/jquery-3.2.1.js"></script> <script> function ajaxForm() { $('.c1').remove(); $.ajax({ url: '{% url "ajax_login" %}', type: 'POST', data: $('#f1').serialize(), dataType: 'JSON', success: function (arg) { if (arg.status) { location.href = '{% url index %}' } else { $.each(arg.message, function (index, value) { var tag = document.createElement('span'); tag.innerHTML = value[0]; tag.className = 'c1'; $('#f1').find("input[name='" + index + "']").after(tag) }) } } }) } </script>
login.html
<form method="post" action="{% url 'login' %}" id="f1"> ... <a onclick="ajaxForm();" class="btn btn-default">ajax提交</a> </form>
PS:
1.serialize()提交表單所有
ajax提交form表單時,能夠將form表單內所有數據提交,使用serialize()方法
data: $('#f1').serialize(), # 其中<form id='f1'>
提交原理
user=alex&pwd=123456&scrftoken=...
不刷新頁面驗證form,而且保留上次輸入內容
1.建立form類
from django.shortcuts import render, HttpResponse, redirect from django.forms import Form, fields, widgets from app01 import models class loginfForm(Form): username = fields.CharField( label='用戶名', widget=widgets.Input(attrs={'class': 'form-control'}) ) password = fields.CharField( label='密碼', widget=widgets.Input(attrs={'class': 'form-control', 'type': 'password'}) )
2.view函數處理
def loginf(request): if request.method == 'GET': obj = loginfForm() return render(request, 'loginf.html', {'obj': obj}) else: obj = loginfForm(request.POST) if obj.is_valid(): row = models.User.objects.filter(username=obj.cleaned_data['username'], password=obj.cleaned_data['password']).first() if row: return redirect('/index.html') return render(request, 'loginf.html', {'obj': obj})
3.loginf.html
<form method="post" action="{% url 'loginf' %}" novalidate> {% csrf_token %} {{ obj.username.label }}{{ obj.username }} {{ obj.errors.username.0 }} {{ obj.password.label }} {{ obj.password }} {{ obj.errors.password.0 }} <input type="submit" value="登陸"> </form>
PS:
單選:生成select字段,參數需是元組
widget=widgets.Select(choices=[(1,'北京'),(2,'上海'),...]) widget=widgets.Select(choices=models.City.objects.values_list('id','title'))
生成字段加css樣式
widget=widgets.TextInput(attrs={'class':'form-control'})
生成html標籤,並含有錯誤信息
obj = funcForm(request.POST) 省略了data,本質就是 obj = funcForm(data = request.POST)
只生成html標籤,而且能夠附帶默認值
obj = funcForm({'username':'alex','cls_id':[1,2,3]}) 省略了initial,本質是 obj = funcForm(initial = {'username':'alex','cls_id':[1,2,3]})
1.首先,字段驗證
2.而後,單獨字段方法 clean__參數
3.最後,clean 方法
a.字段自己正則
自定義正則表達式的字段有兩種方式
- 1.自定義驗證規則,做爲內置字段正則的補充 fields.CharField( validators=[] ) - 2.自定義正則字段 fields.RegexField('010\d+') # 010開頭的數字
b.額外的正則
from django.forms import Form,widgets,fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')], )
關鍵字:clean_字段 和 clean
class funcForm(Form): user = fields.CharField() pwd = fields.CharField() email = fields.CharField() # 單個字段校驗 def clean_user(self): v = self.cleand_data['user'] if models.Student.objects.filter(name=v).count(): # 主動異常 raise ValidationError('用戶名已存在') return self.cleaned_data['user'] # 必須返回,不然obj.cleaned_data['user']就被清空了 def clean_pwd(self):) return self.cleaned_data['pwd'] # 必須返回 # 聯合校驗 def clean(self): user = self.cleand_data['user'] email = self.cleand_data['email'] if models.Student.objects.filter(name=user,email=email).count(): raise ValidationError('用戶名和郵箱聯合已經存在') return self.cleaned_data # 必須返回,不然obj.cleaned_data就被清空了
以前使用的IntegerField
cls_id = fields.IntegerField( # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')]) widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'class': 'form-control'}) )
使用ChoiceField
cls_id = fields.ChoiceField( choices = models.Classes.objects.values_list('id','title'), widget = widgets.Select() )
首先嚐試使用字段CharField
cls_id = fields.CharField( widget = widgets.SelectMultiple(choices = models.Classes.objects.values_list('id','title')) ) 這樣獲得的cls_id結果是 { 'cls_id': "['1','2']" } 其中,列表是SelectMultiple結果,可是列表又變成了字符串,那是由於CharField的做用,因此不能使用CharField
因此真正的單選是這樣的,使用ChoiceField,而且要把其中的choices參數提取出來
cls_id = fields.MultipleChoiceField( choices = models.Classes.objects.values_list('id','title'), widget = widgets.SelectMultiple ) 這樣獲得的cls_id結果是 { 'cls_id': ['1','2']}
下拉列表中的數據在程序啓動時加入內存,沒法動態顯示數據庫中的變化,爲了修復這個Bug,刷新沒法動態顯示數據庫內容:
方式一:
class TeacherForm(Form): tname = fields.CharField(min_length=2) xx = form_model.ModelMultipleChoiceField(queryset=models.Classes.objects.all()) # xx = form_model.ModelChoiceField(queryset=models.Classes.objects.all())
方式二:
class TeacherForm(Form): tname = fields.CharField(min_length=2) xx = fields.MultipleChoiceField( # choices = models.Classes.objects.values_list('id','title') # 這句可註釋 widget=widgets.SelectMultiple ) def __init__(self,*args,**kwargs): super(TeacherForm,self).__init__(*args,**kwargs) self.fields['xx'].choices = models.Classes.objects.values_list('id','title')
編輯界面如何將信息加載到頁面
def edit_teacher(request, nid): if request.method == 'GET': row = models.Teacher.object.filter(id=nid).first() class_ids = row.c2t.values_list(id) id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else [] obj = TeacherForm(initial={'tname':row.tname,'xx':id_list}) return render(request,'edit_teacher.html',{'obj':obj})
一個
cb = fields.CharField( widget = widgets.CheckboxInput )
多個
cbs = fields.MultipleChoiceField( choices = [(1,'籃球'),(2,'足球'),(3,'排球')], widget = widgets.CheckboxSelectMultiple )
r = fields.ChoiceField( choices = [(1,'籃球'),(2,'足球'),(3,'排球')], widget = widgets.RadioSelect )
f1.html
須要在form標籤增長 enctype="multipart/form-data"
<form method="post" action="/f1/" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="fafafa"> <input type="submit" value="提交"> </form>
普通上傳
def f1(request): if request.method == "GET": return render(request, 'f1.html') else: import os file_obj = request.FILES.get('fafafa') f = open(os.path.join('static', file_obj.name), 'wb') for chunk in file_obj.chunks(): f.write(chunk) f.close() return render(request, 'f1.html')
基於form實現文件上傳
class f2Form(Form): fafafa = fields.FileField() def f2(request): if request.method == "GET": obj = f2Form() return render(request, 'f2.html', {'obj': obj}) else: import os obj = f2Form(files=request.FILES) if obj.is_valid(): print(obj.cleaned_data.get('fafafa').name) print(obj.cleaned_data.get('fafafa').size) f = open(os.path.join('static', obj.cleaned_data.get('fafafa').name), 'wb') for chunk in obj.cleaned_data.get('fafafa').chunks(): f.write(chunk) f.close() return render(request, 'f2.html', {'obj': obj})
惡意web用戶將代碼植入到提供給其它用戶使用的頁面中
經過頁面評論等功能,插入js代碼,獲取cookie等信息,叫作跨站腳本攻擊(Cross Site Scripting)
<script>alert(123)</script>
django默認阻止傳入頁面帶有標籤的字符串,想要瀏覽器不阻止傳入字符串的解析須要標註safe關鍵字
- 前端 <div>{{ msg | safe }}</div> - 後臺 from django.utils.safestring import mark_safe msg = "<a href='http://www.sdf.com'>sdf</a>" safe_msg = mark_safe(msg) return render(request,'conmment.html',{'msg':safe_msg})
因此,前端不要輕易加safe
可是,非要加safe,又想要避免被xss攻擊,能夠在後臺接收字段時,判斷是否有標籤關鍵字
v = request.POST.get('content') if 'script' in v: return render(request,"conmment.html",{'error':'輸入內容存在安全隱患'})
PS:
CSRF(Cross-site request forgery)跨站請求僞造,經過假裝來自受信任用戶的請求來利用受信任的網站。
一個網站用戶Bob可能正在瀏覽聊天論壇,而同時另外一個用戶Alice也在此論壇中,而且後者剛剛發佈了一個具備Bob銀行連接的圖片消息。設想一下,Alice編寫了一個在Bob的銀行站點上進行取款的form提交的連接,並將此連接做爲圖片src。若是Bob的銀行在cookie中保存他的受權信息,而且此cookie沒有過時,那麼當Bob的瀏覽器嘗試裝載圖片時將提交這個取款form和他的cookie,這樣在沒經Bob贊成的狀況下便受權了此次事務。
主要利用受害者恰好也在操做銀行頁面,cookie沒有過時,攻擊者利用圖片連接中的隱藏的form提交POST請求給銀行,銀行驗證cookie沒問題,會執行轉帳操做。
這樣經過其它網站直接向銀行發送請求的操做應該被禁止,因此在用戶登陸銀行系統時,銀行會給用戶一個隨機字符串,在接下來的POST請求中,必需要有這個隨機字符串,用戶才能夠正常操做,不然,銀行認爲操做操做違法。
Diango中帶有自動生成字符串的功能來避免被CSRF攻擊
- 啓用CSRF驗證 settings.py中MIDDLEWAR的'django.middleware.csrf.CsrfViewMiddleware'表示啓用驗證 - form表單中增長 {% csrf_token %} - 頁面加載後會有隱藏的input標籤 <input type="hidden" name="csrfmiddlewaretoken" value="dguniTLGtMB3xCoq3YFuyTzBtgOaCjdYygEf5QvzTwGqQuAyetpnCdvh6o1bIqNm"> - 瀏覽器中的cookie將生成 
- 全站禁用 將settings.py中'django.middleware.csrf.CsrfViewMiddleware',註釋 - 局部禁用 FBV 給函數加裝飾器 from django.views.decorators.csrf import csrf_exempt @csrf_exempt def func(request): ... - 局部使用 # 'django.middleware.csrf.CsrfViewMiddleware', # 註釋 from django.views.decorators.csrf import csrf_protect @csrf_protect def func(request): ... - 局部使用 CBV csrf_protect不可給類中的方法加裝飾器 from django.views import View from django.views.decorators.csrf import csrf_protect from django.utils.decorators import method_decorator # @method_decorator(csrf_protect,name = 'dispatch') # 方法二:給所有方法被裝飾 # @method_decorator(csrf_protect,name = 'post') # 方法三:制定給某個方法加裝飾器 @method_decorator(csrf_protect) # 方法一:給類加,所有方法被裝飾 class Login(View): def get(self, request): return render(request, "login.html") def post(self, request): print(request.POST.get("user")) return HttpResponse("login.post")
Django中CBV不可直接加裝飾器,須要經過method_decorator(裝飾器)
from django.views import View from django.utils.decorators import method_decorator # @method_decorator(auth,name = 'dispatch') # 方法二:給所有方法被裝飾 # @method_decorator(auth,name = 'fun2') # 方法三:制定給某個方法加裝飾器 @method_decorator(auth) # 方法一:給類加,所有方法被裝飾 class Login(View): def func1(self, request): ... # @method_decorator(auth) # 方法四:給方法加,僅該方法被裝飾 def fun2(self, request): ...
若是網站中帶有csrftoken,發送ajax應該將csrftoken帶進去
<script src="/static/jquery-3.2.1.js"></script> <script> function submitForm() { {#從頁面中獲取csrf值#} var csrf = $('input[name="csrfmiddlewaretoken"]').val(); $.ajax({ url:"/login.html", type:"POST", data:{'csrfmiddlewaretoken':csrf}, success:function(arg){ console.log(arg) } }) } </script>
從cookie中獲取csrf值
獲取cookie的插件:jquery.cookie.js
獲取token:$.cookie('csrftoken')
加入請求頭:headers:{'X-CSRFToken':token}
<script src="/static/jquery-3.2.1.js"></script> <script src="/static/jquery.cookie.js"></script> <script> function submitForm() { var token = $.cookie('csrftoken'); $.ajax({ url:"/login.html", type:"POST", data:{}, {#加入請求頭#} headers:{'X-CSRFToken':token}, success:function(arg){ console.log(arg) } }) } </script>
1.保存在瀏覽器端的「鍵值對」
2.服務端能夠向用戶瀏覽器端寫cookie
3.客戶端每次請求,會攜帶coolie
- 發送cookie obj = redirect("/index/") obj.set_cookie("ticket","1qazxsw23edc",max_age = 10) return obj - set_cookie方法: max_age = 10 //超時時間 path = '/' //指定url才能夠後去cookie domain = None //域名等級劃分,多點登陸,統一認證登陸 httponly = True //只有http請求中才可用,js代碼沒法獲取cookie secure = True //只有https請求才可用,用戶修改cookie沒用 - 接收cookie tk = request.COOKIES.get("ticket") - 加簽名 set_signed_cookie("ticket","1qazxsw23edc",salt='abc') salt = '' //加鹽 - 接收cookie+解密 tk = request.get_signed_cookie("ticket",salt='abc')
cookie是保存在客戶端瀏覽器上的鍵值對
Session是保存在服務端的數據(本質也是鍵值對)
http請求特色是短連接,無狀態
session與cookie具備一樣的功能:用於服務端驗證客戶端信息,啓到保持會話做用,
而且session應用須要依賴cookie
session與cookie具備不一樣的是:session不會將敏感信息直接給客戶端
發送session步驟:
1.生成 隨機字符串
2.經過cookie將 隨機字符串 發送給客戶端
3.服務端將 隨機字符串 對應 客戶端信息 鍵值對{ 隨機字符串:{‘username’:‘alex’,...} }保存到某個地方
- 語句 def func(request): request.session['username']='alex' # request.session['user_info']={'username':'alex',...} #能夠寫成字典,做爲session隨機字符串的value return ...
獲取session步驟:
1.獲取客戶端cookie中的 隨機字符串
2.去session中查詢有沒有對應 隨機字符串
3.去session對應的key的value中查看是否有username
- 語句 def func2(request): v = request.session.get('username') # v = request.session.get('user_info').get('username') if v: 登陸成功 else: 失敗
前端:
- 前端直接經過request.session獲取字典 {{ request.session.user_info.username }}
session其餘操做:
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的隨機字符串 s_k = request.session.session_key # 獲取隨機字符串自己 # 將全部Session失效日期小於當前日期的數據刪除 request.session.clear_expired() # 檢查 用戶session的隨機字符串 在數據庫中是否存在 request.session.exists(s_k) # 刪除 當前用戶的全部Session數據 request.session.delete(s_k) # 能夠用做登出loginout # session超時 request.session.clear() # 能夠用做登出loginout # 修改 session超時時間 request.session.set_expiry(value) * 若是value是個整數,session會在些秒數後失效。 * 若是value是個datatime或timedelta,session就會在這個時間後失效。 * 若是value是0,用戶關閉瀏覽器session就會失效。 * 若是value是None,session會依賴全局session失效策略。
配置文件設置session settings.py
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,默認修改以後才保存(默認)只要用戶從新操做頁面,session超時時間就會重置,能夠設置爲True
session保存設置
session默認保存在數據庫,django可讓session放在內存、緩存、數據庫(默認)、文件、cookie等等
想要session存放於不一樣位置,只須要修改配置文件settings.py
數據庫:
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默認)
緩存:(緩存至關於另一臺服務器的內存)
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的緩存別名(默認內存緩存,也能夠是memcache),此處別名依賴緩存的設置
文件:
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 緩存文件路徑,若是爲None,則使用tempfile模塊獲取一個臨時地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
緩存+數據庫(優先去緩存拿,緩存沒有去數據庫拿)
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
加密cookie(至關於又放到coolie,沒什麼用)
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
數據查詢到後顯示到頁面,如何實現分頁,Django自帶分頁功能,可是有些缺陷,因此咱們自定義一個分頁組件
ORM經過相似python中的切片功能實現分批獲取數據
models.UserInfo.objects.all()[0:10] # 從0開始取到9,取頭不取尾 models.UserInfo.objects.all()[10:20] # 從10開始取到19
Django自帶分頁適用於只有上一頁,下一頁的狀況
views.py
def index(request): ''' 分頁 :param request: :return: ''' current_page = request.GET.get('page') user_list = models.UserInfo.objects.all() paginator = Paginator(user_list, 10) # 用戶列表,每頁顯示10條數據 # ---paginator方法--- # page: page對象 # per_page: 每頁顯示條目數量 # count: 數據總個數 # num_pages:總頁數 # page_range:總頁數的索引範圍,如: (1,10),(1,200) try: posts = paginator.page(current_page) # 新建page對象 # ---posts方法--- # has_next 是否有下一頁 # next_page_number 下一頁頁碼 # has_previous 是否有上一頁 # previous_page_number 上一頁頁碼 # object_list 分頁以後的數據列表 # number 當前頁 # paginator paginator對象 except PageNotAnInteger as e: # 頁碼不是整型 posts = paginator.page(1) except EmptyPage as e: # 頁碼爲空 posts = paginator.page(1) return render(request, 'index.html', {'posts': posts})
index.html
<ul> {% for i in posts.object_list %} <li>{{ i.name }}</li> {% endfor %} </ul> <div> {% if posts.has_previous %} <a href="/index.html?page={{ posts.previous_page_number }}">上一頁</a> {% endif %} </div> <div> {% if posts.has_next %} <a href="/index.html?page={{ posts.next_page_number }}">下一頁</a> {% endif %} </div>
views.py
from utils.pager import PageInfo def custom(request): total_row = models.UserInfo.objects.all().count() # 所有數據行數 page_info = PageInfo(request.GET.get('page'), total_row, 10, '/custom.html', 11) user_list = models.UserInfo.objects.all()[page_info.start():page_info.end()] return render(request, 'custom.html', {"userlist": user_list, "page_info": page_info})
utils.pager.py
class PageInfo(object): def __init__(self, current_page, total_row, per_page, base_url, show_page=11): ''' :param current_page: 當前頁 :param total_row: 總數據行數 :param per_page: 每頁顯示幾條數據 :param base_url: url地址 :param show_page: 顯示幾頁 ''' try: self.current_page = int(current_page) except Exception as e: self.current_page = 1 self.per_page = per_page # 求頁碼數 x, y = divmod(total_row, per_page) if y: # 若是餘數不爲0,頁碼+1 x = x + 1 self.total_page = x self.show_page = show_page self.base_url = base_url def start(self): return (self.current_page - 1) * self.per_page def end(self): return self.current_page * self.per_page def pager(self): page_list = [] half_page = int((self.show_page - 1) / 2) # 1/2顯示幾頁 # 總頁數小於顯示幾頁 <11 if self.total_page < self.show_page: begin = 1 stop = self.total_page + 1 # 總頁數大於顯示幾頁 >11 else: # 當前頁小於一半 if self.current_page <= half_page: begin = 1 stop = self.show_page + 1 # 當前頁大於一半 else: # 當前頁+一半大於總頁碼 if self.current_page + half_page > self.total_page: begin = self.total_page - self.show_page + 1 stop = self.total_page + 1 # 當前頁+一半小於總頁碼 else: begin = self.current_page - half_page stop = self.current_page + half_page + 1 if self.current_page <= 1: prev = '<li><a href="#">上一頁</a></li>' else: prev = '<li><a href="%s?page=%s">上一頁</a></li>' % (self.base_url, self.current_page - 1,) page_list.append(prev) for i in range(begin, stop): if i == self.current_page: temp = '<li class="active"><a href="%s?page=%s">%s</a></li>' % (self.base_url, i, i) else: temp = '<li><a href="%s?page=%s">%s</a></li>' % (self.base_url, i, i) page_list.append(temp) if self.current_page >= self.total_page: nex = '<li><a href="#">下</a></li>' else: nex = '<li><a href="%s?page=%s">下一頁</a></li>' % (self.base_url, self.current_page + 1,) page_list.append(nex) return ''.join(page_list)
custom.html
<ul> {% for i in userlist %} <li>{{ i.name }}</li> {% endfor %} </ul> <nav aria-label="Page navigation"> <ul class="pagination"> {{ page_info.pager|safe }} {# html調用函數不須要加括號pager(),瀏覽器信任插入字符串safe#} </ul> </nav>