写完了写完了
先说明当前开发的版本
python-->3.6.2
django -->2.0.4
djangorestframework-->3.8.2
django-rest-framework-mongoengine-->3.3.1
mongoengine-->0.15.0
mongo-->3.6.4
操作系统为deepin 15.5beta(操作系统随意,只是我在windows下无法使用uwsgi,如果会的朋友希望你能告诉我方法
postman 6.0.4
pycharm-community 2017.1-1
安装请善用project interpreter
首先创建python空项目,名字随意
创建完成之后,在命令行里cd创建的目录,然后输入
django-admin.py startproject demo .
注意后面的点,表示在此目录下新建,若没有也行,只是会多一层目录
其中这个demo是项目名,可以更改
若windows情况下报错,可以在python目录内找到django-admin.py这个文件,然后将它复制到你的项目根目录,然后命令行输入(大概,具体忘了
python django-admin.py startproject demo .
成功之后能看见新生成的文件
之后创建应用,依旧在命令行输入,这次不需要加点
django-admin.py startapp service
成功之后
现在打开demo目录下的settings.py
在 INSTALLED_APPS 中插入
'rest_framework',
'service.apps.ServiceConfig',
'rest_framework_mongoengine',
其中 1和3 是restful和mongo app的导入
2 是在应用app目录下的apps.py 下的类名
然后 settings.py 中继续添加代码:
from mongoengine import connect
connect('test')
其中 test 是数据库库名,不用提前创建
这样数据库的连接就弄好了,就两行代码
然后把 settings.py 中的 DATABASES 写成:
DATABASES = {
'default': {
'ENGINE': None,
}
}
最后把 ALLOWED_HOSTS修改成:
ALLOWED_HOSTS = ['*']
这里意思是允许任何ip访问,当然不想的朋友也能够自己写允许访问的ip
然后打开 service 目录下的 models.py ,这里是定义数据表结构的类,这里我们简单弄弄:
import mongoengine
class User(mongoengine.Document):
# 默认id
# 名字,字符串字段,最大长度36位,默认字符KirisameMarisa,允许为空
name = mongoengine.StringField(max_length=36, default='KirisameMarisa')
# 年龄,整型字段,最大长度5,不允许位空
age = mongoengine.IntField(max_length=5, null=False)
# 更多字段请百度
然后在 service 目录下新建 serializers.py :
from .models import User
from rest_framework_mongoengine import serializers as mongo_serializers
# 名字随意
class UserSerializer(mongo_serializers.DocumentSerializer):
class Meta:
# 对应类名
model = User
# 各个字段,其中_id是默认id字段
fields = ('id', 'name', 'age')
然后在 service 目录下 views.py 添加代码(views是写数据传入后要执行的操作):
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import User
from .serializers import UserSerializer
# 规定该方法只能通过post、get和delete请求
@api_view(['POST', 'GET', 'DELETE'])
# request就是你的请求
def user_api(request):
# 如果请求是get
if request.method == 'GET':
# 获取user表全部的用户
users = User.objects.all()
# 将获取结果序列化,当many=True的时候才允许返回多条数据,不然报错
serializer = UserSerializer(users, many=True)
# serializer.data是一个字典,status是状态码,2XX是成功返回
return Response(serializer.data, status=status.HTTP_200_OK)
# 如果请求是post
elif request.method == 'POST':
# request.data也是一个字典,有兴趣可以 print(request.data)
serializer = UserSerializer(data=request.data)
# 如果数据符合规定,字符长度之类的
if serializer.is_valid():
# 保存
serializer.save()
# 同上
return Response(serializer.data, status=status.HTTP_201_CREATED)
# 如果不符合规定
return Response(status=status.HTTP_400_BAD_REQUEST)
# 如果是delete请求
elif request.method == 'DELETE':
# 删除全部用户
User.objects.all().delete()
return Response(status=status.HTTP_204_NO_CONTENT)
# 同上
@api_view(['GET', 'PUT', 'DELETE'])
# name是参数,这不是我常用的方法,仅仅是和大家说有这样的用法
def another_user_api(request, username):
# 同上
if request.method == 'GET':
# 获取单个用户,其中name是字段名,username是参数
# user = User.objects.get(name=username)
# 由于我在models中没写不允许字段重复,所有get方法当有字段重复时会报错
# filter就是根据条件查找,first很容易理解,就是第一条数据
user = User.objects.filter(name=username).first()
# 将结果序列化,不需要many=True
serializer = UserSerializer(user)
# 同上
return Response(serializer.data, status=status.HTTP_200_OK)
# put一般是修改
elif request.method == 'PUT':
# 同上
user = User.objects.filter(name=username).first()
# 同上,request.data是传入的要修改的新数据
# 先把要修改的那条数据从数据库中获取,然后修改数据,保存
serializer = UserSerializer(user, data=request.data)
# 同样要检查数据合法性
if serializer.is_valid():
# 合法
serializer.save()
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
# 不合法
return Response(status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
# 同上
user = User.objects.filter(name=username).first()
# 删除
user.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
写的很详细了注释
随后在 service 目录下创建 urls.py ,并写上:
from django.conf.urls import url
from . import views
urlpatterns = [
# 定义url,移动端通过这个url访问服务端
url(r'^users/$', views.user_api),
# username就是之前views中another_user_api方法中的参数
url(r'^users/(?P[A-Za-z0-9]+)/$', views.another_user_api)
]
在 demo 目录的 urls.py 修改:
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 前面没必要添加,导入service.urls
url(r'^', include('service.urls'))
]
以上两个文件注意目录
这时候我们的服务器已经可以使用了
在命令行中进入有 manage.py 的目录,输入
python manage.py runserver
默认使用的是本机8000端口,若被使用,请参考下面
若成功,顺利打开服务器
无视我的python3.6
若是在公网中布置,出于安全考虑先把 settings.py 中的 DEBUG 和 ALLOWED_HOSTS 修改成:
DEBUG = False
然后命令行要这样写(或者8000端口被占用也可以这样写换成其他端口):
python manage.py runserver 0.0.0.0:XXXX
其中XXXX是开放端口
成功开启之后打开postman调试,在postman能使用的在android中也能使用,IOS应该也可以只是我没开发过不清楚
注意 GET 方法
ip地址+端口+在urls中定义好的地段就是访问服务端的url,注意"/"也不能漏
这个挺好理解的吧,指定的url访问指定的方法
若是访问成功,在命令行中有响应信息
2XX是访问成功,400是请求错误,500是服务器错误
插入数据看看
注意 POST 请求,Body就像是模拟请求数据,就像网页表单中的
访问成功下面就有返回信息
我们在添加一条,让两条数据不一样,然后获取全部数据看看返回
可以看到,旧的数据在上面,新的数据在下面,虽然id不是整型,但是也可以用来排序
修改 views.py 代码,让返回结果变成根据 id 降序排列:
users = User.objects.all().order_by('-id')
postman再次查询所有用户
和之前返回的数据不一样,新的在上旧的在下
将请求换成 DELETE 会删除所有用户
现在测试另一个URL
请注意URL,后面带中文就报错,换成英文就没事,我们增添一个name为英文的用户试试
这时我们缺省了 name 字段,系统会自动填充在 models.py 定义好的字符串
现在再次测试URL
返回我们刚添加的用户,至于为什么会这样,因为
至于怎么解决,我几乎不会使用这样的URL,所以我没想过怎么解决,有需要的朋友可以扩展思维
现在修改这条数据,把年龄改成23
删除也是可以运行的,不展示了
接下来写 uwsgi+nginx 的简单简单超简单的教程,不需要的可以跳过
至于怎么样安装请百度
现在我们复制一份我们刚才的项目到一个新建文件夹,名字随意,我的是 django_demo_1
现在修改其中一个项目的 views.py ,让其中一个项目按升序输出所有用户,另一个降序输出所有用户
项目A views.py
项目B views.py
在项目A目录下新建 start.ini 配置文件
# start.ini file
[uwsgi]
# 项目运行的端口
http = 127.0.0.1:8001
# 在app加载前切换到该目录,设置为Django项目根目录,manage.py所在目录
chdir = /media/aikoyanye/hdd/workspace/python/django_demo
# 加载指定的python WSGI模块,设置为Django项目的wsgi文件,wsgi.py文件所在目录
module = demo.wsgi
# 启动一个master进程来管理其他进程
master = true
# 工作的进程数
processes = 4
# 每个进程下的线程数量
threads = 2
# 当服务器退出的时候自动删除unix socket文件和pid文件
vacuum = true
# 使进程在后台运行,并将日志打到指定的日志文件或者udp服务器
daemonize = /media/aikoyanye/hdd/workspace/python/uwsgi.log
# uwsgi监听的http
socket = 127.0.0.1:9123
注意注释!!!
项目B下也新建 start.ini
# start.ini file
[uwsgi]
# 项目运行的端口
http = 127.0.0.1:8002
# 在app加载前切换到该目录,设置为Django项目根目录,manage.py所在目录
chdir = /media/aikoyanye/hdd/workspace/python/django_demo_1
# 加载指定的python WSGI模块,设置为Django项目的wsgi文件,wsgi.py文件所在目录
module = demo.wsgi
# 启动一个master进程来管理其他进程
master = true
# 工作的进程数
processes = 4
# 每个进程下的线程数量
threads = 2
# 当服务器退出的时候自动删除unix socket文件和pid文件
vacuum = true
# 使进程在后台运行,并将日志打到指定的日志文件或者udp服务器
daemonize = /media/aikoyanye/hdd/workspace/python/django_demo_1/uwsgi.log
# uwsgi监听的http
socket = 127.0.0.1:9124
注意 http 和 socket 的端口
启动任然是命令行,cd在项目A\B的目录,然后分别启动:
uwsgi --ini start.ini
然后依旧
http://127.0.0.1:8001/users/
http://127.0.0.1:8002/users/
可以照常访问服务器
结束,终止所有uwsgi服务
sudo killall -9 uwsgi
然后在目录 /etc/nginx 下的 nginx.conf 的 http 下添加代码:
upstream django {
server 127.0.0.1:9123;
server 127.0.0.1:9124;
}
server {
listen 8000; # 设置监听端口号
server_name 127.0.0.1; # 设置对外访问入口,可以是域名可以是IP地址,我设置的是IP
charset UTF-8; # 设置访问的语言编码
client_max_body_size 75M; #
access_log /var/log/nginx/test1_access.log; # 访问日志记录
error_log /var/log/nginx/test1_error.log; # 错误日志记
location / { # 设置虚拟主机的基本信息
uwsgi_pass django; # 刚才uwsgi设置的socket
include /etc/nginx/uwsgi_params;
}
}
任然在 /etc/nginx 下新建文件 uwsgi_params ,复制粘贴:
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
开启服务
/etc/init.d/nginx start
然后重启uwsgi服务,先结束所有在开过,访问:
http://127.0.0.1:8000/users/
就会发现:
可以注意到返回的数据是先 倒叙 然后 升序 (或者先 升序 然后 倒叙
这里不解释太多,请参考 负载均衡
接下来准备开发移动端的代码,这里我选择android 因为我不会ios开发:)
首先修改 models.py 代码,让 name 字段不允许重复,并且新增 pwd 字段:
import mongoengine
class User(mongoengine.Document):
# 默认id
# 名字,字符串字段,最大长度36位,默认字符KirisameMarisa,允许为空,不允许重复,
name = mongoengine.StringField(max_length=36, unique=True)
# 年龄,整型字段,最大长度5,不允许位空
age = mongoengine.IntField(max_length=5, null=False)
# 密码,最长12,不允许为空
pwd = mongoengine.StringField(max_length=12, null=False)
# 更多字段请百度
修改 serializers.py 文件:
from .models import User
from rest_framework_mongoengine import serializers as mongo_serializers
# 名字随意
class UserSerializer(mongo_serializers.DocumentSerializer):
class Meta:
# 对应类名
model = User
# 各个字段,其中id是默认id字段
fields = ('id', 'name', 'age', 'pwd')
让序列化把新增的 pwd 字段加进去
修改 views.py 的代码,在里面添加(随意项目A也行项目B也行:
# 这样做的话我每次都是POST请求,但是返回却是自定义
@api_view(['POST'])
def android_user_api(request):
if request.method == 'POST':
_data = dict(request.data)
# 之前说过request.data是一个字典,可以利用这个
if _data['method'][0] == '_GET':
user = User.objects.get(name=_data['name'][0], pwd=_data['pwd'][0])
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)
elif _data['method'][0] == '_POST':
# request.data 中多余的数据不会保存到数据库中
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_400_BAD_REQUEST)
elif _data['method'][0] == '_PUT':
user = User.objects.get(name=_data['name'][0])
serializer = UserSerializer(user, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_202_ACCEPTED)
return Response(status=status.HTTP_400_BAD_REQUEST)
elif _data['method'][0] == '_DELETE':
User.objects.get(name=_data['name'][0]).delete()
return Response(status=status.HTTP_204_NO_CONTENT)
然后修改 service 目录下的 urls.py :
from django.conf.urls import url
from . import views
urlpatterns = [
# 定义url,移动端通过这个url访问服务端
url(r'^users/$', views.user_api),
# username就是之前views中another_user_api方法中的参数
url(r'^users/(?P[A-Za-z0-9]+)/$', views.another_user_api),
# 安卓端用api
url(r'^android_user/$', views.android_user_api)
]
写的很简单粗暴,就是一个模拟登录注册的增删查改,再次之前先清空user表,我直接删除表了,在mongo shell中:
use test
db.user.drop()
然后重启 django 服务:
python manage.py runserver
在postman上可以测试,当读取数据位空时会报错,这是故意的,在 Android 端会用到报错
接下来可以动手打 Android 端的代码了,我用的是 kotlin ,java代码也差不多的
我会贴出kotlin代码,但是会讲清楚思路,用java的同学按照思路来执行也是可以使用的
首先导入jar包,用于访问url地址(文章末尾我会给出项目地址:
第二个包看情况导入,因为我是从自带的lib中提取的
然后在 Activity 中写一个静态的handler
companion object {
val handler = Handler(){
when(it.what){
}
return@Handler true
}
}
其中 it 是就是 msg
然后新建一个类 MyTextListener ,让它继承 TextHttpResponseHandler:
import android.os.Handler
import android.os.Message
import com.loopj.android.http.TextHttpResponseHandler
// 对用activity中的handler,通过msg把数据传回activity,s_state是成功访问的状态码,f_state是访问失败的状态码
// 这个类可以自己定义,不一定按照我这种写法
// 花式构造函数
class MyTextListener(var handler: Handler, var s_state: Int, var f_state: Int): TextHttpResponseHandler() {
val msg = Message.obtain()
override fun onSuccess(p0: Int, p1: Array?, p2: String?) {
// 成功访问
msg.what = s_state
msg.obj = p2
handler.sendMessage(msg)
}
override fun onFailure(p0: Int, p1: Array?, p2: String?, p3: Throwable?) {
// 失败访问
msg.what = f_state
handler.sendMessage(msg)
}
}
然后实例化一个 AsyncHttpClient,用于访问url
val client = AsyncHttpClient()
实例化 RequestParams,用于存放访问url时附带的表单数据
val params = RequestParams()
如何访问?
// client.post(url, params,HttpResponseHandler)
client.post("http://10.0.2.2/android_user/", params, MyTextListener(MainActivity.handler, 2, 0x2))
如果你的 django 代码是复制我上面写的,请你这样写:
params.put("method", "_DELETE")
params.put("name", MainActivity.delete_name.text.toString())
// 虚拟机访问本机地址,如果是用真机的话,那就只能部署在公网上然后访问
client.post("http://10.0.2.2:8000/android_user/", params, MyTextListener(MainActivity.handler, 1, 0x1))
params.put("method", "_POST")
params.put("name", MainActivity.add_name.text.toString())
params.put("age", MainActivity.add_age.text.toString())
params.put("pwd", MainActivity.add_pwd.text.toString())
client.post("http://10.0.2.2:8000/android_user/", params, MyTextListener(MainActivity.handler, 2, 0x2))
params.put("method", "_GET")
params.put("name", MainActivity.get_name.text.toString())
params.put("pwd", MainActivity.get_pwd.text.toString())
client.post("http://10.0.2.2:8000/android_user/", params, MyTextListener(MainActivity.handler, 3, 0x3))
params.put("method", "_PUT")
params.put("name", MainActivity.put_name.text.toString())
params.put("age", MainActivity.put_age.text.toString())
params.put("pwd", MainActivity.put_pwd.text.toString())
client.post("http://10.0.2.2:8000/android_user/", params, MyTextListener(MainActivity.handler, 4, 0x4))
在 handler 处写判断:
val handler = Handler(){
when(it.what){
1 ->{
} 0x1 ->{
} 2 -> {
} 0x2 ->{
} 3 -> {
} 0x3 -> {
} 4 -> {
} 0x4 -> {
}
}
return@Handler true
}
注意 1 0x1 这些是你自己定义的 成功访问码和失败访问码,这里写上你想要执行的代码即可
别忘了,权限,只需要一个访问网络权限就好了
然后就可以使用啦
可以看控制台输出:
可以看到,新添加的数据在下面显示,当然这是我自己写的
模拟登录,可以看到数据是json格式传进来的,然后在自行解析,可以得到下面的数据
就这样一个移动端服务器写完了
忘记给了哈哈哈
github