【4】Django上传图片后怎么使用图片呀喂?

楼主之前做了一个IT新闻聚合的网站叫三四秒,这个网站是用爬虫直接把数据抓取到数据库,然后在前台搭建个页面展示出来,所以楼主我只要隔山差五检查一下爬虫是否正常运作就行,这个项目没有User用户模块、UGC模块,也没有Comment模块,所以理所当然地楼主从来没有实践过图片怎么上传和展示。

那今天楼主决定勇敢的跨出这一步。我们做一个注册页面,填写一个用户名、上传一个图片,点击提交后跳转到注册成功页面并把图片上传到我们服务器上,最后在注册成功页面把刚刚的用户名和图片显示出来。

1、创建一个项目名字叫mysite(要不是因为懒得起名字,怎么可能叫mysite这么没有创意的名字呀喂)。

django-admin startproject mysite

2、进入到mysite文件夹,创建一个app应用叫my_reg(好吧,我承认起名字是编程界最难的事了)。

cd mysite
django-admin startapp my_reg

3、立刻把my_reg这个app添加在settings.py中。

INSTALLED_APPS = (
    ...
    'my_reg',
)```

4、第一步当然是创建数据了,也就是User模型,用来储存用户名和图片……的路径。

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、创建完数据模型,要养成好习惯,就是同步一下数据库。

python manage.py makemigratons
python manage.py migrate

6、然后我们就开始写路由url了,添加两个url,一个是注册页面,一个是注册完成页面。看到下面这两个放荡不羁的名字,相信你应该已经知道哪个是哪个了吧。

urlpatterns = [
    ...
    url(r'^register/$', 'my_reg.views.reg_index', name='my_regi'),
    url(r'^register/done/$', 'my_reg.views.result', name='reg_done'),
] ```

7、既然我们在路由url里写到了`views`里的几个函数,那么我们这就去完成它。

from django.shortcuts import render
from django.http import HttpResponseRedirect
from django import forms
from my_reg.models import User

创建一个form表单类

class UserForm(forms.Form):
username = forms.CharField()
headImg = forms.FileField()

第一次打开页面不是POST请求,所以走else那条路,创建一个form表单,然后在前台显示。第二次点击“提交”按钮是POST请求,走if那条路:意思就是这个表单请求内容有files文件,然后如果它们有数据,就存到数据库中,并且把用户名放在session中,最后跳转到一个新的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中找到这个用户名,按照用户名找到数据库中的用户信息,把用户信息展示出来。

def result(request):
uuu = User.objects.get(username=request.session['user_info'])
return render(request, 'my_reg/result.html', {'user': uuu})```

8、views里面用到了两个html页面,一个是注册页面,一个是注册完成页面,我们简单搭建一下:

# 注册页面
...
    <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>
...```

注册成功页面

...
<p>Result!</p>
<p>{{user.username}}</p>
<p>name done!</p>
<img src="这里放的是图片的路径" alt=""/>
...```

9、好了,这个上传图片和展示图片的程序就做好了。

有种别走啊

10、这个原理是:只要我们合理的配置后,Django就会帮我们自动上传图片。这个配置就是:

  • 首先你得定义一个存放文件的字段headImg = models.FileField(upload_to='./upload/'),当然这里要指定文件存放路径。
  • 然后你在表单中上传文件后,用uf.cleaned_data['headImg']取得文件,再把它赋值给我们的模型字段`u.headImg。
  • 至此,我们点击提交后,Django就帮我们把文件上传到定义的存放文件的路径中,然后把文件路径赋值给headImg路径。

11、等等——看标题貌似应该着重讲解上传之后怎么使用图片的吧?可为什么快结束了还在讲怎么上传啊?

耍劳资,484?

12、来了,来了……我们可以打印出来储存在数据库中的路径,咦,是这个样子的:

数据库中的存储路径

13、我们直接在imgsrc中放这个路径,试试。

<img src="{{user.headImg}}" alt=""/>

14、不行,图片显示的是:

图片无法显示

15、看看源代码:

没错啊,是我们储存在数据库中的路径

16、莫非是绝对路径和相对路径的问题?试一试在前面给它加上http://127.0.0.1:8000/

<img src="http://127.0.0.1:8000/{{user.headImg}}" alt=""/>

17、仍然不行,这个时候源代码显示的路径是这个样子的,看样子貌似已经很完美了,但为什么就是不显示了:

源代码显示的路径

18、这个时候,楼主已经逐渐丧失理智,觉得肯定是Django在玩我。但是楼主修炼多年,岂能因为这点小事失态,于是楼主继续各种stackoverflow,google,bing。有人说是因为上面这个看似完美的路径也是一个url,Django里面处理url都是要经过路由设置的,你在路由里面没设置当然它不知道你这个用来干嘛。

19、楼主顿时恍然大区,说的真好,那我就去路由里面设置吧,添加一行:

url(r'upload/([*]+)'),

20、出错了,提示说这个需要2个参数。好吧,再给你个参数:

url(r'upload/([*]+)', name='handleImgUrl'),

21、仍然提示出错,需要2个参数,Django仿佛在说:你TM在耍我么,给劳资一个name参数是几个意思?

22、等等——我知道你想要一个下面这个样子的:

url(r'upload/([*]+)', 'my_reg.views.XXX'),

23、但是,我TM不知道我写出这么个XXX函数后,这个函数里写什么啊。我这里只是要一个url路径而已,你还得逼我写个函数,楼主长舒了一口气,淡定——,写就写麽,大不了我这个XXX函数里面什么都都不写,直接写个pass什么的糊弄一下。

24、结果还是不行。呼——呼——,接下来该怎么办?容楼主想想,图片已经上传到服务器上了,现在全部问题就在怎么把它显示出来,急死了,先上个厕所。

