楼主之前做了一个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、我们直接在img
的src
中放这个路径,试试。
<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_URL
和MEDIA_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、好了,我们还剩下最后一步,就是在img
的src
中填写正确的图片地址。我们之前说了图片是储存在/media/avatar/
下面的,所以图片的路径就是:
<img src="/media/{{user.headImg}}" alt=""/>
- 你问为什么?因为我们储存在数据库中的图片路径是
upload_to
的值和图片名称的拼接,比如下面的avatar/test_mini.jpg
。
37、成功了!
用户名和图片的展示38、瞧,解决方案中,在settings
里设置MEDIA
的相关属性,然后在urlpatterns
中设置相关路由,这些我们在之前的探索中都有尝试,但就是差那么一点点。所以,如果我们不懂原理,仅仅照搬修改几个设置,那么远远不能解决问题,虽然我们离答案曾经那么近。
39、还有,为毛网上那么多教你上传图片的教程,就是没有教你显示图片的教程呀喂!
我需要安慰40、再见!
网友评论
NoReverseMatch at /upload/
Reverse for 'my_regi' not found. 'my_regi' is not a valid view function or pattern name.
提供API给移动端的时候 要在图片路径前面加上服务器地址
这个还没找到办法,我使用的是ModelSerializer,如果你知道方法还请赐教
url('^', views.index), 所以一直匹配不到... 多么痛的领悟
生产环境下怎么解决的呢?
(笑脸相迎) 大哥,怎么个简单法? 说来听听