django 視圖
視圖
CBV FBV
- CBV : class based view 基于類的視圖
FBV: funcation based view 基于函數的視圖
使用:在views里,好處是不需要加if判斷了
舉個基于類的views的例子
在views里
from django.views import View
class AddPublisher(View):
def get(self,request):
# get請求hr@qutke.com
return response
def post(self,request):
# post請求
return response
在url里的路徑
url(r'^add_publisher/', views.AddPublisher.as_view()),
as_view()的流程:
1). 程序啟動的時候,就執行as_view() 定義view函數并返回
2). 請求到來的時候,執行父類里面的view函數:
(1). 實例化當前的類 ——》 self
(2). self.request = request
(3). 執行self.dispatch的方法:
3.1. 判斷請求方式是否別允許:
3.1.1. 允許 通過反射拿到對用請求方式的方法 —— 》 handler
3.1.2. 不允許 self.http_method_not_allowed ——》 handler
3.2. 執行handler 得到 HttpResponse對象 返回
使用裝飾器
定義一個timer裝飾器
FBV : 直接加在函數上
CBV : from django.utils.decorators import method_decorator
@method_decorator(timer)
def get(self, request):
@method_decorator(timer)
def dispatch(self, request, *args, **kwargs):
ret=super().dispatch(request,*args,**kwargs) #調用父類方法
return ret
(1)指定給誰加上
@method_decorator(timer,name='post')
@method_decorator(timer,name='get')
class AddPublisher(View):
(2)要是這種的話,就不用寫dispatch方法了,他會加在父類的方法上
@method_decorator(timer,name='dispatch')
class AddPublisher(View):
使用類裝飾器和直接使用@timer有什么區別?
沒什么區別,主要區別在于裝飾器里的args,func上
1) 不使用method_decorator:
Args (<app01.views.AddPublisher object at 0x03B465B0>, <WSGIRequest: GET '/add_publisher/'>)
Func :func函數,取值,對應得request是第二個值(就是取值不太方便)
2) 使用method_decorator:
Args 是 (<WSGIRequest: GET '/add_publisher/'>,)
Func :bondfunc 綁定函數,取值的話第一個值對應得是request對象
request
1)屬性
request.method #請求方式 POST GET :類型是字符串
request.GET #是url地址上的參數,不是get帶的參數
request.POST #form表單用戶提交POST請求的數據
#request.POST.get(‘name’) #得到數據(返回值,get()是字典形式)*
request.FILES # 上傳的文件
request.path_info # 路徑 不包含IP和端口 參數
print(request.path_info,type(request.path_info)) 結果:/index/ <class 'str'>
request.body # 請求體 請求數據 get沒有請求體
結果:get b’ ‘ 是空的,post ’ b’ czc…name=’’’里面有內容
Request.META #請求頭的信息
Request.COOKIES: #一個標準的python字典
Request.session: #一個既會讀又課寫的類似于字典的形象,表示當前會話
以上的必須記住
Request.scheme 協議 http https 結果是http 類型是字符串
Request.encoding:表示提交數據的編碼方式,如果為None,則default_charset=’utf-8’
2)方法
Request.get_full_path() #路徑 不包含ip和端口 包含參數
Request.is_ajax() #判斷是否是ajax請求
Request.host() #獲取主機的ip和端口
上傳文件
-
form 表單里寫上編碼方式
enctype="multipart/form-data"
-
要是POST請求,在模板里要寫上
-
request.FILES 打印結果是字典
request.FILES.get(‘s19’) 獲取值, 文件在內存里
4) chunks:意思是一片一片的傳
這是一個上傳下載文件的代碼:
在views里
def upload(request):
if request.method=='POST':
print(request.POST)
f1 = request.FILES.get('s19')
print(f1,type(f1))
with open(f1.name,'wb')as f: #把文件寫進去,字節形式
for chunk in f1.chunks():
f.write(chunk)
return render(request,'upload.html')
在模板里
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
文件 <input type="file" name="s19">
<button>上傳</button>
</form>
response
HttpResponse('字符串') ——》 返回的字符串
render(request,'模板的文件名',{k1:v1}) ——》 返回一個完整的HTML頁面
redirect(要跳轉的地址) —— 》重定向 Location :地址
jsonresponse
httpresponse 和jsonresponse 的區別
Jsonresponse 傳輸字典類型的,要想傳輸其他類型,加上safe=False
from django.http.response import JsonResponse
JsonResponse(data) # Content-Type: application/json
JsonResponse(data,safe=False) # 傳輸非字典類型
舉個例子
import json
from django.http.response import JsonResponse
def json_data(request):
date={'name':'alex','age':30}
# return HttpResponse(date) #得到的是字典的鍵
ret = HttpResponse(json.dumps(date))
ret['Content-Type']='application/json'
return ret #{'name':'alex','age':30}
# Content-Type: text/html; charset=utf-8
# return JsonResponse(date) #{'name':'alex','age':30}
# Content-Type: application/json
return JsonResponse(li,safe=False)
分組
- Django settings.py配置文件中默認沒有 APPEND_SLASH 這個參數,但 Django 默認這個參數為 APPEND_SLASH = True。 其作用就是自動在網址結尾加'/'。
2.分組 ( )
url(r'^blog/([0-9]{4})/([0-9]{2})/$', views.blog),
分組的結果會當做位置參數 ——》 傳遞給視圖函數
def blog(request,name,aa):
return HttpResponse('ok')
3.命名分組 (?P
url(r'^blog/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.blog),
?分組的結果會當做關鍵字參數 ——》 傳遞給視圖函數
def blog(request,year,month):
print(year,month)
return HttpResponse('ok')
4.include 路由分發
先匹配一級目錄,在匹配二級目錄 (多個app的時候)
from django.conf.urls import url,include
from app01 import views
urlpatterns = [
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls')),
]
**傳遞額外的參數給視圖函數 **
注:要是命名函數的名字和參數的鍵同名,那命名函數名字的值就是參數的值
url(r'^blug/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/',views.blug,{'foo':'v1'})
def blug(request,year,month,**kwargs):
print(year,month)
print(kwargs)
return HttpResponse('ok')
5. URL命名和反向解析
靜態地址
1)命名
url(r'^blog/$', views.blogs, name='xxxx'),
url(r'^home/',views.home)
使用:
?
在模板
? {% url 'xxxx' %} ——》 '/blog/' 一個完整的URL
或是在py文件
? from django.urls import reverse
? reverse('xxxx') ——》 '/blog/
2)命名分組
url(r'^blog/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.blog,name='blog' ),
使用:
?
在模板
? {% url 'blog' ‘2019’ ‘12’ %} ——》 '/blog/2019/12/' 一個完整的URL 位置傳參
? {% url 'blog' month='09' year='2016' %} '/blog/2016/09/' 關鍵字傳參
? 在py文件
? from django.urls import reverse
? reverse('blog',args=('2018','02')) ——》 '/blog/2018/02'
reverse('blog', kwargs={'year': '2020', 'month': '03'}) ——》 /blog/2020/03
namespace 在多個app下有相同的方法,用'namespace:name'便于區分
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app01/', include('app01.urls',namespace='app01')),
url(r'^app02/', include('app02.urls',namespace='app02')),
]
使用:
在模板
{% url 'namespace:name' 參數 %}
在py文件
reverse( 'namespace:name' ,arrgs=(),kwargs={} )
admin的使用
LANGUAGE_CODE = 'zh-Hans' 改成中文
1. 創建超級用戶
python manage.py createsuperuser
用戶名和密碼 密碼 數字+字母+至少8位
2. 注冊model
在app下的admin.py中寫
from app01 import models
admin.site.register(models.Person)
3. 訪問 http://127.0.0.1:8000/admin/
ORM模型
對象關系映射(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向對象與關系數據庫存在的互不匹配的現象的技術。
ORM的操作:
類 —— 》 表
對象 —— 》 數據(記錄)
屬性 —— 》 字段
models.User.objects.all() # 所有數據 對象列表
models.User.objects.get(username='alex') # 獲取一個滿足條件的對象 沒有數據 或者 多條數據就報錯
models.User.objects.filter(username='alex1') # 獲取滿足條件的所有對象 對象列表1) 對應關系
class Student(models.Model)
models.CharField (max_length=32) # varchar(32)
models.AutoField (primary_key=True) #自增字段 pk主鍵
models.ForeignKey (‘Publisher’,on_delete=models.CASCADE) #publisher是所管連的表的類名(可以是字符串的形式)
models.CASCADE 是級聯刪除
default=’男’
on_delete=models.SET_DEFAULT 是設置默認值
SET_NULL是設置為空
SET(value)
on_delete:所關聯的表刪除后,對這個表所做的操作
django1.11版本之前的不用填寫 默認是on_delete=models.CASCADE
django2.0版本之后就是必填項
1.ORM字段 對象關系映射
orm跟MySQL的區別
orm是關聯數據庫的,讓開發者用代碼操作對象,簡化開發,屏蔽不同數據庫的SQL差異,減少手寫SQL的工作,降低出錯率。
直接操作MySQL比用orm執行效率更高。
orm因額外的映射轉換過程,會存在輕微的性能損耗,"代碼->SQL"的轉換過程會消耗少量CPU和時間,而直接手寫SQL可跳過此步驟直接與MySQL交互。
- 對于簡單業務(如單表查詢,新增),orm的性能損耗通常可忽略,此時開發效率比微小的性能差距更重要,
- 對于高并發,復雜查詢,(如多表聯查,大數據量統計),orm自動生成的SQL可能不夠優化(如冗余字段,低效關聯),此時手寫SQL可針對性優化,效率優勢會更明顯。
常用的字段
? AutoField :自增字段 自增的整形字段,必填參數primary_key=True,則成為數據庫的主鍵。無該字段時,django自動創建。
一個model不能有兩個AutoField字段。
? AutoField****IntegerField: 一個整數類型。數值的范圍是 -2147483648 ~ 2147483647。
? AutoField****CharField: 字符類型
? AutoField****DateField ; 日期類型
參數:
auto_now:每次修改時修改為當前日期時間。
auto_now_add:新創建對象時自動添加當前日期時間。每次修改時,時間不變
auto_now和auto_now_add和default參數是互斥的,不能同時設置。
? AutoField****DatetimeField : 日期時間字
AutoField****BooleanField : - 布爾值類型
AutoField****NullBooleanField 可以為空
AutoField****TextField- 文本類型
AutoField****FloatField:浮點型
AutoField****DecimalField 10進制小數
2.參數
1). null = Ture 數據庫中可以為空
2). blank = Ture 表單中可以為空
3). db_column 修改數據庫中字段的列名

