程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Django學習

編輯:Python

Django學習

1 創建項目

終端:django-admin startproject 項目名
Pycharm:新建項目選擇Django

2 默認文件介紹

HelloWorld
|-- HelloWorld
| |-- __init__.py
| |-- asgi.py [接收網絡請求](不用動)
| |-- settings.py [項目配置](經常使用)
| |-- urls.py [URL和函數的對應關系](經常使用)
| `-- wsgi.py [接收網絡請求](不用動)
`-- manage.py [項目的管理,啟動項目、創建app、數據管理](經常使用)

3,APP

創建APP:python mange.py startapp app名
HelloWorld
|-- HelloWorld
| |-- __init__.py
| |-- settings.py
| |-- urls.py
| `-- wsgi.py
|--app01
| |--__init__.py
| |--admin.py [固定,不用動](django默認提供admin後台管理)
| |--apps.py [固定,不用動](app啟動類)
| |--migrations [固定,不用動](數據庫變更記錄)
| | `--__init__.py
| |--models.py [重要](對數據庫的操作)
| |--tests.py [固定,不用動](單元測試)
| `--views.py [重要](函數)
`-- manage.py

4 快速入手

4.1 注冊app[settings.py ]

 settings.py INSTALLED_APPS 添加:app名.apps.AppConfig(app目錄下apps文件裡的AppConfig類)

4.2 編寫URL和視圖函數對應關系[urls.py]

 # 導入app裡的views文件
from app01 import views
# www.xxx.com/index -> 函數 path裡index是視圖函數名
urlpatterns = [
path('index/',views.index)
]

4.3 編寫視圖函數[views.py]

 def 函數名 (request):
pass

4.4 啟動

 終端:python manage.py runserver
Pycharm直接啟動

5 templates目錄(模板)

from django.shortcuts import render,HttpResponse
render:返回html
HttpResponse:返回字符

5.1 在app目錄下創建templates文件夾

 創建HTML文件

6 靜態文件

圖片
js
css
插件

6.1 創建static文件夾

 一般文件夾類包含
img文件夾
js文件夾
css文件夾
plugins文件夾

6.2 引用靜態文件

 {% load static %}
導入靜態文件通常在HTML文件前加入{% load static %},路徑為{% static 'img/1.png' %}

7 模板語法

7.1 傳入後端數據(data)

 data為字典
例:data = {'n1':'book'} data = {'n1':data}
return render(request,'1.html',data)

7.2 前端接收

 {
{ 字典鍵 }}
例:{
{ n1 }}
傳入的是字典n1後面加上點和要取的值的索引(字典鍵)
{
{ n1.n1 }}
取所有鍵
{% for child in n1.keys %}
<span>{
{ child }}</span>
{% endfor %}
取所有值
{% for child in n1.values %}
<span>{
{ child }}</span>
{% endfor %}
取所有鍵值
{% for k,v in n1.items %}
<span>{
{ k }} = {
{ v }}</span>
{% endfor %}
傳入的是列表n1後面加上點和要取的值的編號(列表值的位置0,1,2)
{
{ n1.0 }}
可以用for循環把列表裡的每個元素的值都取到
<div>
{% for child in n1 %}
<span>{
{ child }}</span>
{% endfor %}
</div>
要有開頭{% for child in n1 %}和結尾{% endfor %}接收值要用{
{ }}
支持判斷語句
{% if n1 == 'xxx' %}
<h1>xxx</h1>
{% elif n1 == 'yyy' %}
<h1>yyy</h1>
{% else %}
<h1>kkk</h1>

8 請求和響應

8.1 請求類型

request.method

8.2 接收數據(接收的數據為QueryDict類型)

GET請求
request.GET
獲取數據(單個項數據用get不用getlist)
request.GET.getlist('user')
#接收到的數據生成字符串
request.GET.urlencode()
POST請求
request.POST
獲取數據(單個項數據用get不用getlist)
request.POST.getlist('user')

8.3 返回數據(需要導包)

from django.shortcuts import render,HttpResponse,redirect
render:返回html
HttpResponse:返回字符
redirect:重定向

9 數據庫操作

9.1 安裝第三方模塊

pip install mysqlclient

9.2 ORM

可以創建、修改、刪除數據庫中的表
可以操作表裡的數據
無法操作數據庫

9.2.1 創建刪除數據庫

9.2.1.1 創建數據庫
 create database 庫名稱;
create database if not exists 庫名稱;#如果不存在該庫,則創建
9.2.1.2 刪除數據庫
 drop database 庫名稱;
9.2.1.3 選擇使用數據庫
 use 庫名稱;
9.2.1.4 顯示當前使用的數據庫
 select database();

9.2.2 Django連接數據庫

 修改settings.py文件裡的DATABASES
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', #數據庫類型
'NAME':'Learm', #數據庫名
'USER':'root', #賬號
'PASSWORD':'xxxxx', #密碼
'HOST':'127.0.0.1', #數據庫地址
'POST':'3306' #數據庫端口號
}
}

9.2.3 Django操作表

