在这个例子中,我们将使用Django编写饿了么高校外卖商家查询API接口,并且使用AJAX技术来实现API接口的使用,包括使用ajax get方法加载更多数据,使用ajax方法来更新、修改、新增、删除数据。利用API可以做到前后端分离,为开发web应用提供了便利。
安装rest framework
首先使用Pycharm新建一个Django项目,并且使用virtualenv或者pipenv虚拟环境

创建成功会自动安装Django2.1和所需依赖,restframework框架需要自己手动安装
- //激活虚拟环境安装以下
- (venv)$ pip install djangorestframework
- (venv)$ pip install django-filter
- (venv)$ pip install pytest
- (venv)$ pip install pytest-django
- //由于笔者使用Postgresql数据库,所以还需要安装以下
- (venv)$ pip install psycopg2
- //使用mysql数据库安装如下
- (venv)$ pip install pymysql
准备数据来提供服务
数据来源:饿了么爬虫
数据内容:全国所有大学附近的外卖商家Top20
数据需要导入数据库

Django编写rest api接口
项目结构

settings.py.
- // 安装的app如下
- INSTALLED_APPS = [
- #...
- 'rest_framework',
- 'django_filters',
- 'api.apps.ApiConfig',
- 'front.apps.FrontConfig',
- ]
- //restframework 配置如下
- REST_FRAMEWORK = {
- //这里配置了分页处理,每页最多20个项目
- 'DEFAULT_PAGINATION_CLASS':'api.custompagination.LimitOffsetPaginationWithUpperBound',
- 'PAGE_SIZE': 20,
- 'DEFAULT_FILTER_BACKENDS': (
- //这里配置了排序、过滤、搜索器
- 'django_filters.rest_framework.DjangoFilterBackend',
- 'rest_framework.filters.OrderingFilter',
- 'rest_framework.filters.SearchFilter',
- ),
- //这里配置了用户认证,管理员才可以更改内容,未登录不能更改
- 'DEFAULT_AUTHENTICATION_CLASSES':(
- 'rest_framework.authentication.BasicAuthentication',
- 'rest_framework.authentication.SessionAuthentication',
- ),
- //这里配置了访问次数限制,过多会返回429错误 too many requests
- 'DEFAULT_THROTTLE_CLASSES': (
- 'rest_framework.throttling.AnonRateThrottle',
- 'rest_framework.throttling.UserRateThrottle',
- ),
- //这里配置了访问次数,anon代表匿名用户,user代表已登录用户,entries是我自己设置的作用域,300/hour代表最多300次每小时
- 'DEFAULT_THROTTLE_RATES': {
- 'anon': '300/hour',
- 'user': '100/hour',
- 'entries': '200/hour',
- },
- 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.NamespaceVersioning',
- }
models.py.
- from django.db import models
- class Entry(models.Model):
- city = models.CharField(max_length=50)
- school = models.CharField(max_length=100)
- link = models.CharField(max_length=100,null=True,default='null')
- name = models.CharField(max_length=200)
- lat = models.CharField(max_length=20,null=True,default='0.0')
- lng = models.CharField(max_length=20,null=True,default='0.0')
- address = models.CharField(max_length=200,null=True,default='null')
- distance = models.CharField(max_length=20,null=True,default='0')
- time = models.CharField(max_length=20,null=True,default='0:00')
- contact = models.CharField(max_length=200,null=True,default='null')
- score = models.CharField(max_length=10,null=True,default='0')
- comments = models.CharField(max_length=20,null=True,default='0')
- sell = models.CharField(max_length=20,null=True,default='0')
- image = models.CharField(max_length=200,null=True,default='null')
- owner = models.ForeignKey('auth.User',related_name='entries',on_delete=models.CASCADE)
- # class Meta:
- # ordering = ('name',)
- def __str__(self):
- return self.name
serializers.py.
- from rest_framework import serializers
- from api.models import Entry
- //这里继承自超链接模型序列器,用于把数据转换为json格式,并且显示链接
- class EntrySerializer(serializers.HyperlinkedModelSerializer):
- owner = serializers.ReadOnlyField(source='owner.username')
- class Meta:
- model = Entry
- fields = ('url','pk','name','city','school','link','lat','lng','address','distance','time','contact',
- 'score','comments','sell','image','owner')
views.py.
- from rest_framework import generics
- from rest_framework.response import Response
- from rest_framework.reverse import reverse
- from api.models import Entry
- from api.serializers import EntrySerializer
- from rest_framework import permissions
- from rest_framework.permissions import IsAuthenticated
- from rest_framework.throttling import ScopedRateThrottle
- from api import custompermission
- //这里是获取所有数据,可实现HTTP get、Post、Option操作
- class EntryList(generics.ListCreateAPIView):
- //限流自定义作用域
- throttle_scope = 'entries'
- throttle_classes = (ScopedRateThrottle,)
- queryset = Entry.objects.all()
- serializer_class = EntrySerializer
- name = 'entry-list'
- filter_fields = ('city','school','name')
- search_fields = ('school','city')
- ordering_fields = ('city')
-
- //管理员才能post操作创建新的数据
- permission_classes = (
- permissions.IsAuthenticatedOrReadOnly,
- custompermission.IsCurrentUserOwnerOrReadOnly,
- )
- def perform_create(self, serializer):
- serializer.save(owner=self.request.user)
-
- //这里是获取具体某一项的数据,可实现HTTP GET、PUT、PATCH、Option操作
- class EntryDetail(generics.RetrieveUpdateDestroyAPIView):
- throttle_scope = 'entries'
- throttle_classes = (ScopedRateThrottle,)
- queryset = Entry.objects.all()
- serializer_class = EntrySerializer
- name = 'entry-detail'
- permission_classes = (
- permissions.IsAuthenticatedOrReadOnly,
- custompermission.IsCurrentUserOwnerOrReadOnly,
- )
- //api根目录
- class ApiRoot(generics.GenericAPIView):
- name = 'api-root'
- def get(self, request, *args, **kwargs):
- return Response({
- 'entries': reverse(EntryList.name, request=request),
- })
urls.py.
- from django.urls import path
- from api import views
- urlpatterns = [
- path('entries/', views.EntryList.as_view(), name=views.EntryList.name),
- path('entry-detail/<int:pk>', views.EntryDetail.as_view(), name=views.EntryDetail.name),
- path('', views.ApiRoot.as_view(), name=views.ApiRoot.name)
- ]
ele/urls.py.
- from django.urls import path,include
- urlpatterns = [
- path('v1/',include('api.urls')),
- path('v1/api-auth/',include('rest_framework.urls')),
- path('',include('front.urls'))
- ]
以下为启动界面


