Django开发实战:快速构建简易图片管理应用
初次接触Django框架,按照官方文档的指引走了一遍入门示例。作为一个标准的MVC架构框架,如果对MVC模式还不熟悉的话,建议先查阅相关资料进行了解。
Django项目创建、数据库迁移等基础操作官方文档中都有详细说明。本文演示环境采用Django 2.1版本配合Python 3.6,主要实现一个最简单的图片管理功能,包含数据的增删操作。
环境准备与项目创建
首先安装Django框架:
pip install django
创建新的Django项目:
django-admin startproject mysite
项目创建完成后,会自动生成一系列文件。关于各个文件的用途,官方文档中有详细的解释说明,这里就不再赘述。
数据库配置
由于开发机上的MySQL服务部署在另一台虚拟机上,所以在settings.py中需要调整数据库连接配置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangodb',
'HOST': '192.168.175.130',
'USER': 'root',
'PASSWORD': '123456',
'CHARSET': 'utf8mb4',
}
}
模型定义
创建应用并定义数据模型:
# myadmin/models.py
class ImageRecord(models.Model):
resource_id = models.IntegerField()
image_url = models.CharField(max_length=255)
category_id = models.SmallIntegerField()
image_title = models.CharField(max_length=255)
description = models.TextField()
def __str__(self):
return self.image_title
配置应用:
# myadmin/apps.py
class MyadminConfig(AppConfig):
name = 'myadmin'
在settings.py中注册应用:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog.apps.BlogConfig',
'myadmin.apps.MyadminConfig',
]
数据库迁移
执行数据迁移命令,生成建表语句:
manage.py makemigrations myadmin
该命令会根据模型定义生成相应的数据库表结构。
执行迁移:
manage.py migrate
此时会创建所需的数据库表。
MySQL驱动问题
初次执行迁移命令时,发现缺少MySQL驱动库。原本计划安装python-mysql,但尝试后发现该库与Python 3.6不兼容。最终选择使用PyMySQL代替,并在settings.py中添加如下配置:
import os
import pymysql
pymysql.install_as_MySQLdb()
配置完成后,数据库迁移正常工作。
视图函数实现
定义视图处理函数:
# myadmin/views.py
from django.shortcuts import render
from django.http import HttpResponse, Http404, HttpResponseRedirect, JsonResponse
from django.urls import reverse
from .models import ImageRecord
import os
import uuid
def home(request):
return HttpResponse('欢迎访问管理后台')
def image_list(request):
images = ImageRecord.objects.all()
context = {"image_data": images}
return render(request, 'gallery/index.html', context)
def image_create(request):
resource_id = request.POST.get('rid', None)
category_id = request.POST.get('cid', None)
title = request.POST.get('title', None)
description = request.POST.get('backup', None)
url = save_upload(request.FILES['img'])
new_image = ImageRecord(
resource_id=resource_id,
category_id=category_id,
image_title=title,
description=description,
image_url=url
)
new_image.save()
if new_image.id is None:
raise Http404
return HttpResponseRedirect(reverse('myadmin:image_list'))
def image_delete(request):
img_id = request.POST.get('id', None)
if img_id is None:
return JsonResponse({'status': 0})
try:
image = ImageRecord.objects.get(pk=img_id)
image.delete()
except Exception as e:
return JsonResponse({'status': 0})
else:
return JsonResponse({'status': 1})
def save_upload(f):
upload_dir = '/images/'
base_url = 'http://127.0.0.1:81/'
if not os.path.exists(upload_dir):
os.mkdir(upload_dir)
filename = str(uuid.uuid1()) + f.name
filepath = upload_dir + filename
with open(filepath, 'wb+') as dest:
for chunk in f.chunks():
dest.write(chunk)
return base_url + filename
URL路由配置
# myadmin/urls.py
from django.urls import path
from . import views
app_name = 'myadmin'
urlpatterns = [
path('', views.home, name='home'),
path('images/list', views.image_list, name='image_list'),
path('images/create', views.image_create, name='image_create'),
path('images/delete', views.image_delete, name='image_delete')
]
模板页面
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片管理</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<style>
.header-bar { background-color: aqua; }
.sidebar { background-color: #fff; }
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-12 header-bar">
<p>管理系统</p>
</div>
<div class="col-md-2 sidebar">
<p>导航栏</p>
</div>
<div class="col-md-10">
<div class="row">
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#addModal">
新增图片
</button>
</div>
<!-- 添加弹窗 -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
<h4 class="modal-title">添加图片</h4>
</div>
<div class="modal-body">
<form id="imageForm" class="form-horizontal" action="{% url 'myadmin:image_create' %}"
method="post" enctype="multipart/form-data">
<input type="hidden" name="rid" value="1">
<input type="hidden" name="cid" value="1">
{% csrf_token %}
<div class="form-group">
<label class="col-sm-2 control-label">标题</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="title">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">描述</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="backup">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">图片文件</label>
<div class="col-sm-10">
<input type="file" class="form-control" name="img">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="submitBtn">保存</button>
</div>
</div>
</div>
</div>
<!-- 数据表格 -->
<div class="row" style="margin-top: 20px;">
| 资源ID | 标题 | 分类ID | 描述 | 图片 | 操作 |
|---|---|---|---|---|---|
| {{item.resource\_id}} | {{item.image\_title}} | {{item.category\_id}} | {{item.description}} |  | <button class="btn btn-danger delete-btn" data-id="{{item.id}}">删除</button> |
</div>
</div>
</div>
</div>
<script>
$('#submitBtn').click(function () {
$('#imageForm').submit();
});
$('.delete-btn').click(function () {
var btn = $(this);
var imgId = btn.attr('data-id');
var csrfToken = jQuery("[name=csrfmiddlewaretoken]").val();
$.ajax({
beforeSend: function (xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", csrfToken);
},
url: "{% url 'myadmin:image_delete' %}",
data: {id: imgId},
type: 'post',
dataType: 'json',
success: function (response) {
if (response.status === 1) {
alert('删除成功');
} else {
alert('删除失败');
}
location.reload();
}
});
});
</script>
</body>
</html>
字符集问题处理
在实际测试过程中,遇到一个编码相关的错误:
1366, "Incorrect string value: '\\xE6\\xB5\\x8B\\xE8\\xAF\\x95' for column 'title' at row 1"
排查发现MySQL数据库默认字符集配置为latin1,导致中文字符无法正常存储。通过以下命令查看数据库字符集配置:
mysql> show variables like '%char%';
解决方案是将数据库和表的字符集统一修改为utf8mb4。关于具体的修改方法,可以参考MySQL字符集配置的相关文档。
修改完成后,中文数据即可正常插入。
小结
本示例实现了Django项目的完整搭建流程,包括数据库配置、模型定义、视图开发、模板渲染以及基本的增删操作。示例代码较为简洁,未做完善的异常处理,目前仅实现了数据的新增和删除功能。
整体使用感受:Django框架功能完备,但相对笨重,学习曲线较陡。接下来计划尝试一下Flask框架,看看是否能获得更轻便的开发体验。