4). default 默認值
5) unique=True 唯一
6) verbose_name=’名字’ Admin中顯示的字段名稱中文提示
7) choices Admin中顯示選擇框的內容可供選擇的值 gender=models.BooleanField('性別',default=False,choices=((0,'男'),(1,'女')))
8) help_text='請輸入正確年齡' Admin中該字段的提示信息
3.自定義一個char類型字段:
class MyCharField(models.Field):
"""
自定義的char類型的字段類
"""
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
def db_type(self, connection):
"""
限定生成數據庫表的字段類型為char,長度為max_length指定的值
"""
return 'char(%s)' % self.max_length
使用:用自定義的類名 phone = MyCharField(max_length=11,)
4. 表的參數
Class Mate類是固定寫法,是給上邊的類做的標配
class Person(models.Model):
pid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32, db_column='username', unique=True, verbose_name='姓名') # varchar(32)
class Meta:
# 數據庫中生成的表名稱 默認 app名稱 + 下劃線 + 類名
db_table = "person"
# 排序
ordering = ('pk',)
# 聯合索引
index_together = [
("name", "age"),
]
# 聯合唯一索引
unique_together = (("name", "age"),)
下圖是該名字,改變后,名字變了,內容不變


orm 批量操作 bulk_create
models.StudyRecord.objects.bulk_create(study_record_list)
或者
models.StudyRecord.objects.get_or_create(student=student,course_record_id=course_record_id) # 獲取或插入
models.StudyRecord.objects.update_or_create(student=student,course_record_id=course_record_id) # 更新或插入
5. ORM的查詢(13條) 必知必會13條
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_practice.settings")
import django
django.setup()
from app01 import models
返回的是對象列表 (8)
all 獲取表中所有的數據 ——》 對象列表 QuerySet
filter # 獲取表中滿足條件的所有數據 ——》 對象列表 QuerySet
exclude 獲取表中不滿足條件的所有數據 ——》 對象列表 QuerySet
order_by 對已經排好序的結果進行倒序 ——》 對象列表 QuerySet
排序 默認升序 字段加- 降序 多個字段 ——》 對象列表 QuerySet
models.Person.objects.all().order_by('-age', 'pk'),先按第一個字段排,要是有兩個一樣的,按第二個排
reverse 翻轉
ret = models.Person.objects.all().order_by('pk').reverse()
values 獲取數據的字段名和對應的值 ——》 對象列表 QuerySet [{} ,{}]字典
# 不填寫參數 獲取所有字段名字和值 obj=models.Person.objects.values()
# 填寫參數 獲取指定字段名字和值 obj=models.Person.objects.values('name',’age’)
values_list
獲取數據的字段對應的值 ——》 對象列表 QuerySet [() ,()] 元祖
# 不填寫參數 獲取所有字段值
# 填寫參數 獲取指定字段值 有順序
distinct 去重 所有字段都一樣才去重
返回的是對象 (3)
get # 獲取一條數據 ——》 對象 獲取不到或者多條就報錯
first 取第一個元素 沒有元素 None
last 取最后一個元素 沒有元素 None
返回布爾值 (1)
exists 判斷是否存在 models.Person.objects.filter(name='alex').exists()
返回數字 (1)
count 計數
單表的雙下劃線
ret = models.Person.objects.filter(pk=1)
ret = models.Person.objects.filter(pk__gt=1) # greater than 大于
ret = models.Person.objects.filter(pk__lt=3) # less than 小于
ret = models.Person.objects.filter(pk__gte=1) # greater than equal 大于等于
ret = models.Person.objects.filter(pk__lte=3) # less than equal 小于等于
ret = models.Person.objects.filter(pk__range=[1, 3]) # 范圍 左右都包含
ret = models.Person.objects.filter(pk__in=[1, 5]) # in 只要1和5
ret = models.Person.objects.filter(name__contains='alex') # like 完全包含
ret = models.Person.objects.filter(name__icontains='ale') # like 忽略大小寫 ignore
ret = models.Person.objects.filter(name__startswith='a') # 以a開頭
ret = models.Person.objects.filter(name__istartswith='a') # 以a開頭忽略大小寫
ret = models.Person.objects.filter(name__endswith='a') # 以a開頭
ret = models.Person.objects.filter(name__iendswith='g') # 以a開頭忽略大小寫
ret = models.Person.objects.filter(phone__isnull=True) # phone字段為空
ret = models.Person.objects.filter(phone__isnull=False) # phone字段不為空
ret = models.Person.objects.filter(birth__year=2019)
ret = models.Person.objects.filter(birth__contains='2019-04-09') 包含,相當于like
# ret = models.Person.objects.filter(birth__month=4)
外鍵的操作
1)基于對象的查詢
正向查詢
正向查詢: 外鍵所在表去查另一張表,Book >> Publisher
反向查詢:普通表去查外鍵所在的表,Publisher >> Book
book_obj = models.Book.objects.get(pk=1)
# print(book_obj.publisher) # 書籍所關聯的對象
# print(book_obj.publisher.name) # 書籍所關聯的對象的名字
# print(book_obj.publisher_id) # 書籍所關聯的對象的ID
反向查詢
pub_obj = models.Publisher.objects.get(pk=1)
# 外鍵中 不指定related_name pub_obj.表名小寫_set 關系管理對象
# print(pub_obj.book_set,type(pub_obj.book_set)) # 表名小寫_set 關系管理對象
# print(pub_obj.book_set.all())
# 外鍵中 指定related_name='books' pub_obj.books 關系管理對象
# print(pub_obj.books,type(pub_obj.books)) # 表名小寫_set 關系管理對象
2)基于字段的查詢 外鍵名__name=’ ’ 跨表查詢
查詢人民出版社出版的所有書籍
ret = models.Book.objects.filter(publisher__name='人民出版社')
查詢豐乳肥臀的出版社
不指定 related_name 跨表時使用 表名小寫__字段
# ret = models.Publisher.objects.filter(book__title='豐乳肥臀')
# 指定 related_name='books' 跨表時使用books__字段
# ret = models.Publisher.objects.filter(books__title='豐乳肥臀')
# 指定 related_name='books' related_query_name='book' 跨表時使用book__字段
ret = models.Publisher.objects.filter(book__title='豐乳肥臀')
# print(ret)
print(pub_obj.books.all())
多對多
基于對象的查詢
author_obj.books ——》 關系管理對象
author_obj.books.all() ——》 關系管理對象
不指定related_name
book_obj.author_set ——》 關系管理對象
book_obj.author_set.all() ——》 作者寫過所有的書籍對象
指定related_name=’authors‘
book_obj.authors——》 關系管理對象
book_obj.authors.all() ——》 作者寫過所有的書籍對象
管理對象的方法
all 獲取所有的對象
set 設置關系 多對多 [ id,id ] [對象,對象] 一對多 [對象]
add 添加關系 多對多 id,id 對象,對象 一對多 對象
remove 刪除關系 一對多:必須設置外鍵可為空,才有remove clear方法
clear 清空所有的關系
create 創建一個對象并且添加關系
`book_obj.authors.create(name='xxxx') `
pub_obj.books.set(models.Book.objects.filter(pk__in=[3])) # 設置關系 【對象 】
pub_obj.books.add(*models.Book.objects.filter(pk__in=[4])) # 添加關系 對象
pub_obj.books.remove(*models.Book.objects.filter(pk__in=[3])) # 刪除關系 對象 外鍵 null=True
pub_obj.books.clear() # 刪除關系 對象 外鍵 null=True
聚合 aggregate
所獲取的是對象列表[{ },{ }]
例:
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_practice.settings")
import django
django.setup()
from app01 import models
from django.db.models import Max, Min, Count, Avg, Sum
# 1 所有書的平均價格
# res = models.Book.objects.aggregate(Avg('price'))
# print(res)
# 2.上述方法一次性使用
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Count('pk'), Avg('price'))
print(res)
分組 annotate
# 分組查詢 annotate
"""
MySQL分組查詢都有哪些特點
分組之后默認只能獲取到分組的依據 組內其他字段都無法直接獲取了
嚴格模式
ONLY_FULL_GROUP_BY
"""
from django.db.models import Max, Min, Sum, Count, Avg
# 1.統計每一個書的作者個數
# res = models.Book.objects.annotate() # models后面點什么 就是按什么分組
"""
author_num是自定義的字段 用來存儲統計出來的每本書對應的作者個數
"""
# res = models.Book.objects.annotate(author_num=Count('authors')).values('title', 'author_num')
# print(res)
# 2.統計每個出版社賣的最便宜書的價格
# res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
# print(res)
# 3.統計不止一個作者的圖書
# 1.先按照圖書分組 求每一本書對應的作者個數
# 2.過濾出不只一個作者的圖書
# res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title', 'author_num')
"""
只要你的orm語句得出的結果還是一個queryset對象
那么就可以繼續無限制的點queryset對象封裝的方法
"""
# print(res)
# 4.查詢每個作者出的書的總價格
# res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
# print(res)
"""
如果按照指定的字段分組該如何處理呢
model.Book.objects.values('price').annotate()
如果出現分組查詢報錯的情況
需要修改數據庫嚴格模式
"""
方式一:
ret = models.Publisher.objects.annotate(min=Min('book__price')).values()
方式二
ret = models.Book.objects.values('publisher__name').annotate(min=Min('price')) #對象列表{}
for i in ret:
print(i)
F和Q查詢
from django.db.models import F, Q
F查詢 比較兩個字段
ret=models.Book.objects.filter(sale__gt=F('kucun')) update(sale=F(‘sale’)*2)
Q 查詢 |或 ;& 與 ;~ 非
Filter(Q(pk_gt=5)) == Q((‘ss’,kk))
F查詢
# 1.查詢賣出數大于庫存數的書籍
"""
能夠幫助你直接獲取表中某個字段對應的數據
"""
from django.db.models import F
# res = models.Book.objects.filter(maichu__gt=F('kuncun'))
# print(res)
# 2.將所有書籍的價格提升50元
# models.Book.objects.update(price=F('price') +500)
# 3.將所有書的名稱后面加上爆款兩個字
"""
在操作字符類型數據的時候 F不能夠直接做到字符串的拼接
"""
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'),Value('爆款')))
# models.Book.objects.update(title=Concat(F('title')+'爆款') # 所有名稱都會全部變成空白
Q查詢
# 1.查詢賣出數大于100或者價格小于600的書籍
# res = models.Book.objects.filter(maichu__gt=100,price__lt=600)
"""filter括號內多個參數是and關系"""
from django.db.models import Q
# res = models.Book.objects.filter(Q(maichu__gt=100),Q(price__lt=600)) # Q包裹逗號分割 還是and關系
# res = models.Book.objects.filter(Q(maichu__gt=100)|Q(price__lt=600)) # | or關系
# res = models.Book.objects.filter(~Q(maichu__gt=100)|Q(price__lt=600)) # ~ not關系
# print(res)
# Q的高階用法 能夠將查詢條件的左邊也變成字符串的形式
# q = Q()
# q.connector = 'or'
# q.children.append(('maichu__gt', 100))
# q.children.append(('price__lt', 600))
# res = models.Book.objects.filter(q) # 默認還是end關系
# print(res)
事務

