樓主之前做了一個(gè)IT新聞聚合的網(wǎng)站叫三四秒,這個(gè)網(wǎng)站是用爬蟲(chóng)直接把數(shù)據(jù)抓取到數(shù)據(jù)庫(kù)蕊唐,然后在前臺(tái)搭建個(gè)頁(yè)面展示出來(lái)屋摔,所以樓主我只要隔山差五檢查一下爬蟲(chóng)是否正常運(yùn)作就行,這個(gè)項(xiàng)目沒(méi)有User用戶模塊替梨、UGC模塊,也沒(méi)有Comment模塊装黑,所以理所當(dāng)然地樓主從來(lái)沒(méi)有實(shí)踐過(guò)圖片怎么上傳和展示副瀑。
那今天樓主決定勇敢的跨出這一步。我們做一個(gè)注冊(cè)頁(yè)面恋谭,填寫一個(gè)用戶名糠睡、上傳一個(gè)圖片,點(diǎn)擊提交后跳轉(zhuǎn)到注冊(cè)成功頁(yè)面并把圖片上傳到我們服務(wù)器上疚颊,最后在注冊(cè)成功頁(yè)面把剛剛的用戶名和圖片顯示出來(lái)狈孔。
1、創(chuàng)建一個(gè)項(xiàng)目名字叫mysite
(要不是因?yàn)閼械闷鹈植囊澹趺纯赡芙衜ysite這么沒(méi)有創(chuàng)意的名字呀喂)均抽。
django-admin startproject mysite
2笨奠、進(jìn)入到mysite
文件夾幕侠,創(chuàng)建一個(gè)app
應(yīng)用叫my_reg
(好吧,我承認(rèn)起名字是編程界最難的事了)暇检。
cd mysite
django-admin startapp my_reg
3款熬、立刻把my_reg
這個(gè)app
添加在settings.py
中深寥。
INSTALLED_APPS = (
...
'my_reg',
)```
4、第一步當(dāng)然是創(chuàng)建數(shù)據(jù)了贤牛,也就是User模型惋鹅,用來(lái)儲(chǔ)存用戶名和圖片……的路徑。
from django.db import models
這里的上傳路徑就是mysite/upload/xxx.jpg
class User(models.Model):
username = models.CharField(max_length=20)
headImg = models.FileField(upload_to='./upload/')```
5殉簸、創(chuàng)建完數(shù)據(jù)模型闰集,要養(yǎng)成好習(xí)慣,就是同步一下數(shù)據(jù)庫(kù)喂链。
python manage.py makemigratons
python manage.py migrate
6返十、然后我們就開(kāi)始寫路由url了,添加兩個(gè)url椭微,一個(gè)是注冊(cè)頁(yè)面洞坑,一個(gè)是注冊(cè)完成頁(yè)面∮剩看到下面這兩個(gè)放蕩不羈的名字迟杂,相信你應(yīng)該已經(jīng)知道哪個(gè)是哪個(gè)了吧刽沾。
urlpatterns = [
...
url(r'^register/$', 'my_reg.views.reg_index', name='my_regi'),
url(r'^register/done/$', 'my_reg.views.result', name='reg_done'),
] ```
7、既然我們?cè)诼酚蓇rl里寫到了`views`里的幾個(gè)函數(shù)排拷,那么我們這就去完成它侧漓。
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django import forms
from my_reg.models import User
創(chuàng)建一個(gè)form表單類
class UserForm(forms.Form):
username = forms.CharField()
headImg = forms.FileField()
第一次打開(kāi)頁(yè)面不是POST請(qǐng)求,所以走else那條路监氢,創(chuàng)建一個(gè)form表單布蔗,然后在前臺(tái)顯示。第二次點(diǎn)擊“提交”按鈕是POST請(qǐng)求浪腐,走if那條路:意思就是這個(gè)表單請(qǐng)求內(nèi)容有files文件纵揍,然后如果它們有數(shù)據(jù),就存到數(shù)據(jù)庫(kù)中议街,并且把用戶名放在session
中泽谨,最后跳轉(zhuǎn)到一個(gè)新的url去。
def reg_index(request):
if request.method == 'POST':
uf = UserForm(request.POST, request.FILES)
if uf.is_valid():
uname = uf.cleaned_data['username']
hImg = uf.cleaned_data['headImg']
u = User()
u.username = uname
u.headImg = hImg
u.save()
request.session['user_info'] = uname
return HttpResponseRedirect('/register/done/')
else:
uf = UserForm()
return render(request, 'my_reg/reg.html', {'uf': uf})
從session中找到這個(gè)用戶名特漩,按照用戶名找到數(shù)據(jù)庫(kù)中的用戶信息吧雹,把用戶信息展示出來(lái)。
def result(request):
uuu = User.objects.get(username=request.session['user_info'])
return render(request, 'my_reg/result.html', {'user': uuu})```
8涂身、views
里面用到了兩個(gè)html頁(yè)面雄卷,一個(gè)是注冊(cè)頁(yè)面,一個(gè)是注冊(cè)完成頁(yè)面访得,我們簡(jiǎn)單搭建一下:
# 注冊(cè)頁(yè)面
...
<h1>Register!</h1>
<form method="post" enctype="multipart/form-data" action="{% url 'my_regi' %}">
{% csrf_token %}
{{ uf.as_p }}
<input type="submit" value="OK"/>
</form>
...```
注冊(cè)成功頁(yè)面
...
<p>Result!</p>
<p>{{user.username}}</p>
<p>name done!</p>
<img src="這里放的是圖片的路徑" alt=""/>
...```
9龙亲、好了,這個(gè)上傳圖片和展示圖片的程序就做好了悍抑。
10鳄炉、這個(gè)原理是:只要我們合理的配置后,Django就會(huì)幫我們自動(dòng)上傳圖片搜骡。這個(gè)配置就是:
- 首先你得定義一個(gè)存放文件的字段
headImg = models.FileField(upload_to='./upload/')
拂盯,當(dāng)然這里要指定文件存放路徑。 - 然后你在表單中上傳文件后记靡,用
uf.cleaned_data['headImg']
取得文件谈竿,再把它賦值給我們的模型字段`u.headImg。 - 至此摸吠,我們點(diǎn)擊提交后空凸,Django就幫我們把文件上傳到定義的存放文件的路徑中,然后把文件路徑賦值給headImg路徑寸痢。
11呀洲、等等——看標(biāo)題貌似應(yīng)該著重講解上傳之后怎么使用圖片的吧?可為什么快結(jié)束了還在講怎么上傳啊道逗?
12滓窍、來(lái)了卖词,來(lái)了……我們可以打印出來(lái)儲(chǔ)存在數(shù)據(jù)庫(kù)中的路徑,咦吏夯,是這個(gè)樣子的:
13此蜈、我們直接在img
的src
中放這個(gè)路徑,試試噪生。
<img src="{{user.headImg}}" alt=""/>
14舶替、不行,圖片顯示的是:
15杠园、看看源代碼:
16舔庶、莫非是絕對(duì)路徑和相對(duì)路徑的問(wèn)題抛蚁?試一試在前面給它加上http://127.0.0.1:8000/
:
<img src="http://127.0.0.1:8000/{{user.headImg}}" alt=""/>
17、仍然不行惕橙,這個(gè)時(shí)候源代碼顯示的路徑是這個(gè)樣子的瞧甩,看樣子貌似已經(jīng)很完美了,但為什么就是不顯示了:
18弥鹦、這個(gè)時(shí)候肚逸,樓主已經(jīng)逐漸喪失理智,覺(jué)得肯定是Django在玩我彬坏。但是樓主修煉多年朦促,豈能因?yàn)檫@點(diǎn)小事失態(tài),于是樓主繼續(xù)各種stackoverflow栓始,google务冕,bing。有人說(shuō)是因?yàn)樯厦孢@個(gè)看似完美的路徑也是一個(gè)url幻赚,Django里面處理url都是要經(jīng)過(guò)路由設(shè)置的禀忆,你在路由里面沒(méi)設(shè)置當(dāng)然它不知道你這個(gè)用來(lái)干嘛。
19落恼、樓主頓時(shí)恍然大區(qū)箩退,說(shuō)的真好,那我就去路由里面設(shè)置吧佳谦,添加一行:
url(r'upload/([*]+)'),
20戴涝、出錯(cuò)了,提示說(shuō)這個(gè)需要2個(gè)參數(shù)。好吧喊括,再給你個(gè)參數(shù):
url(r'upload/([*]+)', name='handleImgUrl'),
21胧瓜、仍然提示出錯(cuò),需要2個(gè)參數(shù)郑什,Django仿佛在說(shuō):你TM在耍我么府喳,給勞資一個(gè)name參數(shù)是幾個(gè)意思?
22蘑拯、等等——我知道你想要一個(gè)下面這個(gè)樣子的:
url(r'upload/([*]+)', 'my_reg.views.XXX'),
23钝满、但是,我TM不知道我寫出這么個(gè)XXX函數(shù)后申窘,這個(gè)函數(shù)里寫什么啊弯蚜。我這里只是要一個(gè)url路徑而已,你還得逼我寫個(gè)函數(shù)剃法,樓主長(zhǎng)舒了一口氣碎捺,淡定——,寫就寫麼贷洲,大不了我這個(gè)XXX函數(shù)里面什么都都不寫收厨,直接寫個(gè)pass什么的糊弄一下。
24优构、結(jié)果還是不行诵叁。呼——呼——,接下來(lái)該怎么辦钦椭?容樓主想想拧额,圖片已經(jīng)上傳到服務(wù)器上了,現(xiàn)在全部問(wèn)題就在怎么把它顯示出來(lái)彪腔,急死了侥锦,先上個(gè)廁所。
25漫仆、上廁所回來(lái)了捎拯,網(wǎng)上還說(shuō),設(shè)置一下MEDIA_URL
和MEDIA_ROOT
盲厌,好吧署照,照著寫一下,在settings.py
中加上這兩個(gè)配置吗浩,然后在urlpatterns
中添加一下:
# 這是在settings中的設(shè)置
MEDIA_URL = '/upload/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'upload').replace("http://", "/")```
這是在urls.py中的設(shè)置
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)```
26建芙、寫完這一切,樓主心里已經(jīng)完全沒(méi)底了懂扼,這TM也能行禁荸?因?yàn)闃侵髦皇窃O(shè)置了一下media路徑右蒲,貌似是告訴Django媒體文件在哪,然后Django就能自動(dòng)在我們調(diào)用圖片的時(shí)候把圖片找出來(lái)并顯示赶熟。聽(tīng)著想那么回事瑰妄。
27、還是不行映砖,容勞資捋一捋间坐,不,容樓主捋一捋邑退,樓主已經(jīng)亂了竹宋。唉,對(duì)了地技,貌似是我img
里的src
寫的不對(duì)蜈七,我現(xiàn)在還在用的是:
<img src="http://127.0.0.1:8000/{{user.headImg}}" alt=""/>
但是我們已經(jīng)設(shè)置了媒體路徑了,應(yīng)該Django會(huì)自動(dòng)識(shí)別莫矗,不需要我們多此一舉寫那么多吧飒硅,刪掉變成下面那樣試試:
<img src="{{user.headImg}}" alt=""/>
28、還是仍然依然不行啊作谚,這可怎么辦狡相?樓主的實(shí)驗(yàn)看來(lái)是進(jìn)行不下去了啊,這么半途而廢實(shí)在不是樓主我的風(fēng)格啊食磕,樓主該怎么辦?樓主根本不認(rèn)識(shí)什么Django牛人啊喳挑,不知道請(qǐng)教誰(shuí)氨蚵住?而且這個(gè)貌似不是很難得事吧伊诵?這么請(qǐng)教別人是不是太沒(méi)面子了暗グ蟆?樓主自學(xué)編程這么多年了曹宴,什么困難沒(méi)見(jiàn)過(guò)搂橙,今天是要撲街了么?要振作啊笛坦,樓主区转!
29、容樓主理理思路:這TM不就是個(gè)路徑嘛版扩,路徑啊废离,url啊,懂不懂啊礁芦,相對(duì)路徑啊蜻韭,絕對(duì)路徑啊悼尾,你傻逼啊你——咦,好像有人在罵我——我直接改改路徑試試肖方,比如mysite/{{user.headImg}}
或者/{{user.headImg}}
或者./{{user.headImg}}
或者mysite/{{user.headImg}}
闺魏,依然不行啊,這肯定是一個(gè)坑俯画,既然是坑析桥,樓主決定再潛心修行,一會(huì)再戰(zhàn)活翩。
30烹骨、在看了N多文檔和文章之后,樓主好像懂了材泄。也就是說(shuō)上面第12步-第29步你可以忽略沮焕,直接從這里看怎么展示圖片。
31拉宗、首先峦树,我們看看models.py
里的模型,有個(gè)upload_to
參數(shù)旦事,為了和過(guò)去一刀兩斷魁巩,樓主決定給upload_to
賦值一個(gè)新的值叫avatar/
,這個(gè)參數(shù)的意思是把文件上傳到MEDIA_ROOT/avatar/
下面姐浮。
- 既然這里
upload_to
的值是連接在MEDIA_ROOT/
路徑后的一部分谷遂,所以很自然的只能寫成avatar/
或者./avatar/
,而不能寫成/avatar/
卖鲤,樓主已經(jīng)以身試法過(guò)肾扰。 - 還有一點(diǎn),這里提到了
MEDIA_ROOT
蛋逾,可是我們一直沒(méi)設(shè)置過(guò)啊集晚。
headImg = models.FileField(upload_to='avatar/')
32、所以理所當(dāng)然的要設(shè)置MEDIA_ROOT
区匣,所以在settings.py
中做如下設(shè)置偷拔,這里的意思就是說(shuō),我們?cè)陧?xiàng)目根目錄下會(huì)新建一個(gè)media
文件夾亏钩,專門用來(lái)存放media
文件莲绰。結(jié)合上面的設(shè)置可推出,我們上傳的文件會(huì)放在/media/avatar/
下:
MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace("\\", "/")```
33姑丑、這`MEDIA_ROOT`就是媒體根目錄的路徑钉蒲,這……好像是廢話。上傳的文件也會(huì)放在這里彻坛,但是正如我們上面探索時(shí)提到的:使用文件顷啼,實(shí)質(zhì)上也是調(diào)用了一個(gè)文件的url踏枣,在Django中提到url,都是要從`urlpatterns`中過(guò)濾一遍的钙蒙。
34茵瀑、所以,展示圖片的邏輯應(yīng)該是這樣的:我們調(diào)用圖片的url一般是有規(guī)律的躬厌,我們過(guò)濾的時(shí)候發(fā)現(xiàn)马昨,只要符合,就按照文件名從媒體根目錄中找相應(yīng)的文件扛施。
- 所以鸿捧,我們先找到圖片url的規(guī)律,都說(shuō)了疙渣,圖片都是存在`/media/avatar/`中匙奴,也就是說(shuō)圖片的路徑應(yīng)該是包含`/media/avatar/`的,為了保險(xiǎn)起見(jiàn)以及后續(xù)我們可能會(huì)存除了頭像之外的其他文件妄荔,比如儲(chǔ)存縮略圖的叫`/media/thumb/`泼菌,所以這里我們?nèi)〈蠹夜灿械腵/media/`作為過(guò)濾url的規(guī)律。
MEDIA_URL = '/media/'
- 這也就是為什么`MEDIA_ROOT`和`MEDIA_ROOT`經(jīng)常一起出現(xiàn)啦租,并且他們的有相同的值哗伯。
34、準(zhǔn)備好這些后篷角,在`urlpatterns`中寫吧焊刹,這里寫的路由和普通的路由不一樣,因?yàn)槲覀冞@里的所有的媒體文件其實(shí)都是靜態(tài)文件的一部分恳蹲,而且我們一般路由符合條件后是去執(zhí)行`views`中的某個(gè)函數(shù)伴澄,這里卻是去某個(gè)文件夾中找文件,所以肯定寫法上是不同的阱缓,寫法是`static(如果符合這樣規(guī)律的url,就去這個(gè)目錄中找文件)`:
導(dǎo)入這兩個(gè)包
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)```
35举农、如果你之前在探索的時(shí)候經(jīng)常會(huì)看看瀏覽器會(huì)輸出什么錯(cuò)誤荆针,你一般都是看到要么是404 error
,要么就是500 error
颁糟。為什么會(huì)出現(xiàn)404 error
航背,就是因?yàn)槲覀兘o的圖片路徑?jīng)]有在urlpatterns
中定義過(guò),所以Django在要展示圖片的時(shí)候棱貌,一看玖媚,咦,這什么鬼url婚脱,在urlpatterns
中根本沒(méi)有對(duì)應(yīng)的可以查今魔,所以是錯(cuò)誤的請(qǐng)求網(wǎng)址勺像,返回404 error
。在urlpatterns
中添加之后错森,就不會(huì)有404 error
了吟宦。
36、好了涩维,我們還剩下最后一步殃姓,就是在img
的src
中填寫正確的圖片地址。我們之前說(shuō)了圖片是儲(chǔ)存在/media/avatar/
下面的瓦阐,所以圖片的路徑就是:
<img src="/media/{{user.headImg}}" alt=""/>
- 你問(wèn)為什么蜗侈?因?yàn)槲覀儍?chǔ)存在數(shù)據(jù)庫(kù)中的圖片路徑是
upload_to
的值和圖片名稱的拼接,比如下面的avatar/test_mini.jpg
睡蟋。
37踏幻、成功了!
38薄湿、瞧叫倍,解決方案中,在settings
里設(shè)置MEDIA
的相關(guān)屬性豺瘤,然后在urlpatterns
中設(shè)置相關(guān)路由吆倦,這些我們?cè)谥暗奶剿髦卸加袊L試,但就是差那么一點(diǎn)點(diǎn)坐求。所以蚕泽,如果我們不懂原理,僅僅照搬修改幾個(gè)設(shè)置桥嗤,那么遠(yuǎn)遠(yuǎn)不能解決問(wèn)題须妻,雖然我們離答案曾經(jīng)那么近。
39泛领、還有荒吏,為毛網(wǎng)上那么多教你上傳圖片的教程,就是沒(méi)有教你顯示圖片的教程呀喂渊鞋!
40绰更、再見(jiàn)!