导图社区 一个前后端分离的博客Python,django,flask
一个前后端分离的博客Python,django,flask,前段为Flask,后端为django
编辑于2019-07-11 12:48:53blog_pipilu
django
blog_server
blog_server
__init_.py
settings
urls
views
wsgi
app--btoken
apps
from django.apps import AppConfigclass BtokenConfig(AppConfig): name = 'btoken'
urls
from django.conf.urls import urlfrom . import viewsurlpatterns = [ url(r'^$',views.btoken,name='token')]
views
import hashlibimport timeimport jwtfrom django.http import JsonResponsefrom django.shortcuts import renderimport json# Create your views here.from user.models import UserProfiledef btoken(request): if not request.method == 'POST': # 当前视图函数只接受post # 创建token result = {'code': 101, 'error': 'Please use POST'} return JsonResponse(result) json_str = request.body if not json_str: result = {'code': 102, 'error': 'Please Post data'} return JsonResponse(result) json_obj = json.loads(json_str) # 获取用户名和密码 username = json_obj.get('username') password = json_obj.get('password') if not username: result = {'code': 103, 'error': 'Please give me username'} return JsonResponse(result) if not password: result = {'code': 103, 'error': 'Please give me username'} return JsonResponse(result) # 获取用户 users = UserProfile.objects.filter(username=username) if not users: # 当前用户名不存在 result = {'code': 105, 'error': 'The user is not existed'} return JsonResponse(result) # hash password p_m = hashlib.sha1() p_m.update(password.encode()) # 对比密码 if p_m.hexdigest() != users[0].password: result = {'code': 106, 'error': 'The username is wrong or the password is wrong'} return JsonResponse(result) # 生成token token = make_token(username) result = {'code': 200, 'username': username, 'data': {'token': token.decode()}} return JsonResponse(result)def make_token(username, expire=3600 * 24): ''' 生成token :param username: :param expire: :return: ''' key = 'abcdef1234' now_t = time.time() pay_load = { 'username': username, 'exp': int(now_t + expire) } return jwt.encode(pay_load, key, algorithm='HS256')
1.判断当前用户名是否有注册过
2.如果注册过,对比进行哈希加密过后的密码与数据表中取出的密码是否一致
3.如果密码一致,生成token并返回
app--user
models
UserProfile
from django.db import models# Create your models here.class UserProfile(models.Model): username = models.CharField(max_length=11, verbose_name='用户名', primary_key=True) nickname = models.CharField(max_length=30, verbose_name='昵称') email = models.CharField(max_length=50, verbose_name='邮箱') password = models.CharField(max_length=40,verbose_name='密码') sign = models.CharField(max_length=50, verbose_name='个人签名') info = models.CharField(max_length=150, verbose_name='个人描述') avatar = models.ImageField(upload_to='avatar/') class Meta: db_table = 'user_profile'
urls
from django.conf.urls import urlfrom . import viewsurlpatterns = [ url(r'^$', views.users, name='users'), #/v1/users/guoxiaonao url(r'^/(?P<username>[\w]{1,11})$', views.users, name='user'), #/v1/users/guoxiaonao/avatar url(r'^/(?P<username>[\w]{1,11})/avatar$', views.user_avatar, name='user_avatar')]
r'^$', views.users, name='users'
url(r'^/(?P<username>[\w]{1,11})$', views.users, name='user')
url(r'^/(?P<username>[\w]{1,11})/avatar$', views.user_avatar, name='user_avatar')
views
import hashlibimport jsonimport timeimport jwtfrom django.http import JsonResponsefrom django.shortcuts import render# Create your views here.#UserProfilefrom btoken.views import make_tokenfrom tools.loging_decorator import loging_checkfrom user.models import UserProfile@loging_check('PUT')def users(request, username=None): if request.method == 'GET': #取数据 if username: #具体用户的数据 # /v1/users/guoxiaonao?info=1&email=1 {'info':xxx, 'email':xxx} try: user = UserProfile.objects.get(username=username) except UserProfile.DoesNotExist: user = None if not user: #用户不存在 result = {'code':208, 'error': 'The user is not existed'} return JsonResponse(result) #判断查询字符串 if request.GET.keys(): #证明有查询字符串 data = {} for k in request.GET.keys(): #数据库中最好是有非空默认值 if hasattr(user, k): data[k] = getattr(user,k) result = {'code': 200, 'username':username, 'data':data} return JsonResponse(result) else: #证明指定查询用户全量数据 result = {'code':200, 'username':username, 'data':{'info':user.info, 'sign':user.sign,'nickname':user.nickname, 'avatar':str(user.avatar)}} return JsonResponse(result) else: #全部用户的数据 #UserProfile获取全部用户数据 all_users = UserProfile.objects.all() res = [] for u in all_users: d = {} d['username'] = u.username d['email'] = u.email res.append(d) #[{username:xx,..}, {username:xx,..}] result = {'code':200, 'data':res} return JsonResponse(result) elif request.method == 'POST': #注册用户 #密码需用SHA-1 hashlib.sha1() -> update -> hexdigest() #获取json数据 json_str = request.body if not json_str: #前端异常提交,空数据 result = {'code':202, 'error':'Please POST data'} return JsonResponse(result) #反序列化json str json_obj = json.loads(json_str) username = json_obj.get('username') email = json_obj.get('email') password_1 = json_obj.get('password_1') password_2 = json_obj.get('password_2') if not username: #用户名不存在 result = {'code':203, 'error':'Please give me username'} return JsonResponse(result) if not email: #email不存在 result = {'code':204, 'error':'Please give me email'} return JsonResponse(result) if not password_1 or not password_2: #p1 or p2 不存在 result = {'code':205, 'error': 'Please give me password'} return JsonResponse(result) if password_1 != password_2: result = {'code':206, 'error': 'The password is wrong !'} return JsonResponse(result) #检查用户是否存在 old_user = UserProfile.objects.filter(username=username) if old_user: #该用户已经注册 result = {'code':207, 'error': 'The username is existed !!! '} return JsonResponse(result) #将密码进行hash h_p = hashlib.sha1() h_p.update(password_1.encode()) try: UserProfile.objects.create(username=username,nickname=username,email=email, password=h_p.hexdigest()) except Exception as e: print('UserProfile create error is %s'%(e)) result = {'code': 207, 'error': 'The username is existed !!! '} return JsonResponse(result) #根据用户名 生成token token = make_token(username) result = {'code':200, 'username':username, 'data':{'token': token.decode()}} return JsonResponse(result) elif request.method == 'PUT': #修改用户数据 /v1/users/用户名 #前端返回的json格式{'nickaname': xxx, 'sign':xxx, 'info':xxx} user = request.user json_str = request.body #判断前端是否给了json串 if not json_str: result = {'code':202, 'error': 'Please give me data'} return JsonResponse(result) json_obj = json.loads(json_str) nickname = json_obj.get('nickname') if not nickname: #昵称不能为空 result = {'code':209, 'error': 'nickname is none!'} return JsonResponse(result) #sign&info 默认值为空字符串 sign = json_obj.get('sign', '') info = json_obj.get('info', '') #存 user.sign = sign user.info = info user.nickname = nickname user.save() result = {'code':200, 'username':username} return JsonResponse(result)@loging_check('POST')def user_avatar(request,username): # 上传图片思路: # 1,前端-> form post 提交 并且 content-type 要改成 # multipart/form-data # 2,后端只要拿到post提交, request.FILES['avatar'] # 注意:由于目前django获取put请求的 multipart数据较为复杂,故改为post获取multipart数据 #当前必须是POST提交 # UnicodeDeocdeError if not request.method == 'POST': result = {'code':210 , 'error': 'Please use POST'} return JsonResponse(result) users = UserProfile.objects.filter(username=username) if not users: result = {'code':208, 'error': 'The user is not existed !'} return JsonResponse(result) if request.FILES.get('avatar'): #正常提交图片信息,进行存储 users[0].avatar = request.FILES['avatar'] users[0].save() result = {'code':200, 'username':username} return JsonResponse(result) else: #没有提交图片信息 result = {'code': 211, 'error':'Please give me avatar'} return JsonResponse(result)
"GET"查询用户
单个特定用户
全部用户信息
"POST"注册用户
1.检查前端是否正确传递有效信息
2.判断注册用户是否已存在
3.新用户:对密码进行哈希加密
4.根据用户名,生成token
"PUT"修改用户数据
1.#判断前端是否给了json串
2.进行修改赋值,save()
3.修改头像方法
app--topic
models
Topic
from django.db import models# Create your models here.# 判断前后端分离的金标准, 后台只返数据,数据-json# 所有关于用户显示层面上的设计与开发 - 前端/客户端# 前端 与 后端 之间 HTTP 协议通信, API,- RESTful# RESTful - 1, 避免动词,尽量使用名词 2,配合HTTP method 语义# CORS - 解决跨域的一种规范 - django-cors-headersfrom user.models import UserProfile# 迁移指定模块 - topic# python3 manage.py makemigrations topic# python3 manage.py migrate topic#mysql 查询指定表的创建语句 show create table topic\Gclass Topic(models.Model): #主键为id, django默认添加 title = models.CharField(max_length=50, verbose_name="文章标题") #tec 技术类的 & no-tec 非技术类 category = models.CharField(max_length=20,verbose_name="文章分类") #limit - public 公开的 [所有人都能看] & private 私有的 [只有文章作者本人能看] limit = models.CharField(max_length=10, verbose_name="文章权限") introduce = models.CharField(max_length=90, verbose_name="文章简介") content = models.TextField(verbose_name="文章内容") #文章创建时间 created_time = models.DateTimeField() #文章修改时间 modified_time = models.DateTimeField() #作者 author = models.ForeignKey(UserProfile) class Meta: #自定义表名 db_table = 'topic'
与UserProfile中存在一对多关系
urls
urlpatterns = [ #/v1/topics/author_id url(r'/(?P<author_id>[\w]+)$', views.topics, name='topics')]
url(r'/(?P<author_id>[\w]+)$', views.topics, name='topics')
views
import datetimeimport jsonfrom django.http import JsonResponsefrom django.shortcuts import render# Create your views here.# guoxiaonao error + 4from message.models import Messagefrom tools.loging_decorator import loging_check, get_user_by_requestfrom topic.models import Topicfrom user.models import UserProfile@loging_check("POST", "DELETE")def topics(request, author_id=None): # /v1/topcis/<author_id> if request.method == "POST": #发表博客 注:发表博客必须为登录状态 #当前token中认证通过的用户 即为 作者 author = request.user #拿数据 json_str = request.body if not json_str: #判断字符串是否为空 result = {'code':302, 'error': 'Please give me json '} return JsonResponse(result) #把json串反序列化成python对象 json_obj = json.loads(json_str) title = json_obj.get('title') #带全部样式的文章内容 - content,如加粗,颜色等 content = json_obj.get('content') #纯文本的文章内容 - content_text 用来做introduce的截取 content_text = json_obj.get('content_text') #根据content_text的内容 生成 文章简介 introduce = content_text[:30] #文章权限 public or private limit = json_obj.get('limit') if limit not in ['public', 'private']: #判断权限是否合法 result = {'code': 303, 'error': 'Please give me right limit !'} return JsonResponse(result) #文章种类 tec - 技术类 no-tec - 非技术类 category = json_obj.get('category') if category not in ['tec', 'no-tec']: result = {'code': 304, 'error': 'Please give me right category !'} return JsonResponse(result) #设置创建时间/修改时间 now = datetime.datetime.now() #存储topic Topic.objects.create(title=title, content=content,limit=limit, category=category,author=author,created_time=now,modified_time=now,introduce=introduce) result = {'code':200, 'username':author.username} return JsonResponse(result) elif request.method == 'DELETE': #删除博主的博客文章 author = request.user if author.username != author_id: result = {'code':306, 'error':'You can not do it !'} return JsonResponse(result) #当token中用户名和 url中的author_id严格一致时;方可执行删除 topic_id = request.GET.get('topic_id') if not topic_id: result = {'code':307, 'error': 'You can not do it !! '} return JsonResponse(result) #查询欲删除的topic try: topic = Topic.objects.get(id=topic_id) except Exception as e: #如果当前topic不存在,则返回异常 print('topic delete error is %s'%(e)) result = {'code':308, 'error': 'Your topic is not existed'} return JsonResponse(result) topic.delete() return JsonResponse({'code':200}) elif request.method == 'GET': #/v1/topics/guoxiaonao #获取用户博客列表 or 具体博客内容【带?t_id=xx】 #1,访问当前博客的 访问者 - visitor #2,当前博客的博主 - author authors = UserProfile.objects.filter(username=author_id) if not authors: result = {'code':305, 'error': 'The current author is not existed !'} return JsonResponse(result) #当前访问的博客的博主 author = authors[0] visitor = get_user_by_request(request) visitor_username = None if visitor: visitor_username = visitor.username #对比两者的username是否一致,从而判断当前是否要取 private的博客 #尝试获取t_id,如果有t_id则证明 当前请求是获取用户指定ID的博客内容 #/v1/topics/guoxiaonao?t_id=1 t_id = request.GET.get('t_id') if t_id: #取指定t_id的博客 t_id = int(t_id) #博主访问自己博客的标记/True 则表明当前为 博主访问自己的博客 / False 陌生人访问当前博客 is_self = False if visitor_username == author_id: #改变标记 is_self = True #博主访问自己的博客 try: author_topic = Topic.objects.get(id=t_id) except Exception as e: result = {'code': 309, 'error': 'No topic'} return JsonResponse(result) else: try: author_topic = Topic.objects.get(id=t_id, limit='public') except Exception as e: result = {'code': 309, 'error': 'No topic'} return JsonResponse(result) #生成具体返回 res = make_topic_res(author, author_topic, is_self) return JsonResponse(res) else: #/v1/topics/guoxiaonao 用户全量的数据 #/v1/topics/guoxiaonao?category=tec|no-tec category = request.GET.get('category') if category in ['tec', 'no-tec']: #判断category取值范围 if visitor_username == author_id: #博主在访问自己的博客, 此时获取用户全部权限的博客 author_topics = Topic.objects.filter(author_id=author_id,category=category) else: #其他访问者在访问当前博客 author_topics = Topic.objects.filter(author_id=author_id, limit='public',category=category) else: #获取博主全部博客【判断权限】 if visitor_username == author_id: #博主在访问自己的博客, 此时获取用户全部权限的博客 author_topics = Topic.objects.filter(author_id=author_id) else: #其他访问者在访问当前博客 author_topics = Topic.objects.filter(author_id=author_id, limit='public') res = make_topics_res(author, author_topics) return JsonResponse(res)def make_topics_res(author, author_topics): res = {'code':200 , 'data': {}} topics_res = [] for topic in author_topics: d = {} d['id'] = topic.id d['title'] = topic.title d['category'] = topic.category d['created_time'] = topic.created_time.strftime("%Y-%m-%d %H:%M:%S") d['introduce'] = topic.introduce d['author'] = author.nickname topics_res.append(d) res['data']['topics'] = topics_res res['data']['nickname'] = author.nickname return resdef make_topic_res(author, author_topic, is_self): ''' 生成具体博客内容的返回值 :param author: :param author_topic: :return: ''' if is_self: #博主访问自己的 #next #取出ID大于当前博客ID的数据的第一个 next_topic = Topic.objects.filter(id__gt=author_topic.id,author=author).first() #last #取出ID小于当前博客ID的数据的最后一个 last_topic = Topic.objects.filter(id__lt=author_topic.id,author=author).last() else: #当前访问者不是当前访问博客的博主 next_topic = Topic.objects.filter(id__gt=author_topic.id,limit='public',author=author).first() #取出ID小于当前博客ID的数据的最后一个 last_topic = Topic.objects.filter(id__lt=author_topic.id,limit='public',author=author).last() # 判断下一个是否存在 if next_topic: # 下一个博客内容的id next_id = next_topic.id # 下一个博客内容的title next_title = next_topic.title else: next_id = None next_title = None #原理同next if last_topic: last_id = last_topic.id last_title =last_topic.title else: last_id = None last_title = None #生成message返回结构 #拿出所有该topic的message 并 按时间倒叙排序 all_messages = Message.objects.filter(topic=author_topic).order_by('-created_time') # leve1_msg = {1:[m1,m2,m3], 3,:[m4,m5,m6]} # messages = [ma_1, mb_3, mc_5 ] msg_list = [] #key:是留言id, value是[回复对象,回复对象] level1_msg = {} # 'aaa': [] #全部留言及回复的数量 m_count = 0 for msg in all_messages: m_count += 1 if msg.parent_message: #回复 level1_msg.setdefault(msg.parent_message, []) level1_msg[msg.parent_message].append({'msg_id':msg.id,'publisher':msg.publisher.nickname,'publisher_avatar':str(msg.publisher.avatar), 'content':msg.content,'created_time':msg.created_time.strftime('%Y-%m-%d')}) else: #留言 msg_list.append({'id':msg.id, 'content':msg.content,'publisher':msg.publisher.nickname,'publisher_avatar': str(msg.publisher.avatar), 'created_time':msg.created_time.strftime('%Y-%m-%d'), 'reply':[]}) #关联,将 留言和回复进行合并 for m in msg_list: if m['id'] in level1_msg: m['reply'] = level1_msg[m['id']] result = {'code':200, 'data':{}} result['data']['nickname'] = author.nickname result['data']['title'] = author_topic.title result['data']['category'] = author_topic.category result['data']['created_time'] = author_topic.created_time.strftime('%Y-%m-%d') result['data']['content'] = author_topic.content result['data']['introduce'] = author_topic.introduce result['data']['author'] = author.nickname result['data']['next_id'] = next_id result['data']['next_title'] = next_title result['data']['last_id'] = last_id result['data']['last_title'] = last_title #暂时为假数据 result['data']['messages'] = msg_list result['data']['messages_count'] = m_count return result
"POST"发表博客
"DELETE"删除博客
"GET"获取博客内容
判断是否为博主本人,已确定权限问题
app--message
models
Message
from django.db import models# Create your models here.from topic.models import Topicfrom user.models import UserProfileclass Message(models.Model): #topic外键 topic = models.ForeignKey(Topic) content = models.CharField(max_length=60, verbose_name='留言内容') #UserProfile 外键 publisher = models.ForeignKey(UserProfile) #当前内容的父级留言 parent_message = models.IntegerField(verbose_name='回复的留言') created_time = models.DateTimeField() class Meta: db_table = 'message'
urls
from django.conf.urls import urlfrom . import viewsurlpatterns = [ url(r'^/(?P<topic_id>[\d]+)$', views.messages, name='messages')]
url(r'^/(?P<topic_id>[\d]+)$', views.messages, name='messages')
views
import datetimeimport jsonfrom django.http import JsonResponsefrom django.shortcuts import render# Create your views here.from message.models import Messagefrom tools.loging_decorator import loging_checkfrom topic.models import Topic@loging_check('POST')def messages(request, topic_id): ''' 留言/回复 :param request: :param topic_id: :return: ''' if request.method == 'POST': #创建 留言/回复 user = request.user json_str = request.body if not json_str: result = {'code':402, 'error':'Please give me json str'} return JsonResponse(result) json_obj = json.loads(json_str) #留言内容 content = json_obj.get('content') #父级留言的ID parent_id = json_obj.get('parent_id', 0) if not content: result = {'code':403, 'error': 'Please give me content'} return JsonResponse(result) #当前时间 now = datetime.datetime.now() try: topic = Topic.objects.get(id=topic_id) except Exception as e: result = {'code': 404, 'error': 'This topic is not existed'} return JsonResponse(result) #判断当前topic limit if topic.limit == 'private': #如果当前limit是私有的,则必须为博主方可评论 if user.username != topic.author.username: result = {'code': 405, 'error': 'Please go out !'} return JsonResponse(result) Message.objects.create(topic=topic, content=content, parent_message=parent_id,created_time=now,publisher=user) return JsonResponse({'code':200, 'data':{}})
"POST"
media
avatar存放头像
tools
loging_decorator.py
flask
client
static
css
js
images
templates
flask_client.py
# -*- coding:utf-8 -*- ####################################################### > File Name: flask_client.py# > Author: GuoXiaoNao # > Mail: 250919354@qq.com # > Created Time: Mon 20 May 2019 11:52:00 AM CST ######################################################from flask import Flask, send_file# flask默认 静态文件[css,js,img]目录在static# flask默认 模板目录在 templates# 初始化app = Flask(__name__)app.config['JSON_AS_ASCII'] = False# 绑定路由url和执行函数@app.route('/index')def index(): #首页 #send_file(等价于HttpResponse()) 直接返回具体的页面 return send_file('templates/index.html')@app.route('/login')def login(): #登录 return send_file('templates/login.html')@app.route('/register')def register(): #注册 return send_file('templates/register.html')@app.route('/<username>/info')def info(username): #个人信息 return send_file('templates/about.html')@app.route('/<username>/change_info')def change_info(username): #修改个人信息 return send_file('templates/change_info.html')@app.route('/<username>/topic/release')def topic_release(username): #发表博客 return send_file('templates/release.html')@app.route('/<username>/topics')def topics(username): #个人博客列表 return send_file('templates/list.html')@app.route('/<username>/topics/detail/<t_id>')def topics_detail(username, t_id): #博客内容详情 return send_file('templates/detail.html')@app.route('/test')def test(): return send_file('templates/test.html')if __name__ == '__main__': # 启动flask服务,debug参数,True则进入调试模式 # Linux命令行中 执行 Python3 当前文件名即可开启服务 # 默认监听5000 app.run(debug=True)
'/index'
'templates/index.html'
'/login'
'templates/login.html'
url:"http://127.0.0.1:8000/v1/token",
'/register'
'templates/register.html'
url:"http://127.0.0.1:8000/v1/users",
'/<username>/info'
'templates/about.html'
url:"http://127.0.0.1:8000/v1/users/"+ blog_username
'/<username>/topic/release'
'templates/release.html'
url:"http://127.0.0.1:8000/v1/users/"+ username,
'/<username>/topics'
'templates/list.html'
url: get_url
'/<username>/topics/detail/<t_id>'
'templates/detail.html'
url: get_url
readme.md
前后端分离
需要进行token验证--装饰器
前端服务器采取flask框架,只负责根据每个路由返回对应的模板对象,模板对象中通过ajax响应到具体的url服务器地址(127.0.0.1:8000/...),也是跨域请求