"""
事務
ACID
原子性
不可分割的最小單位
一致性
跟原子性是相輔相成
隔離性
事物之間互相不干擾
持久性
事務一旦確認永久生效
事務的回滾
rollback
事務的確認
commit
"""
# 目前只需要掌握Django如何簡單的開啟事務
# 事務
from django.db import transaction
try:
with transaction.atomic():
# sql1
# sql2
# 在with代碼塊內書寫的所有orm操作都是屬于同一個事務
except Exception as e:
print(e)
print('執行其他操作')
only與defer , select_related與prefetch_related
"""
orm語句的特點:
惰性查詢
如果僅僅只是書寫了orm語句 在后面根本沒有用到該語句查詢出來的參數
那么orm會惰性識別
"""
# only與defer
# res = models.Book.objects.all()
# print(res) # 要用數據了才會走數據庫
# 獲取書籍表中所有書的名字
# res = models.Book.objects.values('title')
# for d in res:
# print(d.get('title'))
# 實現獲取到的是一個數據對象 然后點title就能夠拿到書名 并且沒有其他字段
# res = models.Book.objects.only('title')
# res = models.Book.objects.all()
#QuerySet [<Book: 三國演義爆款>, <Book: 紅樓夢爆款>, <Book: 論語爆款>, <Book: 聊齋爆款>, <Book: 老子爆款>]>
# print(res)
# for i in res:
# print(i.title) # 點擊only括號內的字段 不會走數據庫
# print(i.price) # 點擊only括號內沒有的字段 會重新走數據庫查詢而all不需要走
res = models.Book.objects.defer('title')
for i in res:
print(i.price)
# print(res)
"""
defer與only剛好相反
defer括號內放的字段不在查詢出來的對象里面 查詢該字段需要重新走數據
而如果查詢的是非括號內的字段 則不需要走數據庫
"""
# select_related與prefetch_related
# select_related與prefetch_related 跟跨表操作有關
# res = models.Book.objects.all()
# for i in res:
# print(i.publish.name) # 每循環一次就要走一次數據庫查詢
# res = models.Book.objects.select_related('publish') # INNER JOIN
"""
select_related內部直接先將book與publish連起來 然后一次性將大表里面的所有數據
全部封裝給查詢出來的對象
這個時候無論是點擊book表的數據還是publish的數據都無需再走數據查詢了
select_related括號內只能放外鍵字段 一對多 一對一
多對多不行
"""
# print(res)
# for i in res:
# print(i.publish.name)
res = models.Book.objects.prefetch_related('publish') # 子查詢
"""
prefetch_related該方法內部其實就是子查詢
將子查詢出來的所有結果也給你封裝到對象中
給你的感覺就是一次性完成的
"""
for i in res:
print(i.publish.name)
cookie
定義:cookie就是保存在瀏覽器本地的一組組鍵值對
特性:
- 服務器讓瀏覽器進行設置的
- 保存在瀏覽器本地的,瀏覽器也可以不保存
- 下次訪問時自動攜帶相應的cookie
為什么要有cookie?
HTTP協議是無狀態的,每次請求都是沒關系的,沒辦法保存,狀態,使用cookie保存狀態
django的操作:
-
設置 本質: 響應頭 set-cookie
ret = redirect('/home/') #響應對象 ret.set_cookie('is_login', '1') # 普通的cookie max_age=5設置時間 path domain #ret.set_signed_cookie(key='is_login',value= '1', salt='day62') # 加密的cookie #參數: max_age=5設置時間, path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie可以被任何url的頁面訪問domain=None, Cookie生效的域名 -
獲取 本質:請求頭cookie
request.COOKIES[key] {} # 普通的cookie 或者 request.COOKIES.get(key) #request.get_signed_cookie('is_login',salt='day62',default='') # 加密的cookie -
刪除
ret = redirect('/login/') ret.delete_cookie('is_login')
session
session 當做字典使用
定義: 保存在服務器上一組組鍵值對,必須依賴于cookie。
為什么要使用session?(特性)
- cookie保存在瀏覽器本地,不安全
- cookie的大小受到限制
django中操作session
-
設置
request.session[key] = value 或者 request.session.setdefault(key,value) -
獲取
request.session[key] 或者 request.session.get(key) -
刪除
del request.session['k1'] request.session.delete() # 刪除所有的session數據 不刪除cookie request.session.flush() # 刪除所有的session數據 刪除cookie request.session.exists("session_key") #檢查會話session的key在數據庫中是否存在
其他:
request.session.set_expiry(10) #設置超時時間,但是數據庫的session不會刪
通過request.session.clear_expired() # 清除已過期的數據
配置: 了解
from django.conf import global_settings
#數據庫Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默認)
#緩存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
#文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
SESSION_COOKIE_AGE = 1209600 設置失效時間的
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路徑(默認)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默認)
SESSION_COOKIE_SECURE = False # 是否Https傳輸cookie(默認)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http傳輸(默認)
中間件
本質上就是python中的類
使用:
在app01下新建一個middlewares文件夾,在里面新建一個py文件
from django.utils.deprecation import MiddlewareMixin
在setting里添加配置
1)官方的說法:中間件是在全局范圍內一個用來處理Django的請求和響應的框架級別的鉤子
2) 五種方法:(執行時間、執行順序、參數、返回值)
- process_request(self,request)
- process_view(self, request, view_func, view_args, view_kwargs)
- process_exception(self, request, exception)
- process_template_response(self,request,response)
- process_response(self, request, response)
process_request(self,request)
執行時間:
?視圖函數執行之前(請求到來后,路由匹配前)
執行順序:
?按照注冊的順序 順序執行
參數:
?request - 和視圖中的request對象是同一個
返回值:
?None: 正常流程
?HttpResponse對象: 后面的中間件的process_request方法、視圖都不執行,直接執行當前中間件的process_response方法,再返回給瀏覽器
process_response(self, request, response)
執行時間:
?視圖函數執行之后
執行順序:
?按照注冊的順序 倒序執行
參數:
?request - 和視圖中的request對象是同一個
?response - 視圖函數返回的響應對象
返回值:
HttpResponse對象: 必須返回
process_view(self, request, view_func, view_args, view_kwargs)
執行時間:
?在process_request方法后,路由匹配之后,視圖函數執行之前
執行順序:
?按照注冊的順序 順序執行
參數:
?request - 和視圖中的request對象是同一個
?view_func - 視圖函數
?view_args - 給視圖使用的位置參數
?view_kwargs - 給視圖使用的關鍵字參數
返回值:
?None: 正常流程
?HttpResponse對象: 后面的中間件的process_view方法、視圖都不執行,直接執行最后一個中間件的process_response方法,再返回給瀏覽器
process_exception(self, request, exception)
執行時間(觸發的條件):
? 視圖函數執行之后,視圖有異常時才執行
執行順序:
? 按照注冊的順序 倒序執行
參數:
? request - 和視圖中的request對象是同一個
? exception - 錯誤的對象
返回值:
? None: 正常流程 ,交給下一個中間件處理異常,都返回的是None,交給django處理異常(大黃頁)
? HttpResponse對象: 后面的中間件的process_exception方法不執行,直接執行最后一個中間件的process_response方法,再返回給瀏覽器
process_template_response(self,request,response)
執行時間(觸發條件):
? 視圖函數執行之后,要求視圖函數返回的對象是TemplateResponse對象
執行順序:
? 按照注冊的順序 倒序執行
參數:
? request - 和視圖中的request對象是同一個
? response - 視圖函數返回的響應對象
返回值:
? HttpResponse對象: 必須返回

