django restful mysql,Django RESTful API搭建及常见问题

Django中内置的有一些Json数据的处理和返回方案

myModels = MyModel.objects.all()

# 1.model直接转换为dict

from django.forms.models import model_to_dict

for myModel in myModels:

json_dict = model_to_dict(myModel)

json_list.append(json_dict)

# 2. 直接序列化并通过JsonResponse和HttpResponse返回

from django.core import serializers

import json

json_data = serializers.serialize('json', myModels)

json_data = json.loads(json_data)

from django.http import HttpResponse, JsonResponse

# return HttpResponse(json.dumps(json_data), content_type="application/json")

return JsonResponse(json_data, safe=False)

# 注:在工作中出现调用系统外的SQL Server来获取数据,通过以下方式可返回Json

cur = cursor.fetchall()

from django.core.serializers.json import DjangoJSONEncoder

json_data = json.dumps(cur, cls=DjangoJSONEncoder)

return HttpResponse(json_data, content_type="application/json")

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

myModels=MyModel.objects.all()

# 1.model直接转换为dict

fromdjango.forms.modelsimportmodel_to_dict

formyModelinmyModels:

json_dict=model_to_dict(myModel)

json_list.append(json_dict)

# 2. 直接序列化并通过JsonResponse和HttpResponse返回

fromdjango.coreimportserializers

importjson

json_data=serializers.serialize('json',myModels)

json_data=json.loads(json_data)

fromdjango.httpimportHttpResponse,JsonResponse

# return HttpResponse(json.dumps(json_data), content_type="application/json")

returnJsonResponse(json_data,safe=False)

# 注:在工作中出现调用系统外的SQL Server来获取数据,通过以下方式可返回Json

cur=cursor.fetchall()

fromdjango.core.serializers.jsonimportDjangoJSONEncoder

json_data=json.dumps(cur,cls=DjangoJSONEncoder)

returnHttpResponse(json_data,content_type="application/json")

虽然看似以上方案也可以打造我们的API,但更为灵活的Rest API方案还是应该要采用Django REST framework

安装

除djangorestframework, markdown, django-filter外,建议同时安装coreapi,django-guardian, 环境搭建参照Django环境搭建及开发

mkvirtualenv -p /usr/local/bin/python3 myproject

pip install django djangorestframework markdown django-filter mysqlclient pillow

# urls.py

from rest_framework.documentation import include_docs_urls

url(r'docs/', include_docs_urls(title="")),

url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),

1

2

3

4

5

6

mkvirtualenv-p/usr/local/bin/python3myproject

pipinstalldjangodjangorestframeworkmarkdowndjango-filtermysqlclientpillow

# urls.py

fromrest_framework.documentationimportinclude_docs_urls

url(r'docs/',include_docs_urls(title="")),

url(r'^api-auth/',include('rest_framework.urls',namespace='rest_framework')),

Serializer

使用步骤一、在对应App下新建serializers.py

from rest_framework import serializers

# 方法一、指定字段

class ModelNameSerializer(serializers.Serializer):

name = serializers.CharField(max_length=100, required=True)

num = serializers.IntegerField(default=0)

# 方法二、ModelSerializer(更推荐),其中__all__表示输出所有字段,也可通过传递数组/列表来指定字段

from .models import ModelName

class ModelNameSerializer(serializers.ModelSerializer):

class Meta:

model = ModelName

fields = "__all__"

# 扩展,如果model拥有外键,那么实际只会输出相应的id,要输出其中的内容,可以新建另一个Serializer来实现内嵌输出,如上述更改为:

from .models import ModelName, Model2Name

class Model2NameSerializer(serializers.ModelSerializer):

class Meta:

model = Model2Name

fields = "__all__"

class ModelNameSerializer(serializers.ModelSerializer):

fieldName = Model2NameSerializer()

class Meta:

model = ModelName

fields = "__all__"

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

fromrest_frameworkimportserializers

# 方法一、指定字段

classModelNameSerializer(serializers.Serializer):