9.2.3.1 創建表
 #在models.py文件裡創建類
#表名為APP名_類名,例:app01_User
class User(models.Model):
#列名 數據類型 數據長度
name = models.CharField(max_length=32)
password = models.CharField(max_length=64)
age = models.IntegerField()
#time = models.DateTimeField() 包含年月日時分秒
time = models.DateField() #只包含年月日

執行命令

 python manage.py makemigrations
python manage.py migrate
9.2.3.2 刪除表或列
 注釋了類重新執行命令
新建一列
設置默認值
name=models.CharField(default=’xxx’)
或者設置可以為空
name=models.CharField(null=True,blank=True)
調整數據表結構修改類即可
新建數據
類名.objects.create(name=’張三’)
9.2.3.3 新建數據
 類名.objects.create(name='我',pasword='123',age='12')
如果有默認值則可以不寫他的值
9.2.3.4 刪除數據
 篩選刪除
條件
類名.objects.filter(id=1).delete()
全部刪除
全部
類名.objects.all().delete()
9.2.3.5 獲取數據(QuerySet類型)
9.2.3.5.1 QuerySet類型 -> 數據列表
 全部獲取
類名.objects.all()
可以用for循環獲取數據
for obj in 類名.objects.all():
print(obj.id,obj.name,obj.password,obj.age)

篩選獲取
data_list = 類名.objects.filter(id=1)
#多條件篩選方法一
data_list = 類名.objects.filter(phone="12695478956",id=1)
#多條件篩選方法二
#data_dict = {'phone':"12695478956",'id'=1}
#data_list = 類名.objects.filter(**data_dict)
#給字典賦值
#data_dict["phone"]=12695478956
對於數字(int)
data_list = 類名.objects.filter(id=12) #等於12
data_list = 類名.objects.filter(id__gt=12) #大於12
data_list = 類名.objects.filter(id__gte=12) #大於等於12
data_list = 類名.objects.filter(id__lt=12) #小於12
data_list = 類名.objects.filter(id__lte=12) #小於等於12
對於字符串(str)
models.PrettyNum.objects.filter(mobile__startswith="1999") #以1999開頭
models.PrettyNum.objects.filter(mobile__endswith="999") #以999結尾
models.PrettyNum.objects.filter(mobile__contains="99") #包含99的
 #如果只有一個數據
#None 表示的含義,更多的是一種不存在,是真正的空,而不是空列表([])的空,也不是空的字符串
data_list = 類名.objects.filter(id=1).first()
print(data_list.id,data_list.name,data_list.password,data_list.age)
#判斷數據是否存在在數據庫(返回 數據/None)
data_list = 類名.objects.filter(id=1).exists()
#判斷數據是否存在排除自己(返回 數據/None)
#id!=2 and phone="12695478956"
data_list = 類名.objects.filter(phone="12695478956").exists(id=2)
9.2.3.5.2獲取到的有多少條數據
類名.objects.all().count()
類名.objects.filter(id=1).count()
9.2.3.6更新數據
 篩選更新
類名.objects.filter(id=1).updata(age=999)
全部更新
類名.objects.all().updata(password=666)
9.2.4 部門管理實例

​ 多表鏈接
​ 數據庫存入ID(常見節省存儲開銷)(耗時)(數據庫范式)
​ 數據庫存入名稱(加速查找,允許數據冗余)
​ 約束
​ 無約束

id = models.IntegerField(verbose_name='部門id')

​ 有約束
​ 用到ForeignKey列名自動加下劃線列名 例:depart_id

9.3 顯示為對象時


depart = models.ForeignKey(to='depatments',to_field='id')
#ModelForm打印有約束的字段時在models.py定義的時候在那張表的類裡面要加上
def __str__(self):
#title是表賦值的時候定義的
return self.title

​ [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-l7BlCmGe-1657435349708)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220507150249197.png)]

​ 部門被刪除員工處理
​ 一起刪除(級聯刪除)

depart = models.ForeignKey(to='depatments',to_field='id',on_delete=models.CASCADE)

​ 置空

depart = models.ForeignKey(to='depatments',to_field='id',null=True,blank=True,on_delete=models.SET_NULL)
Django裡的約束
性別(1代表男,2代表女)
gender_choices = ((1,'男'),(2,'女'))
gender = models.SmallIntegerField(verbose_name='性別',choices=gender_choices)

9.4 數據庫元組套元組

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-BEIharJx-1657435349709)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220506232826504.png)]

在django裡用get_字段名稱_display()可以自動把1輸出男2輸出女
obj.get_gender_display()

9.5 數據庫時間

9.5.1 在python裡datatima轉字符串

obj.time.strftime("%Y-%m-%d-%H-%M") #%Y-%m-%d年月日

9.5.2 django模板語法裡

obj.time|date:"Y-m-d H:i:s"

10 urls.py

​ url支持正則表達式

path('zhuce/<int:nid>/list', zhuce)
#views.py文件裡定義函數時接收即可
def zhuce_list(request,nid):
pass

11 模板的繼承