csrf相關的裝飾器
csrf_exempt # 當前的視圖不需要CSRF校驗
csrf_protect # 當前的視圖需要CSRF校驗
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.views.decorators.csrf import ensure_csrf_cookie
@ csrf_exempt
def login(request) 普通函數直接加
CBV中 csrf_exempt只能加在dispatch上或是類上
@method_decorator(csrf_exempt,name='dispatch')
csrf中間件步驟
- 請求到來時執行process_request方法:
從cookie中獲取csrftoken的值 —— 》 賦值給 request.META['CSRF_COOKIE'] - 執行process_view方法:
- 查看視圖是否加了csrf_exempt裝飾器:
- 有 不需要csrf 校驗 返回none
- 沒有 需要csrf 校驗
- 請求方式的判斷:
- 如果是 'GET', 'HEAD', 'OPTIONS', 'TRACE' 不需要csrf 校驗 返回none
- 其他方式需要csrf 校驗
- 進行CSRF校驗:
- 從request.META獲取到csrftoken的值
- 嘗試從表單中獲取csrfmiddlewaretoken的值:
- 能獲取到
csrfmiddlewaretoken的值和cookie中的csrftoken值做對比
1. 對比成功 通過csrf校驗
2. 對比不成功 不通過csrf校驗 拒絕請求 - 獲取不到 嘗試從請求頭中獲取x-csrftoken的值
x-csrftoken的值和cookie中的csrftoken值做對比- 對比成功 通過csrf校驗
- 對比不成功 不通過csrf校驗 拒絕請求
- 能獲取到
- 查看視圖是否加了csrf_exempt裝飾器:
json 和XML對比
JSON 簡單的語法格式和清晰的層次結構明顯要比 XML 容易閱讀,并且在數據交換方面,由于 JSON 所使用的字符要比 XML 少得多,可以大大得節約傳輸數據所占用得帶寬。
ajax
(Asynchronous Javascript And XML)翻譯成中文就是“異步的Javascript和XML”,
AJAX 不是新的編程語言,而是一種使用現有標準的新方法
定義: 是一個js的技術,發送請求的一種途徑。
特點:
- 異步 2. 局部刷新 3. 傳輸的數據量小(xml json)
發請求的途徑:
- 地址欄上輸入地址 GET
- a標簽 GET
- form表單 GET/POST
簡單實例 固定格式
$.ajax({
url:'/calc/',
type:'post',
data:{
i1:$('[name="i1"]').val(),
i2:$('[name="i2"]').val(),
},
success:function (res) {
$('[name="i3"]').val(res)
}
})
參數
$.ajax({
url: '/ajax_test/', # 請求的地址
type: 'post', # 請求的方式
data: { # 請求的數據
name: 'alex',
age: 73,
hobby: JSON.stringify(['裝逼', '作死', '賣燒餅']) #將對象轉成字符串
},
success: function (res) { # 成功后執行的函數
location.href=地址 重定向 #返回時不要redirect了
},
error: function (res) { # 失敗后執行的函數
console.log(res)
},
})

