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

581 查看

楼主之前做了一个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_ROOTMEDIA_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、再见!