LPOJ网址:www.lpoj.cn
LPOJ文档:docs.lpoj.cn
后端开发
后端的开发比前端的开发要简单很多,因为开发者只需专注于数据的呈现即可,不必关心显示的逻辑。在众多后端框架中,我选择了开发和学习成本较低的Python语言中的Django框架,同时Python语言与我们的判题程序又相辅相成,因此是一个很好的选择。Django是一个开源的Web框架,整体采用MVC的设计模式。但是在本系统中,我们并不需要构建自己的前端页面,我们只关心有哪些数据要交付给前端,因此本系统采用Django中的REST框架来快速构建自己的数据API。其中REST是RESTful的简称。RESTful是一种软件架构风格,它采用http协议,非常简单,任意客户端都能运行。因此我们只需要关心有哪些数据要交付给前端即可。其中REST框架主要分为三个部分,分别是Model,Serializer和View。Model即数据层,定义了数据在数据库中的形式。Serializer即序列化器,其中定义了各种数据库的操作,相当于一个中间层,最后View层决定了哪些数据可以呈现给用户,怎么呈现给用户等等。所以当开发者编写API时,只要着重于实现这三层即可。因为有了RESTful框架,这一切都变得非常简单便利。
Django REST framework 介绍
现在越来越多的网站采用前后端分离技术。在前后端分离的应用模式中,后端仅返回前端所需要的数据,不再渲染HTML页面,不再控制前端的效果。前端用户想要看到什么效果,从后端请求的数据如何加载到前端中,都由前端浏览器自己决定。在前后端分离的应用模式中,前端与后端的耦合度相对较低,我们通常将后端开发的每一视图都成为一个接口,或者API,前端通过访问接口来对数据进行增删改查。因此我们采用它进行我们的后端开发,同时他配套有各种各样的服务,如权限管理,路由管理,限流等等,这些在我们的判题系统中尤为重要。
因此本系统采用Django REST framework 开发,接下来跟着我的步伐,一步一步了解Django是如何在LPOJ中运作的!
安装Python3.7
整个开发在Ubuntu 18.10环境下进行,接下来所有教程均在此环境下进行,如果是Windows,请自行查阅安装方法。
Ubuntu自带Python3.6,经测试3.6一样适合本环境。但是Ubuntu中,Python命令默认是Python2,所以我们可以通过如下命令改变默认值,当然你也可以不改变,然后每次执行Python命令时使用的命令应该是Python3
sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 100
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 150
如果要还原到Python2,执行:
sudo update-alternatives --config python
接下来我们安装 pip ,pip是Python的一个包管理工具,可以使你很方便的下载Python的一些包,我们通过如下命令即可安装
sudo apt install python3-pip
安装必要环境
如若你要二次开发本OJ,首先要配置环境。
本后端主要用了如下几个库
- Django==2.2.1
- djangorestframework==3.9.3
- django-filter==2.1.0
- django-cors-headers==2.5.3
- mysqlclient==1.4.2.post1
其中第一个是整个Django框架所需的库,第二个是我们的REST Framework,第三个是用于实现过滤功能的一个框架,第四个是用于实现跨域访问的框架,第五个是访问Mysql所需要的库。
我们进入到Backend目录并执行如下命令即可。
pip3 install -r requirements.txt
如果在安装过程中出现错误,请自行百度错误信息并解决。
如果安装mysqlclient出现 /bin/sh: 1: mysql_config: not found错误,请执行如下语句:
sudo apt-get install libmysqlclient-dev
如无意外,应该会安装成功!
安装Mysql
其他操作请自行百度。
sudo apt-get install mysql-server
安装成功后我们新建一下数据库和修改一些配置。我们登录到数据库中然后执行如下语句。
mysql -uroot -p
mysql > CREATE DATABASE LPOJ DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
mysql > USE mysql
mysql > GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'your_password' WITH GRANT OPTION;
mysql > ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password by 'your_password';
mysql > flush privileges;
mysql > exit;
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
具体内容的意思就是新建一个名为LPOJ的数据库,并给予root用户所有权限。
编辑setting.py
接下来我们修改一下配置文件,我们进入到Backend/Backend目录下,并修改setting.py文件
cd Backend
cd Backend
nano setting.py
我们主要修改数据库的配置信息,我们找到如下语句并修改else后面的内容为你的数据库信息。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'LPOJ',
'USER': os.environ.get("DB_USER") if os.environ.get("DB_USER") else 'root' , # 修改root为你的数据库用户
'PASSWORD':os.environ.get("DB_PASSWORD") if os.environ.get("DB_PASSWORD") else 'lpojdatabase', # 修改lpojdatabase为你的数据库密码
'HOST': os.environ.get("DB_HOST") if os.environ.get("DB_HOST") else 'lpojdatabase',# 修改lpojdatabase为你的数据库IP地址,如localhost
'PORT': os.environ.get("DB_PORT") if os.environ.get("DB_PORT") else 3306,
}
}
运行后端
在Backend根目录中的manager.py是Django非常重要的一个文件,通过它我们可以实现很多操作,比较常用的操作如下:
makemigrations 将你编写的代码变成sql语句
migrate 将你编写的sql语句同步到数据库中
runserver 运行你的后端
首先将LPOJ的后端代码生成mysql语句,然后我们再同步到数据库中
python manager.py makemigrations
python manager.py migrate
执行完毕后查看数据库,会发现你的LPOJ数据库中多了若干表格,这些都是Django运行所需要的表格。
在运行之前让我们先制作一个超级用户,这在部署文档里面有写
echo "from django.contrib.auth.models import User; User.objects.filter(email=\"[email protected]\").delete(); User.objects.create_superuser(\"admin\", \"[email protected]\", \"admin\")" | python manage.py shell
最后我们再执行运行语句,以后如果对后端有任何修改,只需先执行
python manager.py makemigrations
python manager.py migrate
将修改同步到数据库中,然后再通过如下命令运行
python manage.py runserver 0.0.0.0:8000
意思是将后端暴露到8000端口中,并监听所有地址的访问。
接下来,我们就可以通过在浏览器中通过localhost:8000访问你的后端了。
如果能访问,证明你的后端已部署成功!
模块说明与开发
在阅读如下教程时,请先自行学习Django REST Framework的一些基本教程
接下在会对一些模块做简单介绍
- Backend 保存整个后台的配置文件和路由
- blog 博客模块
- board 排行榜相关模块
- contest 比赛相关模块
- judgestatus 提交信息模块
- problem 题目模块
- user 用户相关模块
- wiki Wiki模块
- ProblemData 存放题目数据的文件夹
本教程不会对每个模块都做详细介绍,但是会列出各文件的功能,和点出一些自己的实现方法。
Backend
setting.py
此处添加你自己的模块
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'django_filters',
'judgestatus',
'corsheaders',
'problem',
'user',
'contest',
'board',
'blog',
'wiki'
]
此处填写一下你使用的框架的设置,比如限流设置就是在此处实现。
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'anon': '300/m',
'judge': '300/m',
'post': '2000/m',
}
}
此处是一下数据库的设置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'LPOJ',
'USER': os.environ.get("DB_USER") if os.environ.get("DB_USER") else 'root' ,
'PASSWORD':os.environ.get("DB_PASSWORD") if os.environ.get("DB_PASSWORD") else 'lpojdatabase',
'HOST': os.environ.get("DB_HOST") if os.environ.get("DB_HOST") else 'lpojdatabase',
'PORT': os.environ.get("DB_PORT") if os.environ.get("DB_PORT") else 3306,
}
}
此处是跨域访问的设置
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
此处是Session过期设置
SESSION_COOKIE_AGE = 60 * 60 * 24 # 30分钟
SESSION_SAVE_EVERY_REQUEST = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
urls.py
这个文件主要是用来填写路由信息
修改后端
已存在的接口可以根据需要修改,这里将以添加一个模块为例,展示如何添加一个自己的后台模块或者在已有模块中添加一个API
在已有模块中添加一个API
现在我们假定要在Blog模块中添加一个博客回复功能
- 添加你的model
修改models.py文件,在文件最后添加
class BlogComment(models.Model):
# 此处编写你的字段
username = models.CharField(max_length=50)
msg = models.CharField(max_length=1000)
time = models.DateField(auto_now=True)
# 复制粘贴即可
objects = models.Manager()
- 添加你的序列化器
修改serializers.py文件来
from .models import BlogComment # 记得import
class BlogCommentSerializer(serializers.ModelSerializer):
class Meta:
model = BlogComment # 设置为你的model
fields = '__all__'
具体的修改参考Django教程,这里使用REST Framework模块,使得这一切都如此简单
- 添加视图类
修改views.py来添加
from .models import BlogComment
from .serializers import BlogCommentSerializer
class BlogCommentView(viewsets.ModelViewSet): # 得益于rest_framework,构建一个视图变得很方便
queryset = BlogComment.objects.all() # 查询集合,具体功能和修改,参考Django文档
serializer_class = BlogCommentSerializer # 你的序列化器
filter_fields = ('username', 'time') # 过滤字段,使得你可以在http请求中过滤,如 /?username=123456
pagination_class = LimitOffsetPagination # 分页器,使得你的http请求支持分页,具体自行百度
permission_classes = (ManagerOnly,) # 权限过滤器,具体参考Django文档
throttle_scope = "post" # 限流类,直接复制粘贴即可
throttle_classes = [ScopedRateThrottle, ]
- 添加路由信息
我们修改urls.py文件在 urlpatterns = [url(’’, include(routers.urls)),] 上方添加
routers.register('blogcomment', views.BlogCommentView) # 第一个参数为你的路由,第二个参数为你的视图
至此,我们成功的添加了一个自己的API
现在只需要同步你的数据库
python manager.py makemigrations blog
python manager.py migrate blog
现在可以通过 http://localhost:8000/blogcomment/
来访问你的API了
我们可以通过POST请求来添加一个数据,用PUT请求来修改一个数据
用DELETE请求来删除一个数据,用GET请求来获取所有数据
具体自行阅读HTTP请求的相关教程。
我们可以通过primary_key来获取单个数据,如
http://localhost:8000/blogcomment/1/
Django会默认的生成一个ID字段作为primary_key,当然你也可以自己制定,具体自行参阅Django教程。
我们可以通过limit和offset来实现分页,如
http://localhost:8000/blogcomment/?limit=50&offset=10
我们可以直接过滤设定好的字段,如
http://localhost:8000/blogcomment/?username=123&time=2019-5-29
这样一个普通的API很不安全,因此要做权限认证,自行编辑一个权限类,然后添加到permission_classes中,具体代码编写自行参考Django教程
新建一个模块
我们可以直接新建一个模块,这非常简单,首先我们先执行如下命令
python manage.py startapp yourappname
这样就成功新建一个模块了,我们查看目录会发现多了一个yourappname的文件夹
然后我们要注册我们的模块,我们先修改Backend/Backend/setting.py文件
在INSTALLED_APPS中添加你的模块名字
然后我们就可以参阅上面的在已有模块中添加一个API的教程来添加你的API
如果文件不存在,自行新建对应的文件在目录里即可,具体的内容也可以参考现有的模块。
我们添加完毕后要在Backend中注册你的路由信息。
我们修改Backend/Backend/urls.py在urlpatterns中添加
url(r’’, include(‘yourappname.urls’))
这样就可以了。
不要忘了同步你的数据库
python manager.py makemigrations yourappname
python manager.py migrate yourappname
以上就是后端开发教程,接下来会介绍LPOJ中各个模块的功能