11.1 定義母版

a.html

定義所需的js與css

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<!--在母版裡{% block css %}{% endblock %}類似於占位符,用於一些子版裡才用得到的css導入-->
{% block css %}{% endblock %}
</head>
<body>
<!--在母版裡{% block content %}{% endblock %}類似於占位符-->
{% block content %}{% endblock %}
<!--在母版裡{% block js %}{% endblock %}類似於占位符,用於一些子版裡才用得到的js導入-->
{% block js %}{% endblock %}
</body>
</html>

11.2 繼承母版

<!--在子版裡{% extends "a.html"%}用來繼承母版-->
{% extends "a.html"%}
<!--在子版裡{% block css %}{% endblock %}中間用來書寫應該在母版裡{% block css %}{% endblock %}的位置該寫的css-->
{% block css %}
<style></style>
{% endblock %}
<!--在子版裡{% block content %}{% endblock %}中間用來書寫應該在母版裡{% block content %}{% endblock %}的位置該寫的HTML-->
{% block content %}
<div>
title
</div>
{% endblock %}
<!--在子版裡{% block js %}{% endblock %}中間用來書寫應該在母版裡{% block js %}{% endblock %}的位置該寫的JavaScrip-->
{% block js %}
<script type="text/javascript"></script>
{% endblock %}

可以用多個{% block content %}{% endblock %}

12 Django組件

  • Form(字段全部需要自己寫)
  • ModelForm(字段可以自動生成)

12.1 Form(驗證時方便)

12.1.1 views.py

from django import forms
class MyFrom(forms.Form):
#在html裡會幫我們顯示input輸入框
user = forms.CharField(widget=forms.Input)
pwd = forms.CharField(widget=forms.Input)
email = forms.CharField(widget=forms.Input)
def user(request):
if request.method == 'GET':
#實例化類
form = MyFrom()
#把form傳入html
return render(request,'a.html',{
"form":form})

12.1.2 a.html

<from method="post">
<!--就不用手寫input標簽,直接導入 -->
{
{ from.user }}
{
{ from.pwd }}
{
{ from.email }}
</from>
<from method="post">
<!--就不用手寫input標簽,可以循環類裡的自動創建input標簽 -->
{% for field in form %}
{
{field}}
{% endfor %}
</from>

12.2 ModelForm(增刪改,最簡潔)

12.2.1 models.py

from django.db import models
# Create your models here.
class userinfo(models.Model):
# 員工表
name = models.CharField(verbose_name='姓名',max_length=16)
password = models.CharField(verbose_name='密碼',max_length=64)

12.2.2 views.py

from django import forms
class MyFrom(Form.ModelForm):
#required=True輸入不為空
xx = models.CharField(verbose_name='父母',max_length=16,required=True)
#不僅支持去models裡獲取字段也能自定義字段
class Meta:
model = UserIfo
#fields有三種寫法
#1、fields = "__all__" 取全部字段
#1、exclude = ['name'] 排除name字段
fields = ['name','password','xx']
#定義標簽的屬性(逐一添加屬性)
widgets = {

'name':forms.TextInput(attrs={
"class":"form-control"})
'password':forms.PasswordInput(attrs={
"class":"form-control"})
}
def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs)
# 循環找到所有插件,添加class屬性值
# name是字段名
for name, field in kwargs.items():
#字段為password的不加class屬性
if name == "password":
continue
# 添加class屬性 顯示數據庫verbose_name
field.widget.attrs = {
"class":"from-contorl","placeholder":field.label}
def user(request):
if request.method == 'GET':
#實例化類
form = MyFrom()
#把form傳入html
return render(request,'a.html',{
"form":form})
<from method="post">
<!--就不用手寫input標簽,直接導入 -->
<!--{
{ from.user.label }} 顯示數據庫verbose_name -->
{
{ from.user.label }}:{
{ from.user }}
{
{ from.pwd }}
{
{ from.email }}
</from>
<from method="post">
<!--就不用手寫input標簽,直接導入 -->
<!--{
{ from.user.label }} 顯示數據庫verbose_name -->
{% for field in form %}
{
{ field.label }}:{
{ field }}
{% endfor %}
</from>

12.4 數據校驗與自動提交

數據提交

校驗是否符合數據表定義的要求
import BootStrapMyFrom
class MyFrom(BootStrapMyFrom):
class Meta:
model = UserIfo
fields = ['name','password']
#文件裡form被定義了兩次,第一次沒數據只是實例化一個類,第二次包含了數據,用於驗證和寫入,所以盡量不改變他
def zhuce(request):
form = MyForm()
return render(request,'c.html', {
'forms':form})
form = MyFrom(data=request.POST)
#判斷數據是否合法
if form.is_valid():
#數據合法自動提交到數據庫表名為MyFrom裡定義的model的表(數據寫入)
form.save()
return redirect('/zhuce')
else:
return render(request,'c.html', {
'forms':form})
#數據不合法校驗失敗
<from method="post">
{% for field in form %}
{
{ field.label }}:{
{ field }}
{
{ field.errors.0 }}<!--顯示第一個錯誤-->
{% endfor %}
</from>
錯誤信息改中文