ret=json.loads(ret)
print(ret,type(ret)) #<class 'list'>
ajax上傳文件


ajax通過django的csrf校驗
-
確保csrftoken的cookie
- 一個加
- 給視圖函數加裝飾器
-
提供參數
- 給data添加數據
data: {
csrfmiddlewaretoken:隱藏標簽或是cookie
csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val(),
i1: $('[name="i1"]').val(),
i2: $('[name="i2"]').val(),
},
? 2. 加x-csrftoken的請求頭
headers: {
'x-csrftoken': 隱藏標簽或是cookie
'x-csrftoken': $('[name="csrfmiddlewaretoken"]').val(),
},
3.文件導入
<script src="/static/ajax_setup.js"></script> - 給data添加數據
總結:json
數據交換的格式
Python
? 數據類型: 數字 字符串 列表 字典 布爾值 None
? 轉化:
? 序列化 Python的類型 ——》 json的字符串
? json.dumps(Python的類型)
? json.dump(Python的類型, f )
? 反序列化 json的字符串 ——》 Python的類型
? json.loads( json的字符串)
? json.load(f )
js
? 數據類型: 數字 字符串 列表 字典 布爾值 null
? 轉化:
? 序列化 js的類型 ——》 json的字符串
? JSON.stringify( js的類型 )
? 反序列化 json的字符串 ——》 JS的類型
? JSON.parse( json的字符串 )
JsonResponse({}) # contentType :application/json
JsonResponse([],safe=False)
form組件 是一個類
from django import forms
class RegForm(forms.Form):
user = forms.CharField()
pwd = forms.CharField()
# gender=forms.CharField(widget=forms.Select(choices=((1,'男'),(2,'女')))) #單選下拉框
# gender=forms.CharField(widget=forms.SelectMultiple(choices=((1,'男'),(2,'女')))) #多選
# gender=forms.ChoiceField(choices=((1,'男'),(2,'女')),
widget=forms.widgets.RadioSelect() #單選radio
) #單選下拉框
gender=forms.MultipleChoiceField(choices=((1,'男'),(2,'女'))) #多選
使用: 在views
form_obj=ReForm() #實例化對象
form_obj = RegForm(request.POST) #把post提交過來的數據直接傳進去
return render(request,'reg2.html',{'form_obj':form_obj })
模板 :
{{ form_obj.as_p }} ——》 生成p標簽 label 和 input標簽
{{ form_obj} input框
{{ form_obj.user }} ——》 字段對應的input標簽
{{ form_obj.user.id_for_label }} ——》 input標簽的ID
{{ form_obj.user.label }} ——》 字段對應的input標簽的中文
{{ form_obj.user.errors }} ——》 當前字段的所有錯誤信息
{{ form_obj.user.errors.0 }} ——》 當前字段的第一個錯誤信息
{{ form_obj.non_field_errors.0 }} __all__的錯誤的顯示
form組件的主要功能如下:
- 生成頁面可用的HTML標簽
- 對用戶提交的數據進行校驗
- 保留上次輸入內容
form字段
CharField 文本
ChoiceField 選擇框
MultipleChoiceField 多選框
字段參數
在form標簽里寫上novalidate ,就不提示錯了(取消瀏覽器的提示錯誤)
widget=forms.PasswordInput #密碼是密文 label=’中文’
required=True, 是否允許為空
widget=None, HTML插件
label=None, 用于生成Label標簽或顯示內容
initial=None, 初始值 (設置默認值)
error_messages=None, 錯誤信息 {'required': '不能為空', 'invalid': '格式錯誤'}
validators=[], 自定義驗證規則
disabled=False, 是否可以編輯
is_valide()是否驗證成功
關于choice的注意事項:
在使用選擇標簽時,需要注意choices的選項可以從數據庫中獲取,但是由于是靜態字段 獲取的值無法實時更新,那么需要自定義構造方法從而達到此目的。
方法一:在類里面定義一個__init取繼承父類

方法二:
from django.forms import models as form_model
hobby=form_model.ModelMultipleChoiceField(queryset=models.Hobby.objects.all())
校驗
1). 內置校驗
min_length=8,
max_length
required=True
2). 自定義校驗
Validators=[check]
字段的參數
validators=[check,RegexValidator()],
1. 寫函數
from django.core.exceptions import ValidationError
def check(value):
# 自定義校驗規則
# 通過校驗 不用返回
# 沒有通過校驗 拋出異常 raise ValidationError('有非法字符')
2. 內置的校驗器
from django.core.validators import RegexValidator
phone = forms.CharField(
validators=[RegexValidator(r'^1[3-9]\d{9}$', '手機號格式不正確')]
is_valid()的流程:
- 執行self.errors的方法 ——》 self.full_clean()
- full_clean方法():
- 定義一個存放錯誤信息的字典 self._errors = ErrorDict()
- 定義一個存放正確(經過校驗)值 的字典 self.cleaned_data = {}
- 執行self._clean_fields():
- 依次循環所有的字段
- 執行內部的校驗 和 自定義校驗器的校驗 value = field.clean(value)
- 將正確的值放在self.cleaned_data self.cleaned_data[name] = value
- 有局部鉤子就執行
- 通過校驗 self.cleaned_data[name] 重新賦值
- 不通過校驗 self.cleaned_data[name] 當前字段的鍵值對被清除 self._errors中存放當前字段的錯誤信息
- 執行self._clean_form() ——》 self.clean()
- 如果有重新定義就執行
- 通過校驗 返回所有的值 self.cleaned_data 重新賦值
- 不通過校驗 把錯誤信息添加到 all 中
- 如果有重新定義就執行
局部鉤子和全局鉤子
from django.core.exceptions import ValidationError
def clean_user(self):
# 局部鉤子
value = self.cleaned_data.get('user') # alex
# 通過校驗規則 返回正確的值 (你可以修改)
return "{}dsb".format(value)
# 不通過校驗規則 拋出異常
raise ValidationError('錯誤提示')
def clean(self):
# 全局鉤子
# 通過校驗規則 返回正確的值(所有的值 self.cleaned_data ) (你可以修改)
# 不通過校驗規則 拋出異常
pwd = self.cleaned_data.get('pwd')
re_pwd = self.cleaned_data.get('re_pwd')
if pwd == re_pwd:
return self.cleaned_data
raise ValidationError('兩次密碼不一致')
modelform組件
'oncontextmenu':"return false"是不讓你右鍵點擊
from django import forms
from django.core.exceptions import ValidationError
class RegForm(forms.ModelForm):
password = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={'placeholder': '您的密碼'}))
re_password = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={'placeholder': '確認您的密碼'}))
class Meta:
model = models.UserProfile
fields = '__all__' # ['字段名']
exclude = ['is_active']
widgets = {
'username': forms.TextInput(attrs={'placeholder': '您的用戶名'}),
'password': forms.PasswordInput(attrs={'placeholder': 'xxxxx'}),
'name': forms.TextInput(attrs={'placeholder': '您的真實姓名'}),
'mobile': forms.TextInput(attrs={'placeholder': '您的手機號'}),
}
error_messages = {
'username': {'invalid': '請輸入正確的郵箱地址'}
}
def clean(self):
# 獲取到兩次密碼
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if password == re_password:
# 加密后返回
md5 = hashlib.md5()
md5.update(password.encode('utf-8'))
password = md5.hexdigest()
self.cleaned_data['password'] = password
# 返回所有數據
return self.cleaned_data
# 拋出異常
self.add_error('re_password', '兩次密碼不一致!!')
raise ValidationError('兩次密碼不一致')
字段的展示:
1. 普通字段
{{ 對象.字段名 }}
2. choice參數
對象.字段名 ——》 數據庫的值
對象.get_字段名_display() ——》顯示 中文
{{ 對象.get_字段名_display }} 放在前端不加括號
外鍵字段 對象(__str__)
3. 自定義方法 models里
def show_classes(self):
return ' | '.join([str(i) for i in self.class_list.all()])
{{ 對象.show_classes }}
safe
不需要轉義 和safe一樣
在后端用mark_safe 在前端用safe
from django.utils.safestring import mark_safe
mark_safe(<span style="background-color: {};color: white;padding: 4px">{}</span>)
編輯時, instance=obj : 把原始數據放到這里,給頁面展示
Request.POST:獲取提交的值

