前天研究了一番Django,果然很好使,今天就使用Django设计一个简单API Server试试。
目标
Model类型
以一个旅游日志App为例,我们需要两个Model分别为Trip和Day。
1 | //保存每段旅程相关信息 |
URL分配
HTTP方法 | 动作 | URL |
---|---|---|
GET | 获取Trip列表 | http://example.com/api001/trips/ |
GET | 获取具体Trip和包含日程信息 | http://example.com/api001/trips/[id] |
GET | 获取具体含日程信息 | http://example.com/api001/trips/[tripID]/[dayID] |
实现过程
创建Project和App
首先使用$ django-admin startproject sampleapi
创建一个Project,按照Django学习笔记中方法设置数据库。
使用$ python manage.py startapp api001
来创建第一版API。并且Project配置文件samplepai/settings.py
中增加api001
1 | INSTALLED_APPS = ( |
Models
创建Model
编辑sampleapi/api001/models.py
文件,增加两个model1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28# encoding: utf-8
from django.db import models
# Create your modelss here.
class Trip(models.Model):
id = models.AutoField(primary_key=True) #唯一ID,
trip_title = models. CharField(max_length=100) #标题,
trip_subtitle = models.CharField(max_length=100) #副标题,
cover = models.TextField() #封面照片名称
pub_time = models.DateTimeField('date published') #时间,
place = models.CharField(max_length=100) #地点,
likeCount = models.IntegerField(default=0) #赞的数量
def __str__(self): # __unicode__ on Python 2
return self.trip_title
class Day(models.Model):
id = models.AutoField(primary_key=True) #唯一ID,
trip_title = models.CharField(max_length=100) #标题,
trip_subtitle = models.CharField(max_length=100) #副标题,
trip_day = models.IntegerField(default=1) #旅行日期,
cover = models.TextField() #封面照片名称
pub_time = models.DateTimeField('date published') #时间,
place = models.CharField(max_length=100) #地点,
likeCount = models.IntegerField(default=0) #赞的数量,
content = models.TextField() #日记内容,
trip = models.ForeignKey(Trip) #对应的旅程
def __str__(self): # __unicode__ on Python 2
return self.trip_title
创建迁移(migrations)文件,如果成功则会产生名称为0001_initial.py的文件。1
2
3
4
5
6$ python manage.py makemigrations api001
Migrations for 'api001':
0001_initial.py:
- Create model Day
- Create model Trip
- Add field trip to day
1 | $ python manage.py sqlmigrate api001 0001 #产生迁移语句,运行后可以看产生的相应SQL语句。此时并没有真正创建数据库。 |
增加若干数据
使用shell
模式很方便的操作数据和熟悉Django的API,现在我们也用Shell增加几条数据。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39$ python manage.py shell #进入shell模式
# 引入数据模型
from api001.models import Trip,Day
# 目前并没有数据
Trip.objects.all()
[]
# 引入时间模块
from django.utils import timezone
# 创建第一个Trip数据
'trip_title',trip_subtitle = "subtitle", cover = 'notyet.png',pub_time = timezone.now(),place = "I don't know") firsttrip = Trip(trip_title =
# 保存第一个Trip数据
firsttrip.save()
# 读取firsttrip的ID
firsttrip.id
# 读取模型的各个数据
firsttrip.trip_title
'trip_title'
firsttrip.cover
'notyet.png'
firsttrip.pub_time
datetime.datetime(2015, 10, 31, 9, 17, 0, 497149, tzinfo=<UTC>)
##关联元素
# 读取第一个Trip
1) t = Trip.objects.get(pk=
# 增加第一第二天行程
"day1", trip_day = 1 ,trip_subtitle = "subTitleHere", cover = "notyet.png", pub_time = timezone.now(),place = "I still havar no clue", content = "comments.") t.day_set.create(trip_title =
<Day: day1>
"day2", trip_day = 2 ,trip_subtitle = "subTitleHere", cover = "notyet.png", pub_time = timezone.now(),place = "I still havar no clue", content = "comments.") t.day_set.create(trip_title =
<Day: day2>
# 查看t Trip包含的行程
t.day_set.all()
[<Day: day1>, <Day: day2>]
View & URL
配置View
打开api001/views.py
,增加下面三行1
2
3
4
5from django.http import HttpResponse #引入HttpResponse
#定义indexView的动作
def index(request):
return HttpResponse("Hello, world. You're at the api001 index.")
配置URL
为了调用该目录,还需要配置URL路由表,在api001
文件夹下面增加一个urls.py
文件。此时目录结构应该如下。1
2
3
4
5
6
7
8api001/
├── admin.py
├── __init__.py
├── migrations
├── models.py
├── tests.py
├── urls.py
└── views.py
编辑api001/urls.py
,增加下面几行1
2
3
4
5
6
7
8
9from django.conf.urls import url
#引入本目录下的views模块
from . import views
#URL匹配规则,使用正则表达式匹配。
urlpatterns = [
url(r'^$', views.index, name='index'),
]
编辑上级目录中的项目路由表配置文件,即sampleapi/sampleapi/urls.py
,增加下面几行1
2
3
4
5
6from django.conf.urls import include, url
urlpatterns = [
#引入api001 App中的urls.py文件,并指向api001/ URL路径
url(r'^api001/', include('api001.urls')),
]
此时运行python manager.py 0.0.0.0:8000
,然后访问http://xxx.xxx.xxx.xxx:8000/api001/
即可看到欢迎提示。说明URL和View配置正常。1
Hello, world. You're at the api001 index.
完善
前面的工作只是为了学习配置View和URl,现在正式配置View和URL。代码如下。
views.py1
2
3
4
5
6
7
8
9
10
11
12
13
14
15from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
return HttpResponse("Hello, world. You're at the trips index.")
def tripList(request):
return HttpResponse("This is the view for tripList")
def tripDetail(request,trip_id):
return HttpResponse("This is the view for tripDetail %s." % trip_id)
def tripDayDetail(request,trip_id,day_id):
return HttpResponse("This is the view for tripDayDetail %s-%s." % (trip_id,day_id))
urls.py1
2
3
4
5
6
7
8
9
10
11
12
13
14from django.conf.urls import url
from . import views
urlpatterns = [
# ex: /api001/
url(r'^$', views.index, name='index'),
# ex: /api001/trips/
url(r'^trips/$', views.tripList, name='tripList'),
# ex: /api001/trips/2
url(r'^trips/(?P<trip_id>[0-9]+)/$', views.tripDetail, name='tripDetail'),
# ex: /api001/trips/2/2
url(r'^trips/(?P<trip_id>[0-9]+)/(?P<day_id>[0-9]+)/$', views.tripDayDetail, name='tripDayDetail'),
]
配置完成后可以通过目标里的三种url访问到三个页面并且传递相应的参数。接下来要以Json形式返回数据库中的数据。
返回Json数据
Django中可以使用Django REST framework来生成Json文件。不过自己生成也没什么难度。
Python自带Json函数库能够把字典数据格式化为Json数据,所以首先我们在model中写一个相应的方法,用来把model转换成字典。
以Trip Model为例
models.py1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27···
class Trip(models.Model):
id = models.AutoField(primary_key=True) #唯一ID,
trip_title = models. CharField(max_length=100) #标题,
trip_subtitle = models.CharField(max_length=100) #副标题,
cover = models.TextField() #封面照片名称
pub_time = models.DateTimeField('date published') #时间,
place = models.CharField(max_length=100) #地点,
likeCount = models.IntegerField(default=0) #赞的数量
def __str__(self):
return self.trip_title
def toDict(self):
#datatime时间不能直接发送,需要先转成时间戳再格式化为字典
tmstp = int(time.mktime(self.pub_time.timetuple()))
return {
'trip_title':self.trip_title,
'trip_subtitle':self.trip_subtitle,
'cover':self.cover,
'place':self.place,
'likeCount':self.likeCount,
'pub_time' : tmstp,
'day_Count' : self.day_set.count() #获取Trip对应的日程数量
}
···
urls.py1
2
3
4
5
6
7···
def tripList(request):
all_objs=Trip.objects.all()
all_dicts=toDicts(all_objs)
all_jsons=json.dumps(all_dicts)
return HttpResponse(all_jsons)
···
保存后再次访问http://xxx.xxx.xxx.xxx:8000/api001/trips
即可看到相应的Json数据。1
[{"pub_time": 1446283020, "trip_title": "trip_title", "cover": "notyet.png", "likeCount": 0, "dayCount": 2, "place": "I don't know", "trip_subtitle": "subtitle"}]
进阶
关联查询
第一个API接口完成,接下来的端口需要根据Trip ID获取具体Trip和包含日程信息。可以用一下代码来实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#为了防止命名重复,把import json改为import json as JSON
import json as JSON
···
def tripDetail(request,trip_id):
#根据ID获取相应的Trip数据
trip = Trip.objects.get(pk=trip_id)
#把Trip数据转换成字典
tripDic = trip.toDict()
#获取该Trip包含的日程信息
days = trip.day_set.all()
#把日程信息转换成字典里表并加到tripDic中的days字段
tripDic["days"] = toDicts(days)
#格式化输出tripDic文件为JSON字符串
jsonObj = JSON.dumps(tripDic)
return HttpResponse(jsonObj)
···
try-catch
有时候可能会请求一些不存在的数据,这种情况下需要做错误处理。Python自带的try/catch功能就能很好的处理这些错误。
我们首先定义一个函数来优化JSon结构,在models.py
增加函数1
2
3
4
5
6
7def toJson(status,info,dic):
jsonDic = {
'success' : status, #是否成功
'info' : info, #详细信息 - 失败时候提醒用
'data' : dic #数据
}
return JSON.dumps(jsonDic)
tripDetail方法改为1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18def tripDetail(request,trip_id):
json = ""
try:
#根据ID获取相应的Trip数据
trip = Trip.objects.get(pk=trip_id)
#把Trip数据转换成字典
tripDic = trip.toDict()
#获取该Trip包含的日程信息
days = trip.day_set.all()
#把日程信息转换成字典里表并加到tripDic中的days字段
tripDic["days"] = toDicts(days)
json = toJson(True,"Success",tripDic)
#如果没有数据,则返回空数据和错误信息
#更多详细错误类型参考https://docs.djangoproject.com/en/1.8/ref/exceptions/
except Trip.DoesNotExist:
json = toJson(False,"DoesNotExist",[])
return HttpResponse(json)
Admin Panel
Django自带的Admin panel不需要些几行代码就允许我们很方便的管理数据。
首先设置管理员帐号1
$ python manage.py createsuperuser
输入帐号密码邮箱即可。创建帐号后启动服务,打开http://xxx.xxx.xxx.xxx:8000/admin/
,然后刚刚创建的帐号登录。可以看到Groups和Users两组数据。
此时我们需要把Trip和Days数据引入到admin panel。编辑api001/admin.py
,增加一下语句1
2
3
4
5
6# 引入
from .models import Trip, Day
# 注册
admin.site.register(Trip)
admin.site.register(Day)
刷新Admin页面就可以看到Day和Trip数据,并且能够直接在网页进行删增编辑操作。关于Admin Panel可以查阅Writing your first Django app, part 2