在settings.py文件裡改LANDUAGE_CODE = ‘zh-hans’

添加驗證項(重寫更多校驗)
from django import forms
class MyFrom(Form.ModelForm):
#添加校驗name字段最小長度為3
name = forms.CharFiled(min_length=3,label='用戶名')
class Meta:
model = UserIfo
fields = ['name','password']
widgets = {

'name':forms.TextInput(attrs={
"class":"form-control"})
'password':forms.PasswordInput(attrs={
"class":"form-control"})
}

修改數據

#nid是url攜帶過來的參考上面 10 urls.py
def zhuce(request,nid):
if request.method == 'GET':
#從數據庫獲取id為要修改那行的id的一行數據
ord_object = models.UserInfo.objects.filter(id=nid).first()
#給修改頁面添加默認值 
form = MyForm(instance = ord_object)
return render(request,'c.html', {
'forms':form})
else:
ord_object = models.UserInfo.objects.filter(id=nid).first()
#告訴jango不是新增而是更新到id=nid那一行(編輯數據)
form = MyFrom(data=request.POST,instance = ord_object)
if form.is_valid():
#默認保存用戶輸入的值,如果想要在用戶輸入的值以外增加一些值
#form.instance.字段名 = 值
form.save()
return redirect('/zhuce')
else:
return render(request,'c.html', {
'forms':form})

格式校驗

方式一(正則方法)
from django import forms
from django.core.validators import RegexValidator
class MyFrom(Form.ModelForm):
#validators後面可以寫正則表達式 ^開始 $結束 [3-9]3到9裡的一個數字 \d{9}9位數字
phone = forms.CharFiled(label='手機號',validators=[RegexValidator(r'^1[3-9]\d{9}$','必須以159開頭')])
class Meta:
model = UserIfo
fields = ['name','password']
widgets = {

'name':forms.TextInput(attrs={
"class":"form-control"})
'password':forms.PasswordInput(attrs={
"class":"form-control"})
}
方式二(鉤子方法)
from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
class MyFrom(Form.ModelForm):
#validators後面可以寫正則表達式 ^開始 $結束 [3-9]3到9裡的一個數字 \d{9}9位數字
phone = forms.CharFiled(label='手機號',validators=[RegexValidator(r'^1[3-9]\d{9}$','必須以159開頭')])
class Meta:
model = UserIfo
fields = ['name','password']
#在函數裡新建一個函數用於驗證
def clean_phone(self):
#獲取當前編輯那一行的id(用法配合9.2.3.5.1排除自己使用)
self.instance.pk
#獲取用戶輸入的phone值
txt_phone = self.cleaned_data["phone"]
if len(txt_phone) != 11:
#驗證不通過拋出錯誤信息
raise ValidationError('格式錯誤')
#驗證提供,把用戶輸入的值返回(返回的值就修改了用戶輸入的phone值,如果寫入數據庫就會寫入返回的值,我們不能隨意返回值)
return txt_phone
不讓浏覽器幫我們校驗
<!--novalidate不讓浏覽器幫我們校驗-->
<form novalidate>
</form>

12.5 分頁

#類似於for in 取列表 並拼成一句字符串
list=[]
lists = "".join(list)

後端寫的標簽傳到前端

#需要攔截非法字符(xss攻擊)
#需要把字符串標記為安全的
from django.utils.safestring import mark_safe
#包裹後傳入前端即可編譯
mark_safe(str)
#也可以在前端裡
{
{
 obj.title|safe }}

計算需要分幾頁

divmod(100,3) #得到--->(33,1)商33余1
a,b = divmod(100,3) #a=33 b=1
#如果b不為空頁數就是a+1
#b為空時為False
if b:

封裝一個分頁類運用到所有頁面

class Pagination(object):
def __init__(self,request,page_param="page")
page = page_param="page"
#.isdecimal()如果字符串是否只包含十進制字符返回True,否則返回False。(判讀是不是int類型)
if page.isdecimal():
page = int(page)
else:
page = 1
self.page = page

要注意分頁的時候把搜索值帶上

import copy
#深拷貝一份
get_object = copy.deepcopy(request.GET)
#修改django裡的._mutable為True才可以修改值
get_object._mutable = True
#設置值-->127.0.0.1/url?a=12
get_object.setlist("a",[12])

直接跳轉無法攜帶搜索值

案例

分頁類

