使用的是<推荐系统开发实战>源码, 官方复现说明可参考公众号搜索与推荐Wiki( 微信号: SearchAndRecWiki )
之前看到这篇有几个同学评论说数据库导入报错, 是下载的源码不对, 重新下载官方给出的源码即可
https://drive.google.com/drive/folders/1z17hFhdM253W1UKLtHPKEVQ0jZKQlodN
Vue环境搭建:
Node.js,版本为10.13
没找到,官网推荐10.16
https://www.cnblogs.com/zhaomeizi/p/8483597.html
在https://nodejs.org/en/下载,安装node,改变安装盘符、默认安装4项
二、设置nodejs prefix(全局)和cache(缓存)路径
1、在nodejs安装路径下,新建node_global和node_cache两个文件夹
2、设置缓存文件夹
npm config set cache "D:\Program Files\nodejs\node_cache"
设置全局模块存放路径
npm config set prefix "D:\Program Files\nodejs\node_global"
设置成功后,之后用命令npm install XXX -g安装以后模块就在D:\vueProject\nodejs\node_global里
三、基于 Node.js 安装cnpm(淘宝镜像)
npm install -g cnpm --registry=https://registry.npm.taobao.org
四、设置环境变量
说明:设置环境变量可以使得住任意目录下都可以使用cnpm、vue等命令,而不需要输入全路径
1、鼠标右键"此电脑",选择“属性”菜单,在弹出的“系统”对话框中左侧选择“高级系统设置”,弹出“系统属性”对话框。
2、修改系统变量PATH
五、安装Vue
cnpm install vue -g
六、安装vue命令行工具,即vue-cli 脚手架
cnpm install vue-cli -g
七、新项目的创建
1.打开存放新建项目的文件夹,弹出命令提示符
2.根据模版创建新项目
在当前目录下输入“vue init webpack-simple 项目名称(使用英文)”。
vue init webpack-simple mytest
3、安装工程依赖模块
定位到mytest的工程目录下,安装该工程依赖的模块,这些模块将被安装在:mytest\node_module目录下,node_module文件夹会被新建,而且根据package.json的配置下载该项目的modules
cd mytest
cnpm install
4、运行该项目,测试一下该项目是否能够正常工作,这种方式是用nodejs来启动。
cnpm run dev
后端搭建
activate MusicRec cd /d D:\anacondaProject\MusicRec\z-others\files |
pip install -r requirement.txt |
运行说明
mysql新建musicrec数据库,将MusicRecSys/MusicRec/z-others/files/musicrec.sql 文件导入
修改 MusicRecSys/MusicRec/MusicRec/settings.py 文件中的ALLOWED_HOSTS为本地IP地址和本地mysql配置信息
修改 MusicRecSys/MusicRec-Vue/config/index.js 中的 serverUrl
修改 MusicRecSys/MusicRec-Vue/src/assets/js/linkBase.js 中的 serverUrl
进入 MusicRecSys/MusicRec 执行python manage.py runserver 0.0.0.0:8000
activate MusicRec cd /d D:\anacondaProject\MusicRec python manage.py runserver 0.0.0.0:8000 |
进入 MusicRecSys/MusicRec-Vue 执行npm install / npm run dev
cd /d D:\Workspaces\Vue\MusicRec-Vue 输入 npm install 或 npm run dev |
浏览器输入 http://127.0.0.1:8001 访问
npm install时有警告
D:\Workspaces\Vue\MusicRec-Vue>npm install npm WARN ajv-keywords@2.1.1 requires a peer of ajv@^5.0.0 but none is installed. You must install peer dependencies yourself. npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
audited 29868 packages in 43.88s found 778 vulnerabilities (67 low, 11 moderate, 699 high, 1 critical) run `npm audit fix` to fix them, or `npm audit` for details |
成功run
admin,admin
实现细节
Django编写接口
参考https://www.cnblogs.com/sixrain/p/9138442.html
Django 是用Python开发的一个免费开源的Web框架,可以用于快速搭建高性能,优雅的网站。Django 中提供了开发网站经常用到的模块,常见的代码都为你写好了,减少重复的代码。
Django 目录结构
网址入口,关联到对应的views.py中的一个函数(或者generic类),访问网址就对应一个函数。
处理用户发出的请求,从urls.py中对应过来, 通过渲染templates中的网页可以将显示内容,比如登陆后的用户名,用户请求的数据,输出到网页。
与数据库操作相关,存入或读取数据时用到这个,当然用不到数据库的时候 你可以不使用。
表单,用户在浏览器上输入数据提交,对数据的验证工作以及输入框的生成等工作,当然你也可以不使用。
templates 文件夹
views.py 中的函数渲染templates中的Html模板,得到动态内容的网页,当然可以用缓存来提高速度。
后台,可以用很少量的代码就拥有一个强大的后台。
Django 的设置,配置文件,比如 DEBUG 的开关,静态文件的位置等。
URL:
有关详细信息,请参阅:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
例子:
函数视图Function views
1. 添加导入:from my_app import views
2. 将 URL 添加到 URLPATTERNS:path('', views.home, name='home')
基于类的view,Class-based views
1. 添加导入:from other_app.views import Home
2. 将 URL 添加到 URLPATTERNS:path('', Home.as_view(), name='home')
Including another URLconf
1. 导入 include() 功能:from django.urls import include, path
2. 将 URL 添加到 URLPATTERNS:path('blog/', include('blog.urls'))
Setting.py
在MusicRec文件夹中
接口
接口使用rest_framework,rest_framework是一套基于Django 的 REST 框架,是一个强大灵活的构建 Web API 的工具包。
写接口三步完成:连接数据库、取数据、数据输出
1)连接数据库
在工程目录下的settings.py文件下配置
2)取数据
既然要取数据,那model肯定得和数据库的一致,我发现一个快捷的方式可以把数据库中的表生成对应的model,在项目目录下执行命令
python manage.py inspectdb
取我们表的model拷贝到app下的models.py里
class Meizis(models.Model): mid = models.CharField(max_length=10) title = models.CharField(max_length=50, blank=True, null=True) picname = models.CharField(max_length=10, blank=True, null=True) page_url = models.CharField(max_length=50, blank=True, null=True) img_url = models.CharField(max_length=50, blank=True, null=True)
class Meta: managed = False db_table = 'meizi_meizis' |
创建一个序列化Serializer类
提供序列化和反序列化的途径,使之可以转化为,某种表现形式如json。我们可以借助serializer来实现,类似于Django表单(form)的运作方式。在app目录下,创建文件serializers.py。
这样在views.py就可以来获取数据库的数据了
meizis = Meizis.objects.all() serializer = MeiziSerializer(meizis, many=True) return Response(serializer.data) |
3) 数据输出客户端或前端
REST框架提供了两种编写API视图的封装。
@api_view装饰器,基于方法的视图。
继承APIView类,基于类的视图。
request.data会自行处理输入的json请求
使用格式后缀明确的指向指定的格式,需要添加一个format关键字参数
http://127.0.0.1:8000/getlist.json # JSON 后缀
http://127.0.0.1:8000/getlist.api # 可视化 API 后缀
http://127.0.0.1:8000/getlist/ code="print 123"post
最后别忘了在urls.py配置URL,通过浏览器就可以看到json数据了。
4)分页
我们的数据有好几千条,一块返回来很不合理,所以需要分页,当然rest_framework框架提供了这个功能,post请求不支持,需要自己查数据库或者切片来进行返回
在views.py里编写
@api_view(['GET']) def getlist(request, format=None): if request.method == 'GET': meizis = Meizis.objects.values('mid','title').distinct() # http: // 127.0.0.1:8000 / getlist?limit = 20 # http: // 127.0.0.1:8000 / getlist?limit = 20 & offset = 20 # http: // 127.0.0.1:8000 / getlist?limit = 20 & offset = 40 # 根据url参数 获取分页数据 obj = StandardResultSetPagination() page_list = obj.paginate_queryset(meizis, request) # 对数据序列化 普通序列化 显示的只是数据 ser = ListSerialize(instance=page_list, many=True) # 多个many=True # instance:把对象序列化 response = obj.get_paginated_response(ser.data) return response |
源码获取推荐
# 获取单个歌单的推荐 def getRecBasedOne(pl_id): pl_tags = PlayList.objects.filter(pl_id= pl_id).values("pl_tags")[0]["pl_tags"] pl_tags_list = pl_tags.replace(" ","").split(",") print(pl_tags_list) results = list(PlayList.objects.filter(pl_tags=pl_tags).filter(~Q(pl_id=pl_id))) if results.__len__() < 10: for tag in pl_tags_list: for pl in PlayList.objects.filter(pl_tags__contains=tag): results.append(pl) if results.__len__() >= 10: break if results.__len__() >= 10: break # print(results) # 拼接返回结果 rec_pl_list = list() for one in results: rec_pl_list.append({ "id": one.pl_id, "name": one.pl_name, "creator": one.pl_creator.u_name, "img_url": one.pl_img_url, "cate": "2" }) return rec_pl_list |
可以看出,歌曲的推荐通过tag选取。
# 获得歌曲、歌单标签推荐 def getSongAndPlRecTags(songs, base_click): song_tags = list() pl_tags = list() if base_click == 1: # 表示前端是基于点击行为进入为你推荐模块 click_songs = UserBrowse.objects.filter(click_cate="3").values("click_id") if click_songs.__len__() != 0: for one in click_songs: filter_one = SongTag.objects.filter(song_id=one["click_id"]) if filter_one.__len__() != 0 and filter_one[0].tag not in song_tags: song_tags.append(filter_one[0].tag)
# 歌单tag pl_one = PlayListToSongs.objects.filter( song_id=filter_one[0].song_id ) if pl_one.__len__() !=0: for pl_tag_one in PlayListToTag.objects.filter(pl_id=pl_one[0].song_id): if pl_tag_one.tag not in pl_tags: pl_tags.append(pl_tag_one.tag)
if songs.__len__() != 0: # 表示前端选择了相关歌曲 for sing in songs: choose_one = SongTag.objects.filter(song_id=sing) if choose_one.__len__() != 0 and choose_one[0].tag not in song_tags: song_tags.append(choose_one[0].tag)
# 歌单tag pl_one = PlayListToSongs.objects.filter(song_id=choose_one[0].song_id) if pl_one.__len__() != 0: for pl_tag_one in PlayListToTag.objects.filter(pl_id=pl_one[0].song_id): if pl_tag_one.tag not in pl_tags: pl_tags.append(pl_tag_one.tag) # print("songs_tags_by_click %s" % songs_tags_by_click) # print("pl_tags_by_click %s" % pl_tags_by_click) else: # 表示用户是首次进入为你推荐模块 if songs.__len__() != 0: # 表示前端选择了相关歌曲 for sing in songs: choose_one = SongTag.objects.filter(song_id=sing) if choose_one.__len__() != 0 and choose_one[0].tag not in song_tags: song_tags.append(choose_one[0].tag)
# 歌单tag pl_one = PlayListToSongs.objects.filter(song_id=choose_one[0].song_id) if pl_one.__len__() != 0: for pl_tag_one in PlayListToTag.objects.filter(pl_id=pl_one[0].song_id): if pl_tag_one.tag not in pl_tags: pl_tags.append(pl_tag_one.tag) # print("songs_tags_by_choose: %s" % songs_tags_by_choose) # print("pl_tags_by_choose: %s" % pl_tags_by_choose) # 如果 click 和 choose的tag不够 以 hot来补充 if song_tags.__len__() < 15: hot_tag_dict = dict() for one in SongTag.objects.all(): hot_tag_dict.setdefault(one.tag, 0) hot_tag_dict[one.tag] += 1 tag_dict_song = sorted(hot_tag_dict.items(), key=lambda k: k[1], reverse=True)[:15-song_tags.__len__()] for one in tag_dict_song: if one[0] not in song_tags: song_tags.append(one[0]) # print("songs_tags_by_hot: %s" % songs_tags_by_hot)
# 如果 click 和 choose的tag不够 以 hot来补充 if pl_tags.__len__() < 15: hot_tag_dict = dict() for one in PlayListToTag.objects.all(): hot_tag_dict.setdefault(one.tag, 0) hot_tag_dict[one.tag] += 1 tag_dict_pl = sorted(hot_tag_dict.items(), key=lambda k: k[1], reverse=True)[:15-pl_tags.__len__()] for one in tag_dict_pl: if one[0] not in pl_tags: pl_tags.append(one[0]) # print("pl_tags_by_hot: %s" % pl_tags_by_hot)
return song_tags,pl_tags |
而这些标签来源于:用户选择的标签,用户行为对象对应的标签,热门标签