name=serializers.CharField(max_length=100,required=True)

num=serializers.IntegerField(default=0)

# 方法二、ModelSerializer(更推荐),其中__all__表示输出所有字段,也可通过传递数组/列表来指定字段

from.modelsimportModelName

classModelNameSerializer(serializers.ModelSerializer):

classMeta:

model=ModelName

fields="__all__"

# 扩展,如果model拥有外键,那么实际只会输出相应的id,要输出其中的内容,可以新建另一个Serializer来实现内嵌输出,如上述更改为:

from.modelsimportModelName,Model2Name

classModel2NameSerializer(serializers.ModelSerializer):

classMeta:

model=Model2Name

fields="__all__"

classModelNameSerializer(serializers.ModelSerializer):

fieldName=Model2NameSerializer()

classMeta:

model=ModelName

fields="__all__"

使用步骤二、Views.py

# 方法一

from .models import ModelName

from .serializers import ModelNameSerializer

from rest_framework.views import APIView

from rest_framework.response import Response

class ModelNameList(APIView):

"""

Your description

"""

def get(self, request, format=None):

model_name = ModelName.objects.all()

serializer = ModelNameSerializer(modelName, many=True)

return Response(serializer.data)

# 方法二

from .models import ModelName

from .serializers import ModelNameSerializer

from rest_framework import mixins, generics

class ModelNameList(mixins.ListModelMixin, generics.GenericAPIView):

"""

Your description

"""

queryset = ModelName.objects.all()

serializer_class = ModelNameSerializer

def get(self, request, *args, **kwargs):

return self.list(request, *args, **kwargs)

# 方法三

from .models import ModelName

from .serializers import ModelNameSerializer

from rest_framework import generics

class ModelNameList(generics.ListCreateAPIView):

"""

Your description

"""

queryset = ModelName.objects.all()

serializer_class = ModelNameSerializer

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

40

41

42

# 方法一

from.modelsimportModelName

from.serializersimportModelNameSerializer

fromrest_framework.viewsimportAPIView

fromrest_framework.responseimportResponse

classModelNameList(APIView):

"""

Your description

"""

defget(self,request,format=None):

model_name=ModelName.objects.all()

serializer=ModelNameSerializer(modelName,many=True)

returnResponse(serializer.data)

# 方法二

from.modelsimportModelName

from.serializersimportModelNameSerializer

fromrest_frameworkimportmixins,generics

classModelNameList(mixins.ListModelMixin,generics.GenericAPIView):

"""

Your description

"""

queryset=ModelName.objects.all()

serializer_class=ModelNameSerializer

defget(self,request,*args,**kwargs):

returnself.list(request,*args,**kwargs)

# 方法三

from.modelsimportModelName

from.serializersimportModelNameSerializer

fromrest_frameworkimportgenerics

classModelNameList(generics.ListCreateAPIView):

"""

Your description

"""

queryset=ModelName.objects.all()

serializer_class=ModelNameSerializer

分页

# 配置方法一(settings.py)

REST_FRAMEWORK = {

'PAGE_SIZE': 10

}

# 配置方法二(Views.py)

from rest_framework.pagination import PageNumberPagination

class ModelNamePagination(PageNumberPagination):

page_size = 10

page_size_query_param = 'page_size'

max_page_size = 100

page_query_param = 'p'

class ModelNameList(generics.ListCreateAPIView):

...

pagination_class = ModelNamePagination

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

# 配置方法一(settings.py)

REST_FRAMEWORK={

'PAGE_SIZE':10

}

# 配置方法二(Views.py)

fromrest_framework.paginationimportPageNumberPagination

classModelNamePagination(PageNumberPagination):

page_size=10

page_size_query_param='page_size'

max_page_size=100

page_query_param='p'

classModelNameList(generics.ListCreateAPIView):

...

pagination_class=ModelNamePagination

ViewSet

用法一

# Views.py

from rest_framework import viewsets, mixins

# 习惯性地将上述ModelNameList修改为ModelNameListViewSet

class ModelNameListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):

# urls.py用法一

modelname_list = ModelNameViewSet.as_view({

'get': 'list',

})

urlpatterns = [

url(r'modelname/$', modelname_list, name="goods_list"),

]

# urls.py用法二(更推荐)

from rest_framework.routers import DefaultRouter

router = DefaultRouter()

router.register(r'modelname', ModelNameListViewSet)

urlpatterns = [

url(r'^', include(router.urls)),

]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

# Views.py

fromrest_frameworkimportviewsets,mixins

# 习惯性地将上述ModelNameList修改为ModelNameListViewSet

classModelNameListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):

# urls.py用法一

modelname_list=ModelNameViewSet.as_view({

'get':'list',

})

urlpatterns=[

url(r'modelname/$',modelname_list,name="goods_list"),

]

# urls.py用法二(更推荐)

fromrest_framework.routersimportDefaultRouter

router=DefaultRouter()

router.register(r'modelname',ModelNameListViewSet)

urlpatterns=[

url(r'^',include(router.urls)),

]

GenericViewSet

GenericAPIView

APIView

View

mixin

CreateModelMixin

ListModelMixin

RetrieveModelMixin

UpdateModelMixin

DestroyModelMixin

1

2

3

4

5

6

7

8

9

10

11

GenericViewSet

GenericAPIView

APIView

View

mixin

CreateModelMixin

ListModelMixin

RetrieveModelMixin

UpdateModelMixin

DestroyModelMixin

过滤、搜索、排序

# 方法一、在ViewSet中定义如下方法

def get_queryset(self):

...

# 方法二、

# 注意:这里过滤使用的是django-filter,而搜索、排序则使用的是DRF自身的filters

from django_filters.rest_framework import DjangoFilterBackend

from rest_framework import filters

filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)

filter_fields = ('field_name1', 'field_name2')

search_fields = ('field_name1', 'field_name2')

ordering_fields = ('field_name1',)

# 注:在Django的配置中使用tuple如果只有一个元素建议在最后加一个逗号,否则会导致配置失效,因为不加逗号会将其视为字符串类型

# 升级方案,创建filters.py

import django_filters

from .models import Product

class ProductFilter(django_filters.rest_framework.FilterSet):

# django-filter2.0之后,以下 name 需修改为 field_name

min_price = django_filters.NumberFilter(name="price", lookup_expr='gte')

max_price = django_filters.NumberFilter(name="price", lookup_expr='lte')

class Meta:

model = Product

fields = ['min_price', 'max_price']

# 在ViewSet中添加

filter_class = ProductFilter

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

# 方法一、在ViewSet中定义如下方法

defget_queryset(self):

...

# 方法二、

# 注意:这里过滤使用的是django-filter,而搜索、排序则使用的是DRF自身的filters

fromdjango_filters.rest_frameworkimportDjangoFilterBackend

fromrest_frameworkimportfilters

filter_backends=(DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)

filter_fields=('field_name1','field_name2')

search_fields=('field_name1','field_name2')

ordering_fields=('field_name1',)

# 注:在Django的配置中使用tuple如果只有一个元素建议在最后加一个逗号,否则会导致配置失效,因为不加逗号会将其视为字符串类型

# 升级方案,创建filters.py

importdjango_filters

from.modelsimportProduct

classProductFilter(django_filters.rest_framework.FilterSet):

# django-filter2.0之后,以下 name  需修改为 field_name

min_price=django_filters.NumberFilter(name="price",lookup_expr='gte')

max_price=django_filters.NumberFilter(name="price",lookup_expr='lte')

classMeta:

model=Product

fields=['min_price','max_price']

# 在ViewSet中添加

filter_class=ProductFilter

跨域访问

API通常都是由不同域名来进行调用,此将会出现如下报错 Failed to load resource: the server responded with a status of 504 (Gateway Timeout) Django的解决方案如下

# 1.安装django-cors-headers

pip install django-cors-headers

# 2.settings.py