class Page(object):
''' 自定義分頁類 可以實現Django ORM數據的的分頁展示 輸出HTML代碼: 使用說明: from utils import mypage page_obj = mypage.Page(total_num, current_page, 'publisher_list') publisher_list = data[page_obj.data_start:page_obj.data_end] page_html = page_obj.page_html() 為了顯示效果,show_page_num最好使用奇數 '''
def __init__(self, total_num, current_page, url_prefix, per_page=10, show_page_num=9):
''' :param total_num: 數據總條數 :param current_page: 當前訪問的頁碼 :param url_prefix: 分頁代碼裡a標簽的前綴 :param per_page: 每一頁顯示多少數據 :param show_page_num: 頁面上最多顯示多少個頁碼 '''
self.total_num = total_num
self.url_prefix = url_prefix
self.per_page = per_page
self.show_page_num = show_page_num
# 根據傳入的值計算當前頁碼左右放置多少個頁碼
self.half_show_page_num = self.show_page_num // 2
# 計算當前數據總共需要多少頁碼
total_page, more = divmod(self.total_num, self.per_page)
# 如果有余數就把頁碼加1
if more:
total_page += 1
self.total_page = total_page
# 數據有效性校驗
try:
current_page = int(current_page)
except Exception as e:
current_page = 1
# 如果當前頁碼數大於總頁碼數,默認展示最後一頁數據
if current_page > self.total_page:
current_page = self.total_page
# 如果當前頁碼小於1,默認展示第一頁數據
if current_page < 1:
current_page = 1
self.current_page = current_page
# 計算頁面上需要展示的頁碼范圍
# 頁碼過小處理
if self.current_page - self.half_show_page_num <= 1:
page_start = 1
page_end = show_page_num
# 頁碼過大處理
elif self.current_page + self.half_show_page_num >= self.total_page:
page_end = self.total_page
page_start = self.total_page - self.show_page_num + 1
# 未超過正常范圍
else:
page_start = self.current_page - self.half_show_page_num
page_end = self.current_page + self.half_show_page_num
# 計算得到的最終頁碼數
self.page_start = page_start
self.page_end = page_end
# 如果計算得到的頁碼數比總共需要展示頁碼數多,則把頁碼結束指定為總頁碼數
if self.page_end > self.total_page:
self.page_end = self.total_page
@property
def data_start(self):
''' :return: 返回當前頁面應該從哪裡開始切數據 '''
return (self.current_page - 1) * self.per_page
@property
def data_end(self):
''' :return: 返回當前頁面從哪裡結束數據 '''
return self.current_page * self.per_page
def page_html(self):
li_list = []
# 添加前面的nav和ul標簽
li_list.append(""" <nav aria-label="Page navigation"> <ul class="pagination"> """)
# 添加首頁
li_list.append('<li><a href="/{}/?page=1">首頁</a></li>'.format(self.url_prefix))
# 添加上一頁
if self.current_page <= 1: # 沒有上一頁
prev_html = '<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'
else:
prev_html = '<li><a href="/{}/?page={}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(
self.url_prefix,
self.current_page - 1)
li_list.append(prev_html)
# 循環添加內部所有的頁碼
for i in range(self.page_start, self.page_end + 1):
if i == self.current_page:
tmp = '<li class="active"><a href="/{0}/?page={1}">{1}</a></li>'.format(self.url_prefix, i)
else:
tmp = '<li><a href="/{0}/?page={1}">{1}</a></li>'.format(self.url_prefix, i)
li_list.append(tmp)
# 添加下一頁
if self.current_page >= self.total_page: # 表示沒有下一頁
next_html = '<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>'
else:
next_html = '<li><a href="/{}/?page={}" aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>'.format(
self.url_prefix, self.current_page + 1)
li_list.append(next_html)
# 添加尾頁
li_list.append('<li><a href="/{}/?page={}">尾頁</a></li>'.format(self.url_prefix, self.total_page))
# 添加結尾nav和ul標簽
li_list.append(""" </ul> </nav> """)
# 將生成的標簽拼接成一個大字符串
page_html = ''.join(li_list)
return page_html

使用辦法

# 獲取分頁所需數據
total_num = data.count() # 獲取數據總計條數
current_page = request.GET.get('page') # 獲取當前頁頁碼
url_prefix = request.path_info.strip('/') # 獲取a標簽所需參數
# 調用分頁類
# 調用類傳入參數生成實例
page_obj = mypage.Page(total_num, current_page, url_prefix, per_page=1)
# 按照分頁所需數據對總數據進行分割獲取當前頁面所需展示的數據
data = data[page_obj.data_start:page_obj.data_end]
# 獲取HTML代碼 
page_html = page_obj.page_html()
#封裝成字典傳入前端即可

12.6 時間選擇插件

參考

<!-- bootstrap樣式表 -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<!-- 時間選擇器樣式表 -->
<link href="https://cdn.bootcss.com/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css" rel="stylesheet">
<!-- jquery -->
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<!-- bootstrap腳本 -->
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- 時間選擇器前置腳本 -->
<script src="https://cdn.bootcss.com/moment.js/2.22.1/moment-with-locales.min.js"></script>
<!-- 時間選擇器核心腳本 -->
<script src="https://cdn.bootcss.com/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>
  • jquery版本:可選用 1.8.3 到 3.3.1(最新版樣式出不來)
  • bootstrap版本:選用v3的

Script標簽

參考

