美文网首页Python
二十一、Django2.1 搭建多用户的博客网站——美图模块

二十一、Django2.1 搭建多用户的博客网站——美图模块

作者: 27efec53a72d | 来源:发表于2019-03-04 08:05 被阅读201次

    目录:Django 2.1 从零开始搭建博客网站系列

    服务器环境搭建(选学)

    小试牛刀——简单的博客网站

    庖丁解牛——多用户的博客网站之用户模块

    庖丁解牛——多用户的博客网站之文章模块

    华丽转身——多用户的博客网站之扩展功能

    正文:

    本模块的功能是用户提交图片的网址,然后由程序将图片下载并保存到指定的位置,最后用户可以在“美图”模块里浏览这些图片。

    1、创建应用

    python manage.py startapp image
    

    在 ./lehhe/settings.py 的 INSTALLED_APPS 中新增 image

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'blog',
        'account',
        'article',
        'image', #新增
    ]
    

    如果没安装python的Pillow模块,需要先安装。安装命令是

    pip install pillow
    

    2、增加图片

    2.1 数据模型类

    编辑 ./image/models.py

    from django.db import models
    from django.contrib.auth.models import User
    from slugify import slugify
    
    
    class Image(models.Model):
        user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="images")
        title = models.CharField(max_length=300)
        url = models.URLField()
        slug = models.SlugField(max_length=500, blank=True)
        description = models.TextField(blank=True)
        created = models.DateField(auto_now_add=True, db_index=True)
        image = models.ImageField(upload_to='images/%Y/%m/%d')
    
        def __str__(self):
            return self.title
    
        def save(self, *args, **kwargs):
            self.slug = slugify(self.title)
            super(Image, self).save(*args, **kwargs)
    

    执行以下命令更新数据库

    python manage.py make migrations
    python manage.py migrate
    

    2.2 表单类

    编辑 ./image/forms.py

    from django import forms
    from django.core.files.base import ContentFile
    from slugify import slugify
    from urllib import request
    from .models import Image
    
    
    class ImageForm(forms.ModelForm):
        class Meta:
            model = Image
            fields = ('title', 'url', 'description')
    
        def clean_url(self):
            url = self.cleaned_data['url']
            valid_extensions = ['jpg', 'jpeg', 'png']
            extension = url.rsplit('.', 1)[1].lower()
            if extension not in valid_extensions:
                raise forms.ValidationError("The given Url does not match valid image extension.")
            return url
    
        def save(self, force_insert=False, force_update=False, commit=True):
            image = super(ImageForm, self).save(commit=False)
            image_url = self.cleaned_data['url']
            image_name = '{0}.{1}'.format(slugify(image.title), image_url.rsplit('.', 1)[1].lower())
            response = request.urlopen(image_url)
            image.image.save(image_name, ContentFile(response.read()), save=False)
            if commit:
                image.save()
    
            return image
    

    2.3 视图函数

    编辑 ./image/views.py

    from django.contrib.auth.decorators import login_required
    from django.http import JsonResponse
    from django.shortcuts import render
    from django.views.decorators.csrf import csrf_exempt
    from django.views.decorators.http import require_POST
    
    from .forms import ImageForm
    from .models import Image
    
    
    @login_required(login_url='/account/login/')
    @csrf_exempt
    @require_POST
    def upload_image(request):
        form = ImageForm(data=request.POST)
        if form.is_valid():
            try:
                new_item = form.save(commit=False)
                new_item.user = request.user
                new_item.save()
                return JsonResponse({'status': "1"})
            except:
                return JsonResponse({'status': "0"})
    
    
    @login_required(login_url='/account/login/')
    def list_images(request):
        images = Image.objects.filter(user=request.user)
        return render(request, 'image/list_images.html', {"images": images})
    

    2.4 设置路由

    编辑 ./lehehe/urls.py

    path('image/', include('image.urls')),
    

    创建 ./image/urls.py

    from django.urls import path
    from . import views
    from django.contrib.auth import views as auth_views
    
    app_name = 'image'  # 一定要写这一行,否则html中会报错 'image' is not a registered namespace
    
    urlpatterns = [
        path('list-images/', views.list_images, name="list_images"),
        path('upload-image/', views.upload_image, name='upload_image'),
    ] 
    

    2.5 模板文件

    编辑 ./templates/article/header.html 在“文章管理”之后添加

    <li><a href="{% url 'image:list_images' %}">图片管理</a></li>
    

    编辑 ./templates/article/leftslider.html 增加“图片管理”栏目

        <hr>
        <div class="text-center" style="margin-top: 5px;">
            <p><h4>图片管理</h4></p>
            <p><a href="{% url 'image:list_images' %}">图片管理</a></p>
        </div>
    

    创建 ./templates/image/list_images.html

    {% extends "article/base.html" %}
    {% load staticfiles %}
    {% block title %}images{% endblock %}
    {% block content %}
    <div>
        <button type="button" class="btn btn-primary btn-lg btn-block" onclick="add_image()"> 添加图片</button>
        <div style="margin-top:10px;">
            <table class="table table-hover">
                <tr>
                    <td>序号</td>
                    <td>标题</td>
                    <td>图片</td>
                    <td>操作</td>
                </tr>
                {% for image in images %}
                <tr>
                    <td>{{ forloop.counter }}</td>
                    <td>{{ image.title }}</td>
                    <td>{{ image.image }}</td>
                    <td><a nane="delete" href="javascript:" onclick="del_image(this, {{ image.id }})"><span class="glyphicon glyphicon-trash" style="margin-left:20px;"></span></a></td>
                </tr>
                {% empty %}
                <p>还没有图片,请点击上面的按钮添加图片</p>
                {% endfor %}
            </table>
        </div>
    </div>
    
    
    <script src="{% static 'js/jquery.js' %}"></script>
    <script src="{% static 'js/layer.js' %}"></script>
    <script type="text/javascript">
    function add_image(){
            var index=layer.open({
                type: 1,
                skin: 'layui-layer-demo',
                closeBtn: 0,
                shift: 2,
                shadeClose: true,
                title: "Add Image",
                area: ['600px', '440px'],
                content: "<div style='padding:20px'><p>请新增扩展名是.jpg 或.png 的网上照片地址</p><form><div class='form-group'><label for='phototitle' class='col-sm-2 control-label'> 标题</label><div class='col-sm-10'><input id='phototitle' type='text' class='form-control' style='margin-bottom:5px'></div></div><div class='form-group'><label for='photourl' class='col-sm-2 control-label'>地址</label><div class='col-sm-10'><input id='photourl' style='margin-bottom:5px' type='text' class='form-control'></div></div><div class='form-group'><label for='description' class='col-sm-2 control-label'>描述</label> <div class='col-sm-10'><textarea class='form-control' style='margin-bottom:5px' row='2' id='photodescription'></textarea></div></div><div class='form-group'><div class='col-sm-offset-2 col-sm-10'><input id='newphoto' type='button' class='btn btn-default' value='Add It'></div></div></form></div>",
                success: function(){ $("#newphoto").on('click', function(){
                    var title = $("#phototitle").val();
                    var url = $("#photourl").val();
                    var description = $("#photodescription").val();
                    var photo = {"title":title, "url":url, "description":description};
                    $.ajax({
                        url: '{% url "image:upload_image" %}',
                        type: "POST",
                        data: photo,
                        success: function(e){
                            var status = e['status']
                            if(status =="1"){
                                layer.close(index);
                                window.location.reload();
                            } else {
                                layer.msg("图片无法获取,请更换图片");
                            }
                        },
                    });
                });
            },
        });
    }
    
    </script>
    {% endblock %}
    

    2.6 测试

    访问 http://127.0.0.1:8000/image/list-images/

    图片管理栏目 添加图片

    增加了两张图片后,页面如下


    添加图片后的图片列表

    查看项目目录,观察images文件夹,如图


    images文件夹

    查看数据库中的 image_image 表


    image_image表

    3、删除图片

    3.1 视图函数

    编辑 ./image/views.py 新增 del_image 方法

    @login_required(login_url='/account/lobin/') 
    @require_POST
    @csrf_exempt
    def del_image(request):
        image_id = request.POST['image_id'] 
        try:
            image = Image.objects.get(id=image_id) 
            image.delete()
            return JsonResponse({'status':"1"})
        except:
            return JsonResponse({'status':"2"})
    

    3.2 设置路由

    编辑 ./image/urls.py 新增路由

    path('del-image/', views.del_image, name='del_image'),
    

    3.3 模板文件

    在 ./templates/image/list_images.html 中新增如下js代码

    function del_image(the, image_id){
        var image_title = $(the).parents("tr").children("td").eq(1).text(); 
        layer.open({
            type: 1,
            skin: "layui-layer-rim",
            area: ["400px", "200px"],
            title: "删除图片",
            content: '<div class="text-center" style="margin-top:20px"><p>是否确定删除《'+image_title+'》</p> </div>',
            btn:['确定', '取消'], 
            yes: function(){
                $.ajax({
                    url: '{% url "image:del_image" %}', 
                    type:"POST",
                    data: {"image_id":image_id}, 
                    success: function(e){
                        var status = e['status'] 
                        if(status=="1"){
                            parent.location.reload();
                            layer.msg("has been deleted."); 
                        }else{
                            layer.msg("删除失败");
                        }
                    }, 
                })
            }, 
        });
    }
    

    访问 http://127.0.0.1:8000/image/list-images/ 点击页面中的删除图标,效果如图

    删除图片

    点击确定后,图片将从列表和数据表中删除,却没有从images文件夹中删除,如想进一步删除,还需要继续优化。

    3.4 完善图片列表

    图片列表现在只显示了图片地址,下面我们将在图片地址的位置显示为图片。
    在 ./lehehe/settings.py 中增加如下配置

    # 图片保存地址配置
    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
    

    修改 ./lehehe/urls.py 引入settings和static

    from django.conf import settings
    from django.conf.urls.static import static
    
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    

    在 ./templates/image/list_images.html 中修改这行代码

    <td>{{ image.image }}</td>
    

    <td><img src="{{ image.image.url }}" width="100px" height="100px"></td>
    

    访问 http://127.0.0.1:8000/image/list-images 发现原来提交的图片无法显示了,因为我图片的访问地址发生了变化,所以需要重新提交,重新提交之后,页面如下

    新图片列表

    3.5 使用缩略图应用

    上面我们虽然现实了图片,但是仅仅是对原图尺寸做了修改,读取的仍然是原图。下面我们就用缩略图来代替。幸运地是我们不用从零开始写缩略图功能了,使用缩略图应用 sorl-thumbnail 即可,其官网地址是 https://sorl-thumbnail.readthedocs.io
    首先安装这个应用

    pip install soil-thumbnail
    

    然后把这个应用配置在 ./lehehe/settings.py 的 INSTALLED_APPS 中

    'sorl.thumbnail',
    

    接着更新数据库

    python manage.py make migrations thumbnail
    python manage.py migrate
    

    最后在 ./templates/image/list_images.html 中引入sort-sumbnail,修改之前的显示图片代码

    {% thumbnail image.image "100x100" crop="center" as im %}
      <td><img src="{{im.url}}" width="{{ im.width }}" height="{{ im.height }}"></img></td>
    {% endthumbnail %}
    

    重新访问 http://127.0.0.1:8000/image/list-images 此时的图片已经是原图的缩略图了。

    4、展示图片

    “瀑布流”是目前很流行的布局模式,下面我们将使用“瀑布流”的布局来展示图片。

    4.1 使用瀑布流插件

    插件地址 http://github.com/jmlp-131092/mp-mansory.js ,下载这个插件后,把其中的 mp.mansory.min.js 文件复制到 ./static/js 目录中,然后在 ./static/css 目录中创建 mansory-style.css 文件,并输入以下代码:

    #my-gallery-container {
        background-color: #ededed;
    }
    
    .falls_item {
        border: #c6c6c6 2px solid;
        width: 100%;
        background-color: white;
        min-height: 100px;
        padding: 5px;
        margin: 10px 0px;
        box-shadow: rgba(0, 0, 0, 0.5) 0px 2px 2px;
        border-radius: 4px;,
        
    }
    
    .falls_item:hover {
        opacity: 0.5;
    }
    
    .falls_item.h150{
        width: 100%;
        min-height: 150px;
    }
    
    .falls_item.h200{
        width: 100%;
        min-height: 200px;
    
    }
    
    .falls_item.h250{
        width: 100%;
        min-height: 200px;
    }
    .falls_padding{
        padding: 10px 5px;
    }
    .falls_item img {
        max-width: 100%;
    }
    

    之所以不使用 style.css 文件,是因为原文件中的样式表名称跟本项目中已经使用的 bootstrap.css 有重复,如果使用就会覆盖原有名称,因此这里重写并重新命名。

    4.2 视图函数

    编辑 ./image/views.py 新增 falls_images 方法

    def falls_images(request):
        images = Image.objects.all()
        return render(request, 'image/falls_images.html', {"images": images})
    

    4.3 设置路由

    编辑 ./image/urls.py

    path('images/', views.falls_images, name="falls_images"),
    

    4.4 模板文件

    编辑 ./templates/header.html 中的导航代码,增加“美图”栏目

    <li><a href="{% url 'image:falls_images' %}">美图</a></li>
    

    创建 ./templates/image/falls_images.html

    {% extends "base.html" %}
    {% load staticfiles %}
    {% block title %}Images{% endblock %}
    {% block content %}
    <div class="container">
        <link rel="stylesheet" href="{% static 'css/mansory-style.css' %}" type="text/css"/>
        <div id="my-gallery-container">
            {% for image in images %}
            <div class="falls_item h200" data-order="{{image.id}}">
                <img src="{{ image.image.url }}">
                <p>{{ image.title }}</p>
            </div>
            {% endfor %}
        </div>
    </div>
    
    <script src='{% static "js/jquery.js" %}'></script>
    <script src="{% static 'js/mp.mansory.min.js' %}"></script>
    <script type="text/javascript">
    jQuery(document).ready(function($) {
        $("#my-gallery-container").mpmansory( 
            {
                childrenClass: 'falls_item', // default is a div 
                columnClasses: 'falls_padding', //add classes to items 
                breakpoints:{
                    lg: 3,
                    md: 4,
                    sm: 6,
                    xs: 12
                },
                distributeBy: { order: false, height: false, attr: 'data-order', attrOrder: 'desc' }, //default distribute by order, options => order: true/false, height: true/false, attr => 'data-order', attrOrder=> 'asc'/'desc'
            });
        }); 
    </script>
    {% endblock %}
    

    访问 http://127.0.0.1:8000/image/images/ 页面如图

    瀑布流展示图片

    4.5 查看图片的详细信息

    上面只是展示了所有图片,下面我们实现点击某张图片显示图片详细信息,并能查看原图。一种方式是使用类似查看文章详细内容的方法,另一种方式是借助layer插件使用纯粹前端的方式。第一种方式参考之前的文章即可,下面我们将采用第二种方式来实现。

    修改 ./templates/image/falls_images.html

    {% extends "base.html" %}
    {% load staticfiles %}
    {% block title %}Images{% endblock %}
    {% block content %}
    <div class="container">
        <link rel="stylesheet" href="{% static 'css/mansory-style.css' %}" type="text/css"/>
        <div id="my-gallery-container">
            {% for image in images %}
            <div class="falls_item h200" data-order="{{image.id}}">
                <!--img src="{{ image.image.url }}"-->
                <a href="javascript:void(0)" onclick="displayImage('{{image.user}}', '{{image.title}}','{{image.image.url}}', '{{image.description}}','{{request.get_host}}')">
                    <img src="{{ image.image.url }}">
                </a>
                <p>{{ image.title }}</p>
            </div>
            {% endfor %}
        </div>
    </div>
    
    <script src='{% static "js/jquery.js" %}'></script>
    <script src="{% static 'js/mp.mansory.min.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/layer.js'%}"></script>
    
    <script type="text/javascript">
    jQuery(document).ready(function($) {
        $("#my-gallery-container").mpmansory( 
            {
                childrenClass: 'falls_item', // default is a div 
                columnClasses: 'falls_padding', //add classes to items 
                breakpoints:{
                    lg: 3,
                    md: 4,
                    sm: 6,
                    xs: 12
                },
                distributeBy: { order: false, height: false, attr: 'data-order', attrOrder: 'desc' }, //default distribute by order, options => order: true/false, height: true/false, attr => 'data-order', attrOrder=> 'asc'/'desc'
            });
        });
    
    function displayImage(user, title, url, description, host){
        layer.open({
            type: 1,
            title: title,
            skin: 'layui-layer-rim', //加上边框
            area: ['600px', '600px'], //宽高
            content: '<div class="text-center"><img src="http://'+host+url+'"><p> 发 布 者 :'+user+'</p></div><div style="margin-left:10px;">'+description+'</div>',
        });
    }
    
    </script>
    {% endblock %}
    

    4.6 测试

    访问 http://127.0.0.1:8000/image/images/ 点击一张图片,效果如图

    查看图片详细信息

    相关文章

      网友评论

        本文标题:二十一、Django2.1 搭建多用户的博客网站——美图模块

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