25、上厕所回来了,网上还说,设置一下MEDIA_URLMEDIA_ROOT,好吧,照着写一下,在settings.py中加上这两个配置,然后在urlpatterns中添加一下:

# 这是在settings中的设置
MEDIA_URL = '/upload/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'upload').replace("//", "/")```

这是在urls.py中的设置

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)```

26、写完这一切,楼主心里已经完全没底了,这TM也能行?因为楼主只是设置了一下media路径,貌似是告诉Django媒体文件在哪,然后Django就能自动在我们调用图片的时候把图片找出来并显示。听着想那么回事。

27、还是不行,容劳资捋一捋,不,容楼主捋一捋,楼主已经乱了。唉,对了,貌似是我img里的src写的不对,我现在还在用的是:

<img src="http://127.0.0.1:8000/{{user.headImg}}" alt=""/>

但是我们已经设置了媒体路径了,应该Django会自动识别,不需要我们多此一举写那么多吧,删掉变成下面那样试试:

<img src="{{user.headImg}}" alt=""/>

28、还是仍然依然不行啊,这可怎么办?楼主的实验看来是进行不下去了啊,这么半途而废实在不是楼主我的风格啊,楼主该怎么办?楼主根本不认识什么Django牛人啊,不知道请教谁啊?而且这个貌似不是很难得事吧?这么请教别人是不是太没面子了啊?楼主自学编程这么多年了,什么困难没见过,今天是要扑街了么?要振作啊,楼主!

振作啊楼主

29、容楼主理理思路:这TM不就是个路径嘛,路径啊,url啊,懂不懂啊,相对路径啊,绝对路径啊,你傻逼啊你——咦,好像有人在骂我——我直接改改路径试试,比如mysite/{{user.headImg}}或者/{{user.headImg}}或者./{{user.headImg}}或者mysite/{{user.headImg}},依然不行啊,这肯定是一个坑,既然是坑,楼主决定再潜心修行,一会再战。

30、在看了N多文档和文章之后,楼主好像懂了。也就是说上面第12步-第29步你可以忽略,直接从这里看怎么展示图片。

31、首先,我们看看models.py里的模型,有个upload_to参数,为了和过去一刀两断,楼主决定给upload_to赋值一个新的值叫avatar/,这个参数的意思是把文件上传到MEDIA_ROOT/avatar/下面。

  • 既然这里upload_to的值是连接在MEDIA_ROOT/路径后的一部分,所以很自然的只能写成avatar/或者./avatar/,而不能写成/avatar/,楼主已经以身试法过。
  • 还有一点,这里提到了MEDIA_ROOT,可是我们一直没设置过啊。
headImg = models.FileField(upload_to='avatar/')

32、所以理所当然的要设置MEDIA_ROOT,所以在settings.py中做如下设置,这里的意思就是说,我们在项目根目录下会新建一个media文件夹,专门用来存放media文件。结合上面的设置可推出,我们上传的文件会放在/media/avatar/下:

MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace("\\", "/")```

33、这`MEDIA_ROOT`就是媒体根目录的路径,这……好像是废话。上传的文件也会放在这里,但是正如我们上面探索时提到的:使用文件,实质上也是调用了一个文件的url,在Django中提到url,都是要从`urlpatterns`中过滤一遍的。

34、所以,展示图片的逻辑应该是这样的:我们调用图片的url一般是有规律的,我们过滤的时候发现,只要符合,就按照文件名从媒体根目录中找相应的文件。
- 所以,我们先找到图片url的规律,都说了,图片都是存在`/media/avatar/`中,也就是说图片的路径应该是包含`/media/avatar/`的,为了保险起见以及后续我们可能会存除了头像之外的其他文件,比如储存缩略图的叫`/media/thumb/`,所以这里我们取大家共有的`/media/`作为过滤url的规律。

MEDIA_URL = '/media/'

- 这也就是为什么`MEDIA_ROOT`和`MEDIA_ROOT`经常一起出现,并且他们的有相同的值。

34、准备好这些后,在`urlpatterns`中写吧,这里写的路由和普通的路由不一样,因为我们这里的所有的媒体文件其实都是静态文件的一部分,而且我们一般路由符合条件后是去执行`views`中的某个函数,这里却是去某个文件夹中找文件,所以肯定写法上是不同的,写法是`static(如果符合这样规律的url,就去这个目录中找文件)`:

导入这两个包

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)```

35、如果你之前在探索的时候经常会看看浏览器会输出什么错误,你一般都是看到要么是404 error,要么就是500 error。为什么会出现404 error,就是因为我们给的图片路径没有在urlpatterns中定义过,所以Django在要展示图片的时候,一看,咦,这什么鬼url,在urlpatterns中根本没有对应的可以查,所以是错误的请求网址,返回404 error。在urlpatterns中添加之后,就不会有404 error了。

36、好了,我们还剩下最后一步,就是在imgsrc中填写正确的图片地址。我们之前说了图片是储存在/media/avatar/下面的,所以图片的路径就是:

<img src="/media/{{user.headImg}}" alt=""/>
  • 你问为什么?因为我们储存在数据库中的图片路径是upload_to的值和图片名称的拼接,比如下面的avatar/test_mini.jpg
数据库中储存的图片路径

37、成功了!

用户名和图片的展示

38、瞧,解决方案中,在settings里设置MEDIA的相关属性,然后在urlpatterns中设置相关路由,这些我们在之前的探索中都有尝试,但就是差那么一点点。所以,如果我们不懂原理,仅仅照搬修改几个设置,那么远远不能解决问题,虽然我们离答案曾经那么近。

39、还有,为毛网上那么多教你上传图片的教程,就是没有教你显示图片的教程呀喂!

我需要安慰

40、再见!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容