<script type="text/javascript"> $('#date').datetimepicker({
 forceParse: 0,//設置為0,時間不會跳轉1899,會顯示當前時間。 language: 'zh-CN',//顯示中文 format: 'yyyymmdd',//顯示格式 minView: "month",//設置只顯示到月份 initialDate: new Date(),//初始化當前日期 autoclose: true,//選中自動關閉 todayBtn: true//顯示今日按鈕 }) $("#date").datetimepicker("setDate", new Date() ); //設置顯示默認當天的時間 </script>

12.7ModelFrom和BootStrap

方法一

定義樣式
from django import forms
class MyFrom(Form.ModelForm):
xx = models.CharField(verbose_name='確認密碼',max_length=16)
#不僅支持去models裡獲取字段也能自定義字段
class Meta:
model = UserIfo
fields = ['name','password','xx']
#定義標簽的屬性(逐一添加屬性)
#render_value=True報錯不清空,一般默認不清空但是密碼默認會清空
widgets = {

'name':forms.TextInput(attrs={
"class":"form-control"})
'password':forms.PasswordInput(attrs={
"class":"form-control"},
render_value=True)
}
from django import forms
class MyFrom(Form.ModelForm):
xx = models.CharField(verbose_name='父母',max_length=16)
#不僅支持去models裡獲取字段也能自定義字段
class Meta:
model = UserIfo
fields = ['name','password','xx']
def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs)
# 循環找到所有插件,添加class屬性值
# name是字段名
for name, field in kwargs.items():
#字段為password的不加class屬性
#if name == "password":
# continue
# 添加class屬性 顯示數據庫verbose_name
field.widget.attrs = {
"class":"from-contorl","placeholder":field.label}

優化

from django import forms
class MyFrom(Form.ModelForm):
class Meta:
model = UserIfo
fields = ['name','password']
def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs)
#循環找到所有插件,添加class屬性值
for name, field in kwargs.items():
if field.widget.attrs:
field.widget.attrs['class'] = 'form-control'
field.widget.attrs['placeholder'] = field.label
field.widget.attrs = {
"class":"from-contorl","placeholder":field.label}

自定義類

from django import forms
class BootStrapMyFrom(Form.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs)
for name, field in kwargs.items():
if field.widget.attrs:
field.widget.attrs['class'] = 'form-control'
field.widget.attrs['placeholder'] = field.label
field.widget.attrs = {
"class":"from-contorl","placeholder":field.label}
用法
import BootStrapMyFrom
class MyFrom(BootStrapMyFrom):
class Meta:
model = UserIfo
fields = ['name','password']

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-SrcJ7O7U-1657435349718)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508011705707.png)]

13 目錄

