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

作者: wsb200514 | 来源:发表于2016-08-01 15:15 被阅读5848次

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

    相关文章

      网友评论

      • d778b09685bb:我被坑的好惨呐,搞了三天,看了无数教程,都没解决,就是不知道问题在哪里。现在不知道是什么情况突然想到楼主配置的那个url.py,项目里有两个url.py文件,之前看视频教程老师让在app里面在新建一个urls.py然后用外面的那个urls.py包含起来,会方便很多,然后一想,会不会是在外面那个urls里面配置呢,然后配置了之后。特么的,终于出来了。妈个鸡的:sob:
      • Chan_ae46:楼主,请问这样应该怎么解决呢,我在app的urls已经加上my_regi了,而且我发现在我app的urls没有出现pyc的文件那就是说我没有用到urls的py文件,我应该怎么去修改呢
        NoReverseMatch at /upload/
        Reverse for 'my_regi' not found. 'my_regi' is not a valid view function or pattern name.
      • 饵心:楼主帅气,劳资终于成功了,搞了两天……
      • 2a2f5458dfb2:我还有一个问题就是,
        提供API给移动端的时候 要在图片路径前面加上服务器地址
        这个还没找到办法,我使用的是ModelSerializer,如果你知道方法还请赐教
      • 2a2f5458dfb2::joy: 楼主我也经历了你经历的事情... 我的错误是我在urlPattern里写了一个
        url('^', views.index), 所以一直匹配不到... 多么痛的领悟
      • 7b1459273d80:楼主我在本地试了 是可以显示图片的,但是部署之后就显示不了了,用chrome查看图片的链接和本地也是一抹一样的。部署使用Nginx和gunicorn
      • 只有软肋:楼主真是好人啊,激动得都快哭了
      • 05c1d458acf7:感谢楼主,很详细
      • 豪门百里:写得实在是太好了,就喜欢这样的详细记录自己的思维历程的作者。
      • 142696cfabcf:可以请问下楼主是用什么软件查看上传文件的filesystem么?
        142696cfabcf:@wsb200514 不管怎么说,楼主的这个记录帮助很大。现在在学习部署的东西,也是一头雾水啊:joy:
        wsb200514:上传文件的路径是存在数据库的,所以用一个数据库软件查看即可,我用的是navicat。其他的除了python的IDE之外,没有用到其他工具。不知道有没有get到你的问题哈。。。
      • Andrew玩Android:楼主,生产环境下还是不行,DEBUG=False情况下。

        生产环境下怎么解决的呢?
        wsb200514:我这边开发环境只要没问题,部署到服务器也是正常的。你部署之后可以chrome查看一下图片的url。
      • 撵规:老铁 没毛病 关注走一波
      • 空转风:感恩楼主,网上到处都找不到方法
      • River先生:还有更简单的方式.url
        ZealouSnesS:这里说的是存用户的个人文件吧,用url不就把文件向所有访客公开了么
        River先生:@Andrew玩Android 不是说了么,Image字段的可以直接用.url方式。
        Andrew玩Android:说不要说一半,这样很不好!

        (笑脸相迎) 大哥,怎么个简单法? 说来听听:smile:
      • 空心小黑菜:刚好遇到同样的问题,你的文章令我茅塞顿开并解决了问题,十分感谢
      • a1624ccb41fd:感谢楼主😬
      • 泛小七零食店:多么痛的领悟~感谢楼主
      • 71739aed572c:多么痛的过程啊

      本文标题:【4】Django上传图片后怎么使用图片呀喂?

      本文链接:https://www.haomeiwen.com/subject/eluxsttx.html