中山大学数据科学与计算机学院本科生实验报告
(2019年春季学期)
一、实验题目
IM业务系统后台实现
二、实现内容
基于Django框架实现下列接口api
- 信息的发送,保存,读取
- 图片
- 文字
三、实验结果
信息发送
1.概述
为了实现聊天工具对话的消息传递,这里我们引入了序列号的概念,每一个消息都有它特定的序号,我们根据这个序列号来确定这个消息是否已经发送,是否已经被接受到,是否需要删除等逻辑。
除此之外,为了实现多用户间的对话,例如A->B, A->C, B->C,他们之间如何能找到属于他们各自的信息呢?这里我们利用的是维护一个消息表,每一条消息都有发送用户的id,接受用户的id,内容的id,然后再通过内容的id在内容表格中找具体的内容。
2.数据库设计——Message表
class Msg(models.Model):
Username = models.CharField(max_length = 20)
Seq = models.IntegerField(default = 0)
From = models.CharField(max_length = 20)
Type = models.CharField(max_length = 10)
ContentID = models.IntegerField(default = 0)
def __str__(self):
return self.Username
消息表,保留的是每一条消息的发送方,接受方,序列号,内容的id,类型(判断是图片或者文本数据)等
3.数据库设计——Content表
class Content_Text(models.Model):
Cid = models.AutoField(primary_key = True)
Cstr = models.CharField(max_length = 500)
def __str__(self):
return self.Cid
class Content_Image(models.Model):
Cid = models.AutoField(primary_key = True)
# 这里的upload_to是指定图片存储的文件夹名称,上传文件之后会自动创建
Cimage = models.ImageField(upload_to='img')
def __str__(self):
return self.Cid
内容表,这里我们设置了两个部分,一个是文字,一个是图片。特别是图片部分使用的是Djungo中的ImageField类型,后面的upload_to是指定图片存储的文件夹名称,上传文件之后会自动创建。
两个表都是简单的包含一个id,一个内容,便于Message表的查询
3. URL设计
用户发送信息:
/content/{ type (text, image) }/
POST
{
to: {username}
data: {}
}
获取内容:
/content/{ type (text, image) }/{id}/
GET
用户消息表:
/message/{seq}/
GET
返回:
{
...
data:[ ]
}
在Django的urls.py文件中表现为
from django.urls import path
from . import views
urlpatterns = [
path('text/', views.text, name='text'),
path('image/', views.image, name='image'),
path('text/<int:text_id>', views.text_detail, name='text_detail'),
path('image/<int:image_id>', views.image_detail, name='image_detail'),
]
4. 具体实现
a.从消息表通过序列号获取消息
from django.forms.models import model_to_dict
from django.http import HttpResponse
import json
from .models import Msg
def messageTable(request, seq):
response = {'state':'fail', 'msg':'no msg'}
# 要在登录状态下
if 'login_id' not in request.session:
response['msg'] = 'no login'
return HttpResponse(json.dumps(response), content_type = 'application/json')
# 只接受 GET 请求
if request.method != 'GET':
response['msg'] = 'wrong method'
return HttpResponse(json.dumps(response), content_type = 'application/json')
# 已经登录, 所以拿取用户信息
t_username = request.session['login_id']
# 数据库操作
try:
t_msg = Msg.objects.filter(Username = t_username, Seq__gt = seq)
except Exception as e:
response['msg'] = 'db error'
return HttpResponse(json.dumps(response), content_type = 'application/json')
else:
if t_msg.count() <= 0:
response['msg'] = 'no data'
else:
temp = []
for index in range(t_msg.count()):
temp.append(model_to_dict(t_user[index]))
response = {'state':'ok', 'msg':'ok', "data":temp}
response['state'] = 'ok'
response['msg'] = 'get message successfully'
return HttpResponse(json.dumps(response), content_type = 'application/json')
具体逻辑
- 定义返回体的内容,包括一个state和msg
- 判断是否已经登陆状态
- 是,直接返回HttpResponse,告诉前端已经登陆,无须注册
- 否,下一步
- 判断方法是否为GET
- 获取用户注册的用户名以及密码
- 数据库操作
- 通过序列号来查找消息内容,最后返回改内容
上传一个string
# 上传一个string,只允许post方法
def text(request):
response = {'state':'fail', 'msg':'no msg'}
# 要在登录状态下
if 'login_id' not in request.session:
response['msg'] = 'no login'
return HttpResponse(json.dumps(response), content_type = 'application/json')
# 只允许POST方法
if request.method != 'POST':
response['msg'] = 'wrong method'
return HttpResponse(json.dumps(response), content_type = 'application/json')
# 已经登录, 所以拿取用户信息
from_username = request.session['login_id']
# 获取参数
try:
t_data = request.POST['data']
to_username = request.POST['to']
except Exception as e:
response['msg'] = 'POST parameter error'
return HttpResponse(json.dumps(response), content_type = 'application/json')
# 为消息设定一个id,这里使用的是消息表的长度+1
cid = len(Content_Text.objects.all()) + 1
seq = 1
# 处理seq,在Msg根据接收方用户名找到上一个seq,若不存在则初始化为1
try:
t_msg = Msg.objects.filter(Username = to_username)
except Exception as e:
response['msg'] = 'db error'
return HttpResponse(json.dumps(response), content_type = 'application/json')
else:
# 找到该用户最大的seq
if t_msg.count() > 0:
for msg in t_msg:
seq = max(seq, msg.Seq)
seq += 1
# 数据库操作,插入消息,并插入到message
try:
Content_Text.objects.create(
Cid = cid,
Cstr = t_data
)
Msg.objects.create(
Username = to_username,
Seq = seq,
From = from_username,
Type = 'text',
ContentID = cid
)
response['state'] = 'ok'
response['msg'] = 'send successfully'
except Exception as e:
response['msg'] = 'db error'
return HttpResponse(json.dumps(response), content_type = 'application/json')
具体逻辑
- 判断是否为登陆的状态
- 定义返回体的内容,包括一个state和msg
- 判断方法是否为POST,不允许GET方法
- 获取post方法传递的两个参数
- 这时能得到用户上传的data与发送的对象名
- 为这个用户上传的消息安排一个cid,这里使用的是消息表的长度+1
- 处理seq,在Msg根据接收方用户名找到上一个seq,若不存在则初始化为1
- 数据库操作
- 插入内容
- 插入消息
- 返回消息
获取一个string
def text_detail(request, text_id):
response = {'state':'fail', 'msg':'no msg'}
# 要在登录状态下
if 'login_id' not in request.session:
response['msg'] = 'no login'
return HttpResponse(json.dumps(response), content_type = 'application/json')
# 只允许GET方法
if request.method != 'GET':
response['msg'] = 'wrong method'
return HttpResponse(json.dumps(response), content_type = 'application/json')
# 数据库操作,查询消息
try:
t_text = Content_Text.objects.filter(Cid = text_id)
except Exception as e:
response['msg'] = 'db error'
return HttpResponse(json.dumps(response), content_type = 'application/json')
else:
if t_text.count() == 1:
temp = model_to_dict(t_text[0])
response = {'state':'ok', 'msg':'ok', "data":temp}
else:
response['msg'] = 'no data'
return HttpResponse(json.dumps(response), content_type = 'application/json')
具体逻辑
- 判断是否为登陆的状态
- 定义返回体的内容,包括一个state和msg
- 判断方法是否为GET方法
- 数据库操作
- 根据这个id来查找数据库是否存在该信息
- 返回消息
上传图片与获取图片
这两个部分与上面也是类似,上传图片实际上是 把图片存在服务器的硬盘中,将图片存储的路径存在数据库中。
首先要配置静态路径保存图片,setting.py
#设置静态文件路径为主目录下的media文件夹
MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace('\\', '/')
#url映射
MEDIA_URL = '/media/'
新增的逻辑在与对数据库操作:
# 数据库操作,插入图片
try:
Content_Image.objects.create(
Cid = cid,
Cimage = "img/"+ t_data.name
)
Msg.objects.create(
Username = to_username,
Seq = seq,
From = from_username,
Type = 'image',
ContentID = cid
)
# 保存文件
fname = settings.MEDIA_ROOT + "/img/" + f1.name
with open(fname,'wb') as pic:
for c in f1.chunks():
pic.write(c)