Git
https://gitee.com/
1.配置:
先去當前項目找,沒有就去全局配置找
全局配置命令:vim ~/.gitconfig文件查看
Git config –-global user.email ‘qi@live.com’
Git config –-global user.name ‘qi’
項目配置命令:當前項目下 vim .git/config 文件查看
Git config –-local user.email ‘qi@live.com’
Git config –-local user.name ‘qi’
系統配置命令:vim /etc/gitconfig文件查看
Git config –-system user.email ‘qi@live.com’
Git config –-system user.name ‘qi’
注意: 需要有root權限
基本命令如下
Git init :先創建個空庫
git status: 查看狀態
git add . :添加
git add 文件
get --version : 查看版本
git commit –m’添加注冊和登錄功能’ :提交到本地
Git log :查看版本信息
git reset :是回滾到commit之前,(還可以重新修改,在提交)
git reset –hard ‘版本號’ :回到最原始的位置(會刪除本地的代碼)
Git reflog :查看版本號 在會滾回來還是git reset –hard ‘版本號’
Git clone 遠程倉庫地址
給遠程倉庫起別名:Git remote add origin 遠程倉庫地址
Git stash :保存
Git stash list : 查看“某個地方”存儲的所有記錄
Git stash clear : 清空某個地方
Git stash drop :編號 ,刪除指定編號的記錄
git push origin master:最后提交到服務器碼云
git pull origin master :拉代碼,從遠程拉到本地
==>
Git fetch origin master : 去倉庫 獲取
+
git merge origin/master-和網上下的master分支合并
git show <commit-hashId> 便可以顯示某次提交的修改內容
同樣 git show <commit-hashId> filename 可以顯示某次提交的某個內容的修改信息。
如何把分支A 上某個commit應用到分支b上:git cherry-pick <commit id>
git config --list #查看git配置信息
diff 即比較文件在暫存區和工作區的差異
顯示暫存區和工作區的差異:
$ git diff [file]
顯示暫存區和上一次提交(commit)的差異:
$ git diff --cached [file]
或
$ git diff --staged [file]
顯示兩次提交之間的差異:
$ git diff [first-branch]...[second-branch]
2.rebase變基
使git記錄簡潔(除了第1條后邊的全整合在一起),多個記錄 ——》1個記錄
用法:git rebase 分支
1)Rebase第一種:
第一步:Git rebase -i HEAD~3

第二步:把第二,三行pick 變成s, s代表把當前版本合并到上一個版本,最終v3,v4都合并到v2中了

第三步:

HEAD~3:合并前3條
注:合并記錄時不要把已提交(push)到倉庫的代碼合并,容易起沖突
2)rebase第二種:
- 第一步:git branch dev touch dev1.py git add git commit
- 第二步:git checkout master touch master1.py git add git commit
- 第三步:git checkout dev git rebase master(在dev上合并master)
- 第四步:git checkout master git merge dev(切換到master,再合并dev)
Git log --graph --pretty=format:”%h %s” 結果就是一條簡單的直線
3)Rebase第三種:
git pull origin master (有分叉)
==》
Git fetch origin dev
+
Git rebase origin master/dev
3.記錄圖形展示
Git log --graph
git log --graph --pretty=format:"%h %s"
4.merge 和 rebase命令的區別
Merge一次性展示所有沖突,rebase每次展示一次commit提交的沖突
Merge保留完整的歷史記錄,rebase會重寫歷史記錄
5. git rebase 產生沖突
方法一:git add 文件名
Git rebase --continue
方法二:

分支
git branch 查看
git branch dev 新建dev分支
git branch -d dev 刪除dev分支
git checkout dev 切換到dev分支
git merge dev 從dev合并到當前分支(有沖突 手動解決),在commit一下
Git clone -b v2 http:xxx :克隆 #-b 指定版本
git checkout -b dev :創建并切換到分支
注:先切換分支在合并,切換到merge,再把dev合并到merge
在家開發一半,再到公司開發...
origin只是名稱,可以換成任意如:xx等
git clone 是指在一臺新電腦上沒有代碼的情況下使用,他會把所有的代碼以及所有分支,提交記錄都可以有。
剛開始提交到云端時把master 和dev一起提交上去。
如果在家本地已經有代碼,想跟公司的同步,使用pull命令就可以更新。



在公司開發了一半,著急去吃飯,只把代碼提交到了本地版本庫,忘記提到云端,等回到家拉下的代碼不是最新的,但也不能在重新寫吧,就繼續寫新功能,在家寫完的代碼push到了云端,第二天去公司pull新代碼時出現了沖突,手動解決沖突,然后繼續開發下邊的功能,最后提交到云端。
在公司把代碼提到本地,忘記提到云端。
沖突指的是:在公司改了a1.py, 回家也改了a1.py, 修改了同一行數據,造成的沖突。
1.修改代碼
2. git add .
3. git commit -m '女神約飯'
回到家:
1. git pull origin dev
此時功能不是最新的,也不可能把寫過的在寫一遍,就開發些新功能吧
2. git add .
3. git commit -m '在家開發了其他新功能'
4. git push origin dev #推到云端
到公司:
1. git pull origin dev
這時出現了沖突,vim文件,手動刪除
2. vim a1.py
3. 繼續開發新功能
4. git add .
5.git commit -m '合并之后并開發完畢'
6. git push origin dev
**單人開發 **
新建一個dev分支 開發新功能
開發完成 合并到master分支
有bug 新建debug分組
到debug分支上修改bug
修改完成 合并到master分支 并且刪除debug分支
多人協作開發
遠程倉庫中每人有自己的分支
在本地開發 開發完成后提交到自己的分支
提交pull requests 合并到dev分支上
Github (倉庫,小公司,網站):花錢放到別人的倉庫
Gitlab (工具,大公司,開源軟件)是一個用于倉庫管理系統的開源項目,使用Git作為代碼管理工 具:自己公司搭建一個屬于自己的倉庫,自己買服務器,自己有管理員,頁避免代碼泄露
第一步:創建項目
1. 寫代碼
2. git add .
3.git commit -m '東北熱基本功能'
4. Git remote add origin 地址
5. git push -u origin master
6. 在master分支 git log 下
7. git tag -a v1 -m '第一版' # tag:打標簽,-a: 版本叫v1,-m: 版本描述 這是在本地打了個標簽 圖1
8. git push origin --tags #推送到倉庫 圖2
圖1: 
圖2 : 
第二步: 創建分支邀請成員
1. git checkout -b dev
2. git branch
3. git push origin dev
4. 在github邀請成員
第三步:小弟拉代碼繼續開發
1. git clone 地址
2. git checkout dev #切換到Dev分支
3. git checkout -b ddz #在Dev分支上在創建一個ddz分支繼續開發
4. touch doudizhu.py
git add .
git commit -m '斗地主功能開發中'
git push origin ddz
5. vim doudizhu.py #回家繼續開發
git add .
git commit -m '加班開發完成'
git push origin ddz
第四部: 給leader審核代碼并合并到dev, 在github上合并的代碼,看視頻吧
第五步: leader開發的release分支合并到master,有兩種方式,一個是在github上合并,一個就是命令,如下
1. git checkout dev #切換到Dev
2. git checkout -b release # 在Dev分支上創建了一個分支,此時release和Dev的代碼一樣
3. git push origin release # 吧release 推上去,提測人員就可以吧這個代碼拉走去提測,有問題直接修改就行(到這一步已經在線上github上合并了,下面是在本地合并一下)
4. git merge release #吧release合并到 Dev
5. git checkout master #切換到master
6. 你本地還沒有ddz功能,需要先拉一下代碼
git checkout master
git pull origin master
7. git tag -a v2 -m '第二版 增加斗地主功能'
8. git push origin --tags # 吧tag標簽推送上去
免密碼登錄
在url中體現

