首先先進入我們的testsite項目下,打開members/models.py文件,先添加我們保存文件的數據模型:
class Document(models.Model):
name=models.CharField(max_length=255)
file=models.FileField(upload_to='uploads/') # 'uploads/' 是文件上傳的相對路徑
uploaded_at=models.DateTimeField(auto_now_add=True)
這里的文件存儲目錄可以自定義我們需要存儲文件的目錄,也可以在我們的 env文件 做配置,這里直接讀取相應的配置 env字段 就可以了。
進入testsite根目錄執行更新遷移文件命令,這一步前面有說到,是為了更新遷移文件,
py manage.py makemigrations
可以看到我們的migrations目錄增加了新的遷移文件
執行遷移
py manage.py migrate
我們新增加的文件模型表就在數據庫生成好了。
進入testsite/members目錄,新增form.py表單文件,這里會定義文件上傳的字段。
from django import forms
from .models import Document
class UploadFileForm(forms.ModelForm):
class Meta:
model=Document
fields=['name', 'file'] # 包含你想要用戶填寫的字段
接下來將上傳的文件綁定到表單中,打開testsite/members/views.py視圖文件:
from .forms import UploadFileForm #需要我們引入上一步建立好的表單文件
def upload_file(request):
if request.method=="POST":
form=UploadFileForm(request.POST, request.FILES)
if form.is_valid():
form.save() # 保存表單數據到數據庫
return HttpResponse("上傳成功")
else:
form=UploadFileForm()
return render(request, "polls/upload.html", {"form": form})
我們傳遞request.FILES 到表單的構造函數;這就是文件數據綁定到表單的方式。
請注意,request.FILES僅當請求方法為POST,實際發送了至少一個文件字段且<form>表單有發送請求的并且具有enctype="multipart/form-data"屬性時。否則request.FILES將為空。
最后一步,添加上傳頁面,進入members/templates/members目錄,新增upload.html文件,注意跟我們的視圖里面指向的文件名保持一致。
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">提交</button>
</form>
打開地址http://127.0.0.1:8000/polls/upload/進入文件上傳頁面
點寫完表單點擊提交
這個時候我們的文件上傳就完成了,可以打開我們的testsite目錄,進入我們設置的uploads目錄,就看到我們上傳好的文件:
在這里插入圖片描述
然后我們再打開數據庫,進入模型對應的members_document表,也可以看到文件上傳生成的數據。
在這里插入圖片描述
為了使用一個表單字段上傳多個文件,創建該字段的子類并將其allow_multiple_selected類屬性設置為True。
進入表單文件forms.py:
class MultipleFileInput(forms.ClearableFileInput):
allow_multiple_selected=True
class MultipleFileField(forms.FileField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", MultipleFileInput())
super().__init__(*args, **kwargs)
def clean(self, data, initial=None):
single_file_clean=super().clean
if isinstance(data, (list, tuple)):
result=[single_file_clean(d, initial) for d in data]
else:
result=[single_file_clean(data, initial)]
return result
class FileFieldForm(forms.Form):
file_field=MultipleFileField()
然后進入view.py視圖文件重寫子類form_valid()的方法 FormView來處理多個文件上傳:
from .forms import FileFieldForm # 確保導入正確的表單定義
from .models import Document # 引入Document模型
def batch_upload_view(request):
if request.method=='POST':
form=FileFieldForm(request.POST, request.FILES)
if form.is_valid():
# 獲取表單中title的值
title=form.cleaned_data['title']
# 處理文件上傳
for file in request.FILES.getlist('file_field'):
document=Document(file=file, name=title) # 假設title作為文件的名稱或描述
document.save()
return HttpResponse("上傳成功")
else:
# 請求方法為GET時,初始化表單
form=FileFieldForm()
return render(request, "polls/uploads.html", {"form": form})
假如你不想使用模型關聯上傳,則使用如下方式:
from django.core.files.storage import default_storage
#使用默認存儲引擎保存文件
file_name=default_storage.save(file.name, file)
然后重新渲染我們的批量上傳頁面,跟我們的forms django表單關聯
<form method="post" enctype="multipart/form-data"> <!-- enctype 必須為 multipart/form-data 以支持文件上傳 -->
{% csrf_token %} <!-- Django表單必須的安全令牌 -->
{{ form.title }}
{{ form.file_field }} <!-- 渲染文件上傳字段,MultipleFileField將會生成相應的input元素 -->
<button type="submit">上傳文件</button>
</form>
上面的 csrf_token django表單必須的安全令牌,form.file_field 也是我們form.py定義的批量上傳字段。
打開members/urls.py,添加我們的路由
path('uploads/', views.batch_upload_view, name='upload'),
我們的批量上傳功能就已經寫好了,我們打開http://127.0.0.1:8000/polls/uploads/頁面
填好表單,批量選擇文件,點擊上傳,這里我只是演示,沒有做頁面渲染樣式與字段驗證,感興趣的小伙伴可以加上自己喜歡的樣式。
然后進入我們的數據庫可以看到我們與上傳模型的記錄被添加
我們打開uploads目錄,也可以看到我們上傳好的文件。
需要注意的是在保存上傳的文件之前,數據需要存儲在某個地方。
默認情況下,如果上傳的文件小于 2.5 MB,django 會將上傳的全部內容保存在內存中。
這意味著保存文件僅涉及從內存讀取和寫入磁盤,因此速度非常快。
但是,如果上傳的文件太大,django 會將上傳的文件寫入存儲在系統臨時目錄中的臨時文件。
例如/tmp/tmpzfp6I6.upload。如果上傳的文件足夠大,您可以看到該文件的大小隨著 django 將數據傳輸到磁盤而增長
文件上傳算是一種很常見的需求,幾乎構建很多項目系統,以及插件都需要用到。
通過上面例子可以看到django通過forms表單的形式靈活定義文件上傳的頁面,字段,以及數據庫存儲。
在實際項目中我們還要考慮到安全性,包括檢查文件大小(可通過FileField的max_length屬性設置)、防止路徑遍歷攻擊。
還需注意服務器端的驗證,比如檢查文件類型、內容安全等。
例子里面有提到,django的FileSystemStorage和DefaultStorage類提供了基本的文件存儲管理。
自定義存儲后端可以提供更多靈活性,如自定義文件命名規則、存儲路徑等,以避免文件名沖突和優化組織結構。
大量文件上傳或下載也會影響應用性能。最好采用云存儲服務、內容分發網絡(CDN)、以及對靜態和媒體文件的有效緩存策略,可以顯著提升用戶體驗。
對于我們本地的存儲策略,需要考慮上傳文件的生命周期管理,包括舊文件的定期清理策略,以避免無限制增長占用存儲空間。
– 歡迎點贊、關注、轉發、收藏【我碼玄黃】,gonghao同名
道Django某一個功能的用途,比單純地知道怎么用更有用。
今天我們要學習的內容是Django的頁面跳轉,重點講的是使用url里的name參數
根據前面做好的web項目,登錄頁面的url為: http://127.0.0.1:8000/login/;如果我們想把路徑名login換成signin,該怎么做呢? 這時候我們就想到了在urls的urlpatterns里直接修改path路徑名,
如果views里對應的方法有重定向跳轉就麻煩了,因為重定向跳轉的redirect方法里已經寫了我們改名之前的路徑名。如果我們在url里將login改成signin,做了一次改名操作了,那么在views里我們還要再改一次。如果有很多個頁面都做了重定向到login頁面,我們就需要對每一個函數進行修改操作,太麻煩了!有沒有什么辦法能讓我們做路徑修改的時候,只需要改一處就能搞定的呢?
實際上Django給我們提供了URL的name屬性用來標記url,我們可以在path中給指定路徑設置一個name屬性,
urlpatterns = [
path('signin/', app01_views.login, name="login"),
]
在views中做重定向時不直接寫死路徑名,而是通過reverse('屬性名')反向查出前面的url,這樣修改路徑名就很方便了,直接修改path就可以了。
return redirect(reverse('login'))
通常情況下,我們是根據path里的路徑名,找到views里的方法,然后渲染頁面。而這里的重定向反轉操作是根據路徑名的name屬性,反向找到路徑的名稱。 這樣我們需要修改路徑名的時候,views里都不要動的,反正它找的是指定的名稱。name屬性規定好了之后,url路徑名想怎么改就怎么改。
注意:使用反轉路徑名的方法,需要導入包from django.shortcuts import reverse。
當前階段我們主要用在兩處: 在View中使用redirect(reverse("login"));
def index(request):
username = request.GET.get('username')
if username:
return render(request,'index.html')
else:
return redirect(reverse('login'))
在html中使用模板語言{% url 'login' %};
<a href="{% url 'login'%}">登錄</a>
我們先復習一下url傳值的兩種常見方法:
(1)直接使用url傳值
以傳遞電影編號movie_id為例,如果想通過url傳值。在定義path的時候,使用path("movile_detail/<movie_id>",...),然后在views里定義方法來接收movie_id這個值,就定義成def movie_id(request,movie_id),瀏覽器中訪問的時候可以通過127.0.0.1:8000/movie_detail/8001就能把movie_id作為參數通過url傳過去。
(2)使用查詢字符串傳值
我們還是以傳遞電影編號為例,如果想通過url傳值。在定義path時,使用path("movie_detail/",...),在views里定義方法來接收就寫成
def movie_id(request):
movie_id = request.GET.get(movie_id)
在瀏覽器中訪問是,url需要輸入127.0.0.1:8000/?movie_id=8001。這樣也能完成url的傳值。
(3)傳遞參數
在views中,我們使用redirect重定向到一個新的url如果要傳遞參數該怎么辦呢? 有兩種方式:
url設置如下:
urlpatterns = [
# 登錄url
path('login/<username>/<password>',app01_views.login, name='login'),
]
在views里定義login方法準備接收兩個參數:
def login(request, username, password):
return HttpResponse("用戶名:%s \t 密碼:%s" % (username, password))
傳遞參數時:
def index(request):
username = request.GET.get('username')
if username:
return HttpResponse('=== 這是首頁 ===')
else:
return redirect(reverse('login', kwargs={'username': 'rico', 'password': '123'}))
注意:是在reverse方法里添加參數,傳的如果是元組則注意前后順序,如果是字典就不用考慮前后順序。
如果要在templates中通過模板語言調用url的名稱, 模板語言里使用url傳遞參數可以寫成這樣:
{% url 'login' username='xiaoyu' password='123' %}
在多app項目中,如果各自的name屬性相同,在重定向訪問的時候會出現無法訪問指定app下的url的問題。這時候我們就在各自app的urls中添加命名空間
app_name = 'app01'
然后在重定向訪問指定url名稱的時候,添加命名空間屬性即可。
def index(request):
username = request.GET.get('username')
if username:
return render(request, 'app01/index.html')
else:
return redirect(reverse('app01:login'))
注意:項目總體url配置使用include關鍵字,將多個app配置到總路由中。
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls')),
path('app02/',include('app02.urls')),
]
這一節,我們主要介紹了Django中url路由跳轉的進階知識,下一節,我們將介紹Django下html頁面中的模板語言DTL,感謝大家的閱讀~
*請認真填寫需求信息,我們會在24小時內與您取得聯系。