Django序列化元件與資料批量操作與簡單使用Forms元件
SweetAlert前端外掛
Django自帶的序列化元件
serializers序列化元件可以把我們用ORM產生的QuerySet物件轉換成json格式資料。
from django.core import serializers def index(request): book_queryset = models.Book.objects.all() res = serializers.serialize('json', book_queryset) return HttpResponse(res)
轉換後的格式長這樣:
批量資料操作
如果我們想要使用ORM去迴圈插入10萬條資料,每次新增資料都執行一次create(),這樣會頻繁走資料庫操作,效率極低,比如:
for i in range(100000):
models.Book.objects.create(title=f'第{i}本書')
這樣操作需要等待很久,所以我們可以換一個方法:先用類建立多個物件,在用bulk_create(),這樣只要走一次資料庫操作就可以新增多個數據了:
obj_list = [] # 存放物件 for i in range(100000): obj = models.Book(title=f'第{i}本書') # 例項化多個數據物件 obj_list.append(obj) # 物件追加到列表種 models.Book.objects.bulk_create(obj_list) # 一次性全部新增
分頁器與推導流程
網站不可能將所有的資料全部展示到一頁,應該考慮使用分頁,每頁只展示部分資料。那麼分頁該如何實現呢?
推導流程
1.首先需要知道ORM中的all()方法返回的結果集是支援正數的索引切片的。
# 取第一個到第10個的結果
book_queryset = models.Books.objects.all()[0:10]
2.在使用者點選分頁的頁數時肯定是要向後端請求資料的,比如第5頁就給前端返回第41到第50的結果(每頁展示10條資料的情況),所以後端需要用一個變數接收前端傳來的頁數。
前端:傳送第五頁的請求,可以用a標籤傳送GET請求,並攜帶資料page=5。
<a href='?page=5'>5</a>
後端:用變數接收
current_page = request.GET.get('page')
3.既然需要分頁,那麼每頁肯定都有最多的展示條數,這裡我們設定每頁10條,返回指定頁數的資料。
def index(request):
current_page = request.GET.get('page')
try: # 異常處理,防止current_page值為空時報錯
current_page = int(current_page)
except:
current_page = 1
start = (current_page - 1) * 10 # 資料起始位置
end = current_page * 10 # 資料結束位置
book_queryset = models.Books.objects.all()[start:end]
return render(request, 'index.html', {'book_queryset': book_queryset})
4.前端接收後端資料:
<div class="text-center">
{% for book_obj in book_queryset %}
<p>{{ book_obj.name }}</p>
{% endfor %}
</div>
5.這時候我們只需要在瀏覽器地址後面輸入?page=10,就可以獲取第10頁的資料。
6.新增Bootstrap提供的分頁器
<div class="text-center">
{% for book_obj in book_queryset %}
<p>{{ book_obj.name }}</p>
{% endfor %}
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
7.由於前端不好寫動態的分頁器,所以我們用後端編寫html標籤,編寫頁數時還需要用到divmod()獲取所有資料需要的頁數,比如99條資料要10頁,100條資料要10頁,101條資料要11頁。
後端:
def index(request):
# 獲取當前頁數
current_page = request.GET.get('page')
try: # 異常處理,防止current_page值為空時報錯
current_page = int(current_page)
except:
current_page = 1
data_queryset = models.Books.objects.all()
start = (current_page - 1) * 10 # 資料起始位置
end = current_page * 10 # 資料結束位置
book_queryset = data_queryset[start:end]
data_count = data_queryset.count()
# 接收整數和餘數
page_count, m = divmod(data_count, 10)
# 餘數不為0,則要把整數部分加一
if m != 0:
page_count += 1
html = []
# 讓當前頁數左邊顯示5個頁碼,右邊顯示五個頁碼
for i in range(current_page - 5, current_page + 5):
if i == current_page: # 當前頁數高亮顯示
html.append(f"<li class='active'><a href='?page={i}'>{i}</a></li>")
else: # 當前頁數普通顯示
html.append(f"<li><a href='?page={i}'>{i}</a></li>")
return render(request, 'index.html', {'book_queryset': book_queryset, 'html': html})
前端:
<div class="text-center">
{% for book_obj in book_queryset %}
<p>{{ book_obj.name }}</p>
{% endfor %}
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% for h in html %}
{{ h|safe }}
{% endfor %}
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
8.現在可以點選分頁器到指定頁面了,但是出現了新問題,當前頁碼小於6時,分頁器有零或負數,當前頁碼過大時,分頁器會超出。
這個問題加個變數就可以了。
temp_page = current_page
# 頁數過小
if current_page < 6:
temp_page = 6
# 頁數過大
if current_page > page_count - 4:
temp_page = page_count - 4
for i in range(temp_page - 5, temp_page + 5):
if i == current_page: # 當前頁數高亮顯示
html.append(f"<li class='active'><a href='?page={i}'>{i}</a></li>")
else: # 當前頁數普通顯示
html.append(f"<li><a href='?page={i}'>{i}</a></li>")
究極大法
上面是自定義分頁器開發流程的基本思路,我們不需要掌握程式碼的編寫,只需要掌握基本用法即可,原文部落格:自定義分頁器 - JasonJi - 部落格園 (cnblogs.com)
自定義分頁器封裝程式碼
點選檢視程式碼
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
"""
封裝分頁相關資料
:param current_page: 當前頁
:param all_count: 資料庫中的資料總條數
:param per_page_num: 每頁顯示的資料條數
:param pager_count: 最多顯示的頁碼個數
"""
try:
current_page = int(current_page)
except Exception as e:
current_page = 1
if current_page < 1:
current_page = 1
self.current_page = current_page
self.all_count = all_count
self.per_page_num = per_page_num
# 總頁碼
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager
self.pager_count = pager_count
self.pager_count_half = int((pager_count - 1) / 2)
@property
def start(self):
return (self.current_page - 1) * self.per_page_num
@property
def end(self):
return self.current_page * self.per_page_num
def page_html(self):
# 如果總頁碼 < 11個:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 總頁碼 > 11
else:
# 當前頁如果<=頁面上最多顯示11/2個頁碼
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1
# 當前頁大於5
else:
# 頁碼翻到最後
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1
page_html_list = []
# 新增前面的nav和ul標籤
page_html_list.append('''
<nav aria-label='Page navigation>'
<ul class='pagination'>
''')
first_page = '<li><a href="?page=%s">首頁</a></li>' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一頁</a></li>'
else:
prev_page = '<li><a href="?page=%s">上一頁</a></li>' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
else:
temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一頁</a></li>'
else:
next_page = '<li><a href="?page=%s">下一頁</a></li>' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '<li><a href="?page=%s">尾頁</a></li>' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部新增標籤
page_html_list.append('''
</nav>
</ul>
''')
return ''.join(page_html_list)
前端使用:
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
{% for book in page_queryset %}
<p>{{ book.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}
</div>
</div>
</div>
後端使用:
def get_book(request):
book_list = models.Book.objects.all()
current_page = request.GET.get("page", 1)
all_count = book_list.count()
page_obj = Pagination(current_page=current_page, all_count=all_count, per_page_num=10)
page_queryset = book_list[page_obj.start:page_obj.end]
return render(request, 'booklist.html', locals())
Forms元件之建立
Forms元件功能:資料校驗、標籤渲染、展示資訊。
- 資料校驗:資料是否符合規範(長度、格式等)
- 標籤渲染:快速生成輸入標籤等
- 資訊展示:展示錯誤的提示資訊,並保留原輸入內容
基本使用
from django import forms
# 建立表單類
class MyForm(forms.Form):
# 使用者名稱至少三個字元最多八個字元
username = forms.CharField(min_length=3, max_length=8)
# 年齡最小不能小於0 最大不能超過150
age = forms.IntegerField(min_value=0, max_value=150)
# 郵箱必須符合郵箱格式(@關鍵符號)
email = forms.EmailField()
Forms元件之資料校驗
建立好表單類之後,在檢視函式中使用:
將資料傳入並例項化物件,需要字典型別,字典的鍵名稱與表單類中自定義的名稱一致:
form_obj = MyForm({
'username': 'abc',
'age': 999,
'email': '12qq'
})
檢視資料是否合法(全部合法結果才是True):
form_obj.isvalid()
檢視不符合條件的資料及原因:
form_obj.errors
檢視符合條件的資料:
form_obj.cleaned_data
補充
1.forms類中所有的欄位資料預設都是必填的,不能少,如果想忽略某些欄位,可以新增 required=False。
email = forms.EmailField(required=False)
2.forms類中額外傳入的欄位資料不會做任何的校驗,直接忽略。
Forms元件之渲染標籤
後端返回給前端form物件,前端可以使用這個物件建立標籤:
def index(request):
form_obj = MyForm()
return render(request, 'index.html', locals())
建立方式一:封裝程度高,擴充套件性較差,主要用於快速生成頁面測試功能
1.每一個輸入框佔一行
<form action="" method="post">
{{ form_obj.as_p }}
<input type="submit">
</form>
2.所有輸入框佔一行
<form action="" method="post">
{{ form_obj.as_table }}
<input type="submit">
</form>
3.輸入框以無序列表形式展示
<form action="" method="post">
{{ form_obj.as_ul }}
<input type="submit">
</form>
建立方式二:封裝程度低,擴充套件性較好,但是欄位比較多的情況下不方便。
form物件.欄位名.label:文字提示
form物件.欄位名:輸入標籤
<form action="" method="post">
{{ form_obj.username.label }}
{{ form_obj.username }}
{{ form_obj.age.label }}
{{ form_obj.age }}
{{ form_obj.email.label }}
{{ form_obj.email }}
<input type="submit">
</form>
建立方式三:建立方式二使用for迴圈建立
<form action="" method="post">
{% for form in form_obj %}
<p>
{{ form.label }}
{{ form }}
</p>
{% endfor %}
<input type="submit">
</form>
補充
1.forms元件只負責渲染獲取使用者資料的標籤,form表單標籤和提交按鈕需要自己寫。
2.渲染標籤中文提示,可以在建立Form類中,建立欄位時用引數 label指定,不指定預設使用欄位名(首字母大寫)。
username = forms.CharField(min_length=3, max_length=8, label='使用者名稱')
Forms元件之資訊展示
在你點選提交表單資訊後,它會提醒你錯誤資訊:
如果不想要這種提示方式,form表單可以取消瀏覽器自動新增校驗功能的操作:新增屬性novalidate。
<form action="" method="post" novalidate>
</form>
這時候前端的校驗功能沒了,我們可以在後端進行校驗:
def index(request):
form_obj = MyForm()
if request.method == 'POST':
# request.POST可以看成字典型別
form_obj = MyForm(request.POST)
# 校驗資料
if form_obj.is_valid():
return HttpResponse('資料正常!')
return render(request, 'index.html', locals())
前端使用form.errors.0獲取錯誤資訊
<form action="" method="post" novalidate>
{% for form in form_obj %}
<p>
{{ form.label }}
{{ form }}
<span style="color: red">{{ form.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit">
</form>
錯誤資訊是可以自定義的,在Form類中建立欄位時定義:
# 使用者名稱至少三個字元最多八個字元
username = forms.CharField(min_length=3, max_length=8, label='使用者名稱',
error_messages={
'min_length': '使用者名稱最短3位',
'max_length': '使用者名稱最長8位',
'required': '使用者名稱必填'
})