到此为止非常简单的api就写完了,接下来就是自动化测试是否达到预期效果。 如图,测试通过!

在程序中调用刚刚写好的api
创建一个新的app并且添加到settings.py里面
- (venv)$ python manage.py startapp front
做好的效果如下:点击加载更多会触发ajax


由于篇幅有限,这里贴出js代码 使用ajax get请求刚刚写好的api接口并且添加到表格中
myjs.js.
- $('#load-more').click(function () {
- $.ajax({
- method:'GET',
- url:api_url,
- dataType:'json',
- success:function (data) {
- api_url = data['next'];
- if (api_url == null){
- $('#load-more').val('已加载全部');
- $('#load-more').attr('disabled',true);
- //api_url这里就是刚刚写好的api接口
- api_url = 'v1/entries/';
- }
- var results = data['results'];
- for (i=0;i<results.length;i++){
- $('#ele-table-body').append(
- ' <tr>\n' +
- ' <th scope="col">'+results[i]['pk']+'</th>\n' +
- ' <th scope="col">'+results[i]['city']+'</th>\n' +
- ' <th scope="col"><a href="/detail/' + results[i]['pk'] +'" rel="external nofollow" >' + results[i]['name'] + '</a></th>\n' +
- ' <th scope="col">'+results[i]['school']+'</th>\n' +
- ' <th scope="col">'+results[i]['score']+'</th>\n' +
- ' </tr>'
- )
- }
- }
- })
- });
可以修改具体的一条数据,使用ajax patch方法提交数据。 注:PUT方法是修改所有数据,而PATCH方法是修改局部数据

myjs.js.
- $('#edit-confirm-btn').click(function () {
- var name = $('#name').val();
- var distance = $('#distance').val();
- var adderss = $('#address').val();
- var time = $('#time').val();
- var score = $('#score').val();
- var comments = $('#comments').val();
- var sell = $('#sell').val();
- var pk = $('#pk').val();
- $.ajax({
- type:'PATCH',
- url:'/detail/' + pk,
- data:{
- "name": name,
- "distance": distance,
- "address": adderss,
- "time": time,
- "score": score,
- "comments": comments,
- "sell": sell,
- },
- success:function (data) {
- if (data.status == 'ok'){
- console.log('success');
- location.reload();
- }
- }
- })
- })
本文通过一个小例子介绍了如何使用Django调用自己写的api
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。