gitignore的作用:
配置 git 需要忽略的文件或文件夾,在 .gitignore 文件配置的文件或文件夾不會隨著 git 提交到你的倉庫
修改 git 歷史提交 commit 信息(重寫歷史)
https://www.jianshu.com/p/0f1fbd50b4be
git 已經提交了,如何去修改以前的提交信息
解決方法
修改最新的 log
如果只修改最新一條提交信息的 log 內容,直接使用命令 git commit --amend 就可以
修改歷史的 log
假設提交記錄如下:
$ git log
commit 9ac1179332670365a3e5ea00a6486998eb66db7a (HEAD -> fix_aop_no_class_defined, origin/fix_aop_no_class_defined)
Author: candyleer <295198088@qq.com>
Date: Mon Apr 16 19:58:23 2018 +0800
update test case
Signed-off-by: candyleer <295198088@qq.com>
commit 223fc80d17273b19238dcb648e6d6eefc579de0f
Author: candyleer <295198088@qq.com>
Date: Mon Apr 16 18:47:50 2018 +0800
unit test case
Signed-off-by: candyleer <295198088@qq.com>
commit 2275781a0d75add037721832bd68c1a8edb3813e
Author: candyleer <295198088@qq.com>
Date: Mon Apr 16 18:29:29 2018 +0800
should find method from parent
-
執行 git 命令, 修改近三次的信息
$ git rebase -i HEAD~3
將會得到提交日志是和git log倒敘排列的,我們要修改的日志信息位于第一位.
1 pick 2275781 should find method from parent
2 pick 223fc80 unit test case
3 pick 9ac1179 update test case
4
5 # Rebase 79db0bd..9ac1179 onto 79db0bd (3 commands)
6 #
7 # Commands:
8 # p, pick = use commit
9 # r, reword = use commit, but edit the commit message
2.我們現在要修改修改要should find method from parent這條日志,那么修改的日志為,將第一個pick 修改為edit, 然后 :wq 退出.
1 edit 2275781 should find method from parent
2 pick 223fc80 unit test case
3 pick 9ac1179 update test case
將會看到如下信息,意思就是如果要改日志,執行git commit --amend,如果修改完成后,執行git rebase --continue
client_java git:(fix_aop_no_class_defined) git rebase -i HEAD~3
Stopped at 2275781... should find method from parent
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
? client_java git:(2275781)
3.正式修改,執行命令,-s 就是自動加上Signed-off-by:
$ git commit --amend -s
client_java git:(63b2cfd) git commit --amend -s
[detached HEAD c46b30e] 1should find method from parent
Date: Mon Apr 16 18:29:29 2018 +0800
1 file changed, 4 insertions(+), 1 deletion(-
修改完成后,:wq 退出,然后完成此次 log 的rebase
$ git rebase --continue
client_java git:(c46b30e) git rebase --continue
Successfully rebased and updated refs/heads/fix_aop_no_class_defined.
4.這樣本地修改就完成啦,用git log 再看下:
commit 449efc747ffb85567667745b978ed7e3418cfe27 (HEAD -> fix_aop_no_class_defined)
Author: candyleer <295198088@qq.com>
Date: Mon Apr 16 19:58:23 2018 +0800
update test case
Signed-off-by: candyleer <295198088@qq.com>
commit 69237c0bd48439ea0d8b87bf2c7c7ac4786c66d4
Author: candyleer <295198088@qq.com>
Date: Mon Apr 16 18:47:50 2018 +0800
unit test case
Signed-off-by: candyleer <295198088@qq.com>
commit c46b30e456af6ecdf4a629a485e5efe5485e52b1
Author: candyleer <295198088@qq.com>
Date: Mon Apr 16 18:29:29 2018 +0800
1should find method from parent
Signed-off-by: candyleer <295198088@qq.com>
所有信息都有Signed-off-by:這個參數了
5。最后push 到遠程倉庫,所有的 DCO 就都可以加上啦,-f強制推送
git push origin <you_branch_name> -f
分頁
url
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^user_list/', views.user_list),
]
views
from django.shortcuts import render,redirect,HttpResponse
users = [ {'name': 'alex-{}'.format(i),'password':123} for i in range(1,304)]
def user_list(request):
'''
:param request:
:return:
page_num : 頁碼數
'''
try:
page_num = int(request.GET.get('page','1'))
if page_num <=0: #page_num : 頁碼數 page=1
page_num = 1
except Exception as e:
page_num = 1
#每頁顯示數據條數
per_num = 10
# 獲取總數量
all_count = len(users)
#獲取總頁碼數
total_page_num, more = divmod(all_count, per_num)
if more:
total_page_num +=1
#最多顯示頁碼數
max_show = 11
half_show = max_show // 2
#總頁碼數不足以滿足最大頁碼數
if total_page_num < max_show:
page_start = 1
page_end = total_page_num
else:
#頁碼數顯示負數時
if page_num -half_show <= 0:
page_start = 1
page_end = max_show
#頁碼數顯示超過總頁碼數時
elif page_num + half_show > total_page_num:
page_start = total_page_num -max_show +1
page_end = total_page_num
else:
#頁碼起始值
page_start = page_num -half_show
#頁碼終止值
page_end = page_num + half_show
'''
1 0 10
2 10 20
3 20 30
'''
start = (page_num-1) * per_num
end = page_num * per_num
return render(request,'user_list.html',{'users':users[start:end], 'total_page_num': range(page_start, page_end+1)})
user_list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table class="table table-bordered">
<tbody>
{% for user in users %}
<tr>
<td>{{ user.name }}</td>
<td>{{ user.password }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% for foo in total_page_num %}
<tr>
{# <ul>#}
<a href="?page={{ foo }}"> {{ foo }}</a>
{# </ul>#}
</tr>
{% endfor %}
</body>
</html>
第二種寫法:
在py文件中循環
改變的代碼如下:
views:
#return render(request,'user_list.html',{'users':users[start:end], 'total_page_num': range(page_start, page_end+1)})
page_list = []
for i in range(2,11):
page_list.append('<li><a href="?page={}"> {}</a></li>'.format(i,i))
page_html = ''.join(page_list)
return render(request,'user_list.html',{'users':users[start:end], "page_html":page_html})
html:
</tbody>
</table>
{{page_html | safe }}
</body>
</html>
添加上頁下一頁的標簽
接著以上代碼繼續寫
py文件:
else:
#頁碼起始值
page_start = page_num -half_show
#頁碼終止值
page_end = page_num + half_show
page_list = []
# 上一頁
if page_num == 1:
page_list.append( '<li class="disabled"><a><span>«</span></a></li>')
else:
page_list.append('<li><a href="?page={}"><span>«</span></a></li>'.format(page_num-1))
for i in range(page_start,page_end+1):
page_list.append('<li><a href="?page={}">{}</a></li>'.format(i,i))
# 下一頁
if page_num == total_page_num:
page_list.append('<li class="disabled"><a><span>»</span></a></li>')
else:
page_list.append('<li><a href="?page={}"><span>»</span></a></li>'.format(page_num + 1))
page_html = ''.join(page_list)
html文件中還是如上。

給頁碼加被選中的狀態
py文件: for i in range(page_start,page_end+1):
# 加選中的狀態
if i == page_num:
page_list.append('<li class="active"><a href="?page={}">{}</a></li>'.format(i, i))
else:
page_list.append('<li><a href="?page={}">{}</a></li>'.format(i,i))

封裝成類的形式完整代碼
pagnation.py
class Pagination:
def __init__(self,page_num,all_count,max_show=11,per_num=10 ):
try:
page_num = int(page_num)
if page_num <= 0: # page_num : 頁碼數 page=1
page_num = 1
except Exception as e:
self.page_num = 1
self.page_num = page_num
# 每頁顯示數據條數
self.per_num = per_num
# 獲取總數量
self.all_count = all_count
# 獲取總頁碼數
total_page_num, more = divmod(all_count, per_num)
if more:
total_page_num += 1
half_show = max_show // 2
# 總頁碼數不足以滿足最大頁碼數
if total_page_num < max_show:
page_start = 1
page_end = total_page_num
else:
# 頁碼數顯示負數時
if page_num - half_show <= 0:
page_start = 1
page_end = max_show
# 頁碼數顯示超過總頁碼數時
elif page_num + half_show > total_page_num:
page_start = total_page_num - max_show + 1
page_end = total_page_num
else:
# 頁碼起始值
page_start = page_num - half_show
# 頁碼終止值
page_end = page_num + half_show
self.page_start = page_start
self.page_end = page_end
self.total_page_num = total_page_num
@property
def page_html(self):
page_list = []
# 上一頁
if self.page_num == 1:
page_list.append('<li class="disabled"><a><span>«</span></a></li>')
else:
page_list.append('<li><a href="?page={}"><span>«</span></a></li>'.format(self.page_num - 1))
for i in range(self.page_start, self.page_end + 1):
# 加選中的狀態
if i == self.page_num:
page_list.append('<li class="active"><a href="?page={}">{}</a></li>'.format(i, i))
else:
page_list.append('<li><a href="?page={}">{}</a></li>'.format(i, i))
# 下一頁
if self.page_num == self.total_page_num:
page_list.append('<li class="disabled"><a><span>»</span></a></li>')
else:
page_list.append('<li><a href="?page={}"><span>»</span></a></li>'.format(self.page_num + 1))
return ''.join(page_list)
'''
1 0 10
2 10 20
3 20 30
'''
@property
def start(self):
return (self.page_num - 1) * self.per_num
@property
def end(self):
return self.page_num * self.per_num
在py文件中直接引用
views.py
from django.shortcuts import HttpResponse, render, redirect, reverse
from app01.pagination import Pagination
users = [ {'name': 'alex-{}'.format(i),'password':123} for i in range(1,304)]
def user_list(request):
'''
:param request:
:return:
page_num : 頁碼數
'''
page = Pagination(request.GET.get('page','1'),len(users))
return render(request,'user_list.html',{'users':users[page.start:page.end], 'page_html': page.page_html})
html 代碼如上
浙公網安備 33010602011771號