創建APP:python mange.py startapp app名
HelloWorld
|-- HelloWorld
| |-- __init__.py
| |-- settings.py
| |-- urls.py
| `-- wsgi.py
|
|--app01
| |--templates
| |--utils (自己寫的模塊)
| |--static
| | |--css
| | |--js
| | |--img
| | `--plugins
| |
| |--__init__.py
| |--admin.py [固定,不用動](django默認提供admin後台管理)
| |--apps.py [固定,不用動](app啟動類)
| |--migrations [固定,不用動](數據庫變更記錄)
| | `--__init__.py
| |
| |--models.py [重要](對數據庫的操作)
| |--tests.py [固定,不用動](單元測試)
| `--views.py [重要](函數)
|
`-- manage.py

14 MD5加密存儲到數據庫

正常MD5加密


import hashlib
def md5(password):
obj = hashlib.md5()
obj.update(data_string.encode('utf-8'))
return obj.hexdigest()

鹽值加密

md5密文是固定的所以一般在自己用的時候添加上自己的鹽值
import hashlib
def md5(password):
yan = 'xxx'
obj = hashlib.md5(yan.encode('utf-8'))
obj.update(data_string.encode('utf-8'))
return obj.hexdigest()

用Django為我們生成的鹽來加密

from django.conf import settings
import hashlib
def md5(password):
obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))
obj.update(data_string.encode('utf-8'))
return obj.hexdigest()

15 用戶登錄

cookie和session

  • cookie:保存在浏覽器的鍵值,發送請求時自動攜帶
  • session(數據庫【Django默認】、redis、文件)

解決了

  • 無狀態&短鏈接(一次請求和響應過後就會斷開連接)

訪問網頁的時候服務器給浏覽器返回響應體和響應頭

  • 響應體(請求的內容…)
  • 響應頭(cookie…)
class LoginForm(forms.Form):
username forms.CharField(
label="用戶名",
widget=forms.TextInput(attrs={
"class":"form-control"})
)
password forms.CharField(
label="密碼",
widget=forms.PasswordInput(attrs={
"class":"form-control"})
)

校驗密碼

def login(request):
#登錄
if request.method =="GET":
form = LoginForm()
return render(request,'login.html',{
'form':form})
form = LoginForm(data=request.POST)
if form.is_valid():
admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
if not admin_object:
#自定義一個校驗錯誤
#form.add_error("password","用戶名或密碼錯誤")
form.add_error("username,"用戶名或密碼錯誤")
return render(request,'login.html',{
'form':form})
return HttpResponse("提交成功")
return render(request,'login.html',{
'form':form})

cookie和session生成

def login(request):
#登錄
if request.method =="GET":
form = LoginForm()
return render(request,'login.html',{
'form':form})
form LoginForm(data=request.POST)
if form.is_valid():
admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
if not admin_object:
#自定義一個校驗錯誤
#form.add_error("password","用戶名或密碼錯誤")
form.add_error("username,"用戶名或密碼錯誤")
return render(request,'login.html',{
'form':form})
#網站生成隨機字符串;寫到用戶浏覽器的cookie中;再寫到session中
#服務器裡就保存了一個info={'id':1,'name':佩奇} 
#session保存到了django_session表裡
request.session["info"] = {
'id':admin_object.id,'name':admin_object.username}
return HttpResponse("提交成功")
return render(request,'login.html',{
'form':form})

判斷有沒有session

在其它需要登錄的頁面加上

def login(request):
#取到上面存入數據庫的字典info={'id':1,'name':佩奇}
#沒有登錄cookie會返回一個None
a= request.session.get['info']
#如果a沒有值
if not a:
return redirect('/denglu')
else:
pass

注:用中間件代替

中間件

中間件文件夾(middleware)

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zdE4yiDn-1657435349720)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508031645437.png)]

from django.utils.deprecation import MiddlewareMixin
class C1(MiddlewareMixin):
# 中間件1
def process_request(self, request):
print('C1,來了')
def process_response(self, request, response):
print('C1,走了')
return response
class C2(MiddlewareMixin):
# 中間件2
def process_request(self, request):
#如果方法裡沒有返回值,那麼安裝黑色路徑繼續走
#有返回值就直接走紅色路徑返回
print('C2,來了')
def process_response(self,request, response):
print('C2,走了')
#必須返回response
return response

運行結果

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1icQk9Rr-1657435349723)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508033210917.png)]

應用中間件

在settings.py裡的MIDDLEWARE列表裡把中間件添加上去

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-kW6HRcZc-1657435349723)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508033305980.png)]

中間件實現登錄校驗

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect
class AuthMiddleware(MiddlewareMixin):
# 中間件1
def process_request(self, request):
# 排除登錄頁面的請求
# 獲取當前用戶訪問的url /login/
if request.path_info == '/login/':
return None
#讀取當前訪問的用戶session信息
info_dict = request.session.get('info')
if info_dict:
return
else:
return redirect('/login/')
def process_response(self, request, response):
return response

注銷

def login(request):
a= request.session.clear()
return rdirect('/denglu/')

獲取用戶登錄的session信息

dict = request.session['info']
<!--前端直接獲取-->
{
{ request.session.info.name }}
{
{ request.session.info.id }}

圖片驗證碼

python生成圖片

PIL庫

pip install pillow
#生成圖片
from PIL import Image,
img = Image.new(mode='RGB',size=(120,30),color=(255,255,255))
with open('./code.png','wb')as f:
img.save(f,format='png')

完善集成代碼

用法
from Image_authentication import get_code
#內存中的文件
from io import BytesIO
#重寫一個url用於驗證圖片
def images_code(request):
image_txt,image = get_code()
#把驗證碼寫進session裡
request.session['image_txt']=image_txt
#設置session超時60後驗證碼失效
request.session.set_expiry(60)
Memory_file = BytesIO()
image.save(Memory_file,'png')
#把內存裡的圖片返回給頁面
return Httpresponse(Memory_file.getvalue())
ModelFrom添加圖片驗證框

參考上面自定義字段

驗證碼校驗(登錄的視圖函數)
def login(request):
#登錄
if request.method =="GET":
form = LoginForm()
return render(request,'login.html',{
'form':form})
form LoginForm(data=request.POST)
if form.is_valid():
admin_object=models.Admin.objects.filter(**form.cleaned_data).first()
if not admin_object:
#因為後面還要用用戶名和密碼去數據庫驗證,所以要用pop把驗證碼提取並剔除,驗證碼不會去數據庫校驗
user_input_code = form.cleaned_data.pop('code') #用戶輸入的驗證碼
#因為驗證碼可能為空所以後面加上取空值
user_code= request.session.get('image_code','') #session裡正確的驗證碼
if user_input_code != user_code:
form.add_error("code","驗證碼錯誤")
return render(request,'login.html',{
'form':form})
form.add_error("username,"用戶名或密碼錯誤")
return render(request,'login.html',{
'form':form})
request.session["info"] = {
'id':admin_object.id,'name':admin_object.username}
#設置驗證成功一天免登錄(session保存1天)
request.session.set_expiry(60*60*24)
return HttpResponse("提交成功")
return render(request,'login.html',{
'form':form})

16 Ajax請求

浏覽器向服務器發送的請求以:向URL發送表單的形式提交

  • GET

  • POST

    每次操作都要刷新頁面

Ajax:偷偷發送請求(不刷新)

  • 依賴jQuery
  • Ajax代碼
$.ajax({

url:'xxx',
type:'get',
data:{

n1:12,
n2:23
},
//發送成功自動執行
success:function(res){

console.log(res);
}
})

GET請求

$.ajax({

url:'/task/ajax/',
type:'get',
data:{

n1:12,
n2:23
},
success:function(res){

console.log(res);
}
})

後端

def task_ajax(request):
request.GET
return HttpResponse(request.GET)

POST請求

$.ajax({

url:'/task/ajax/',
type:'post',
data:{

n1:12,
n2:23
},
success:function(res){

console.log(res);
}
})
可以用上面的批量綁定click點擊事件
//ajxa默認不攜帶cookie
//解決辦法
from django.views.decorators.csrf import csrf_exempt
在函數上面加上
@csrf_exempt
def task_ajax(request):
request.GET
return HttpResponse(request.GET)

Ajax請求的返回值

  • 返回類型:json

後端

$.ajax({

url:'/task/ajax/',
type:'post',
data:{

n1:12,
n2:23
},
dataType:'JSON'
success:function(res){

console.log(res);
console.log(res.a);
console.log(res.b);
}
})
def task_ajax(request):
dict = {

'a':12,
'b':34
}
#以json格式返回前端 JsonResponse(dict) = Httpresponse(json.dumps(dict))
return JsonResponse(dict)

頁面上的數據寫入ajax

$.ajax({

url:'/task/ajax/',
type:'post',
data:{

user:$('#user').val(),
password:$('#password').val()
},
dataType:'JSON'
success:function(res){

console.log(res);
console.log(res.a);
console.log(res.b);
}
})

自動寫ajax傳入後端的值

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-GcGu7f9b-1657435349725)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508232152198.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-nCcUSutw-1657435349730)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508232228145.png)]

案例

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5HEbuD1g-1657435349731)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508234558328.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-7KX9p2Eu-1657435349733)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508232228145.png)]

html

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Z7agGnsT-1657435349735)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508234838719.png)]

把錯誤寫到html

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oUf1AekS-1657435349748)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220508234658363.png)]

17 上傳文件

基本操作

  • request.FILES 請求發送過來的文件

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-GvV0sKGQ-1657435349751)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220509002043979.png)]

不加enctype默認只是上傳文件名,加上才上傳了真正的文件

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XS9fJ6f7-1657435349754)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220509002303320.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-aLGFaAPf-1657435349755)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220509002455007.png)]

  • file_object.name 文件名

  • file_object.chunks 文件數據

  • chunk 文件裡的一部分數據

    一部分一部分的讀取

​ 用form生成input標簽

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-X1x0aXny-1657435349755)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220509004540163.png)]

寫上第二行的就排除img標簽不給他加上bootstrap樣式

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fU1JPpxV-1657435349756)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220509004755564.png)]

上傳文件校驗的時候一定要加上files

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-YMwlT853-1657435349760)(C:\Users\22143\AppData\Roaming\Typora\typora-user-images\image-20220609234355937.png)]

dataType:'JSON'
success:function(res){
console.log(res);
console.log(res.a);
console.log(res.b);
}

})


```python
def task_ajax(request):
dict = {
'a':12,
'b':34
}
#以json格式返回前端 JsonResponse(dict) = Httpresponse(json.dumps(dict))
return JsonResponse(dict)