MIDDLEWARE = [

'corsheaders.middleware.CorsMiddleware',

#请放在CsrfViewMiddleware之前

...

]

CORS_ORIGIN_ALLOW_ALL = True

# 或通过CORS_ORIGIN_WHITELIST设置白名单

1

2

3

4

5

6

7

8

9

10

11

# 1.安装django-cors-headers

pipinstalldjango-cors-headers

# 2.settings.py

MIDDLEWARE=[

'corsheaders.middleware.CorsMiddleware',

#请放在CsrfViewMiddleware之前

...

]

CORS_ORIGIN_ALLOW_ALL=True

# 或通过CORS_ORIGIN_WHITELIST设置白名单

权限认证

Token配置

# settings.py

INSTALLED_APPS = (

...

'rest_framework.authtoken'

)

REST_FRAMEWORK = {

'DEFAULT_AUTHENTICATION_CLASSES': (

'rest_framework.authentication.BasicAuthentication',

'rest_framework.authentication.SessionAuthentication',

'rest_framework.authentication.TokenAuthentication',

)

}

# urls.py

from rest_framework.authtoken import views

urlpatterns += [

url(r'^api-token-auth/', views.obtain_auth_token)

]

# 执行makemigrations 和 migrate生成Token相关数据表

# View内Token认证,注释DEFAULT_AUTHENTICATION_CLASSES中的Token部分,添加

authentication_classes = (TokenAuthentication,)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

# settings.py

INSTALLED_APPS=(

...

'rest_framework.authtoken'

)

REST_FRAMEWORK={

'DEFAULT_AUTHENTICATION_CLASSES':(

'rest_framework.authentication.BasicAuthentication',

'rest_framework.authentication.SessionAuthentication',

'rest_framework.authentication.TokenAuthentication',

)

}

# urls.py

fromrest_framework.authtokenimportviews

urlpatterns+=[

url(r'^api-token-auth/',views.obtain_auth_token)

]

# 执行makemigrations 和 migrate生成Token相关数据表

# View内Token认证,注释DEFAULT_AUTHENTICATION_CLASSES中的Token部分,添加

authentication_classes=(TokenAuthentication,)

django-restful-api

请求时的header为Authorization: Token 虽然Django REST Framework(DRF)的Token非常强大,但存在着无有效期、一旦泄露则面临风险,因此有人根据JWT规范开发一个REST framework JWT的认证方式,安装配置也很简单

pip install djangorestframework-jwt

# settings.py

REST_FRAMEWORK = {

'DEFAULT_AUTHENTICATION_CLASSES': (

...

'rest_framework_jwt.authentication.JSONWebTokenAuthentication',

)

}

import datetime

JWT_AUTH = {

'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),

'JWT_AUTH_HEADER_PREFIX': 'JWT',

}

# urls.py

from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [

...

url(r'^api-token-auth/', obtain_jwt_token),

]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

pipinstalldjangorestframework-jwt

# settings.py

REST_FRAMEWORK={

'DEFAULT_AUTHENTICATION_CLASSES':(

...

'rest_framework_jwt.authentication.JSONWebTokenAuthentication',

)

}

importdatetime

JWT_AUTH={

'JWT_REFRESH_EXPIRATION_DELTA':datetime.timedelta(days=7),

'JWT_AUTH_HEADER_PREFIX':'JWT',

}

# urls.py

fromrest_framework_jwt.viewsimportobtain_jwt_token

urlpatterns=[

...

url(r'^api-token-auth/',obtain_jwt_token),

]

请求时的header为Authorization: JWT

权限控制

AllowAny

IsAuthenticated

IsAdminUser

IsAuthenticatedOrReadOnly

DjangoModelPermissions

DjangoModelPermissionsOrAnonReadOnly

DjangoObjectPermissions API暴露太多权限(POST, PUT)时需谨慎

#官方文档中给出的示例是这样的

class XxxViewSet(viewsets.ModelViewSet):

#只需将ModelViewSet修改成ReadOnlyModelViewSet即可变为只读

class XxxViewSet(viewsets.ReadOnlyModelViewSet):

1

2

3

4

#官方文档中给出的示例是这样的

classXxxViewSet(viewsets.ModelViewSet):

#只需将ModelViewSet修改成ReadOnlyModelViewSet即可变为只读

classXxxViewSet(viewsets.ReadOnlyModelViewSet):

常见问题

1.Cannot apply DjangoModelPermissions on a view that does not set .queryset or have a .get_queryset() method. 很明显这是Django Rest Framework的权限导致某些接口页面无法访问,所以自然而然地大多数人会告诉你进行如下修改

'DEFAULT_PERMISSION_CLASSES': [

'rest_framework.permissions.AllowAny'

],

1

2

3

'DEFAULT_PERMISSION_CLASSES':[

'rest_framework.permissions.AllowAny'

],

但这样做会对全局产生影响,建议通过装饰器仅对相应的类或方法进行这一控制

from rest_framework import permissions

from rest_framework.decorators import permission_classes

@permission_classes((permissions.AllowAny,))

1

2

3

fromrest_frameworkimportpermissions

fromrest_framework.decoratorsimportpermission_classes

@permission_classes((permissions.AllowAny,))

from rest_framework.permissions import IsAuthenticated

from rest_framework.views import APIView

class ExampleView(APIView):

permission_classes = (AllowAny,)

[php]

 

<strong><span style="color: #0000ff;">2.</span></strong>Pymssql

通过pymssql获取SQL Server中的数据会出现Decimal, datetime.datetime等无法序列化的情况

[php]

TypeError(repr(o) + " is not JSON serializable")

TypeError: datetime.datetime(2017, 4, 2, 17, 9, 40) is not JSON serializable

1

2

3

4

5

6

7

8

9

10

11

12

13

14

fromrest_framework.permissionsimportIsAuthenticated

fromrest_framework.viewsimportAPIView

classExampleView(APIView):

permission_classes=(AllowAny,)

[php]

&nbsp;

<strong><spanstyle="color: #0000ff;">2.</span></strong>Pymssql

通过pymssql获取SQLServer中的数据会出现Decimal,datetime.datetime等无法序列化的情况

[php]

TypeError(repr(o)+" is not JSON serializable")

TypeError:datetime.datetime(2017,4,2,17,9,40)isnotJSONserializable

添加DjangoJSONEncoder来解决这一问题

from django.core.serializers.json import DjangoJSONEncoder

json.dumps(yourdata, cls=DjangoJSONEncoder)

1

2

fromdjango.core.serializers.jsonimportDjangoJSONEncoder

json.dumps(yourdata,cls=DjangoJSONEncoder)

3.IntegrityError at /api/***/ (1048, “Column ‘***’ cannot be null”) 这个似乎出现在操作包含外链的Model时,将POST的数据插入到数据库中,一种解决方法是: 将Serializer中的HyperlinkedModelSerializer修改为ModelSerializer 4.Cannot filter a query once a slice has been taken.

django-restful-api 在写api时只想暴露几条记录,获取单条记录详细信息时就出现了上面的错误,只针对调用的话在url后加上?limit=3这样的限制,但如果不只想在调用时进行限制呢?一种方法是添加字段用于过滤,还有就是下面的方法可供参考

# 原语句

queryset = Articles.objects.all().order_by('-pub_date')[:3]

# 改正后的语句

count = Articles.objects.all().count()

queryset = Articles.objects.filter(id__range=(count-2, count)).order_by('-pub_date')

1

2

3

4

5

# 原语句

queryset=Articles.objects.all().order_by('-pub_date')[:3]

# 改正后的语句

count=Articles.objects.all().count()

queryset=Articles.objects.filter(id__range=(count-2,count)).order_by('-pub_date')

5. TemplateDoesNotExist at /xxxx/ django_filters/rest_framework/crispy_form.html 出现这一报错是由于没有在settings.py的INSTALLED_APPS中添加django_filters

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值