頁面上的數據寫入ajax

$.ajax({

url:'/task/ajax/',
type:'post',
data:{

user:$('#user').val(),
password:$('#password').val()
},
dataType:'JSON'
success:function(res){

console.log(res);
console.log(res.a);
console.log(res.b);
}
})

自動寫ajax傳入後端的值

[外鏈圖片轉存中…(img-GcGu7f9b-1657435349725)]

[外鏈圖片轉存中…(img-nCcUSutw-1657435349730)]

案例

[外鏈圖片轉存中…(img-5HEbuD1g-1657435349731)]

[外鏈圖片轉存中…(img-7KX9p2Eu-1657435349733)]

html

[外鏈圖片轉存中…(img-Z7agGnsT-1657435349735)]

把錯誤寫到html

[外鏈圖片轉存中…(img-oUf1AekS-1657435349748)]

17 上傳文件

基本操作

  • request.FILES 請求發送過來的文件

[外鏈圖片轉存中…(img-GvV0sKGQ-1657435349751)]

不加enctype默認只是上傳文件名,加上才上傳了真正的文件

[外鏈圖片轉存中…(img-XS9fJ6f7-1657435349754)]

[外鏈圖片轉存中…(img-aLGFaAPf-1657435349755)]

  • file_object.name 文件名

  • file_object.chunks 文件數據

  • chunk 文件裡的一部分數據

    一部分一部分的讀取

​ 用form生成input標簽

[外鏈圖片轉存中…(img-X1x0aXny-1657435349755)]

寫上第二行的就排除img標簽不給他加上bootstrap樣式

[外鏈圖片轉存中…(img-fU1JPpxV-1657435349756)]

上傳文件校驗的時候一定要加上files

[外鏈圖片轉存中…(img-YMwlT853-1657435349760)]


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved