開始之前,假設(shè)你已經(jīng)有Django和Django REST framework的一些基礎(chǔ)了
mixins,ViewSet和routers配合使用
minxis的類有5種
CreateModelMixinListModelMixinRetrieveModelMixinUpdateModelMixinDestroyModelMixin
他們分別對應(yīng)了對數(shù)據(jù)庫的增查改刪操作,使用它們的好處是不用重復(fù)寫著相同的業(yè)務(wù)代碼邏輯,因為每個mixins內(nèi)部都寫好了對應(yīng)的邏輯,只需要設(shè)置一下queryset和serializer_class就可以了.
ViewSet也有5種,分別是
ViewSetMixinViewSetGenericViewSetReadOnlyModelViewSetModelViewSet
一般來說我們只需要用GenericViewSet就可以了.它繼承了ViewSetMixin和generics.GenericAPIView,后者的功能大家都知道,有了它才能設(shè)置queryset和serializer_class屬性.重點是ViewSetMixin.
它重寫了方法
as_view,這個能讓我們注冊url變得更加簡單,還有一個方法是initialize_request,這個方法主要是給action屬性賦值,這個屬性在設(shè)置動態(tài)serializer和permission的時候有很大的用處!之后會寫到
所以寫一個APIView就變得很簡單,如下:
from rest_framework import mixins
from rest_framework import viewsets
class XXXViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
queryset = Model.objects.all()
serializer_class = ModelSerializer
接下來就是配置url了
from appname.views import XXXViewSet
models = XXXViewSet.asview({
'get': 'list',
'post': 'create'
})
urlpattrtns = [
url(r'apiAddress/$',models, name="models"),
]
這樣就可以把get請求綁定到list的方法上,post請求就綁定到了create方法.不需要再去重寫它們了
其實上面配置url的方法還是過于繁瑣,這時候就是router登場了,上面的代碼改為:
from rest_framework.routers import DefaultRouter
from appname.views import XXXViewSet
router = DefaultRouter()
router.register(r'apiAddress', XXXViewSet, base_name='apiAddress')
urlpattrtns = [
# 這個已經(jīng)不需要了
# url(r'apiAddress/$',models, name="models"),
url(r'^', include(router.urls)),
]
以后再添加url的時候只需要在router里面注冊就行了,urlpattrtns列表不需要做任何改動.
這樣就完成了一個RESTful API的創(chuàng)建, 能夠合理搭配mixins,ViewSet和routers三者的話,就可以超快速地開發(fā)大量的RESTful API!
使用Django REST framework 的過濾功能
一個最簡單的過濾功能, 例如查詢用戶列表,只返回用戶粉絲數(shù)大于100的:
class XXXViewSet(mixins.ListModelMixin, mixins.CreateModelMixin,viewsets.GenericViewSet):
serializer_class = ModelSerializer
def get_queryset(self):
fans_min = self.reuqest.query_params.get("fans_min", 0)
if fans_min:
return User.objects.filter(fans_num__gt=int(fans_min))
return User.objects.all()
上面這種方法如果要過濾的字段多的話,就要寫大量繁瑣的業(yè)務(wù)邏輯代碼
如果想以少量的代碼實現(xiàn)功能強大的過濾要用其他方案了,就要使用django-filter來完成.
首先 pip install django-filter, 然后把
django-filter加到INSTALLED_APPS列表中
代碼實現(xiàn):
from rest_framework import mixins
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from django.contrib.auth import get_user_model
User = get_user_model()
class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
queryset = User.objects.all()
serializer_class = ModelSerializer
filter_backends = (DjangoFilterBackend,)
# 設(shè)置過濾字段,這里設(shè)置過濾用戶的名字和粉絲數(shù)
filter_fields = ('name', 'fans_num')
然后使用瀏覽器打開url,
就會發(fā)現(xiàn)頁面多了一個過濾器,點開之后輸入信息就可以使用過濾功能了.
這種方式還是用局限性,比如用戶名想用模糊搜索,或者想要查詢粉絲數(shù)大于100小于200的用戶,這種方式是做不到的.這時候可以使用自定義
filter來實現(xiàn)!
新建filter.py
import django_filters
from django.contrib.auth import get_user_model
User = get_user_model()
class UserFilter(django_filters.rest_framework.FilterSet):
min_fans_num = django_filter.NumberFilter(name='fans_num', lookup_expr='gte')
max_fans_num = django_filters.NumberFilter(name='fans_num', lookup_expr='lt')
name = django_filters.CharFilter(name='name',lookup_expr='icontains')
class Meta:
model = User
fields = ['name', 'min_fans_num', 'max_fans_num']
然后之前的代碼改為:
from .filter import UserFilter
class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
queryset = User.objects.all()
serializer_class = ModelSerializer
filter_backends = (DjangoFilterBackend,)
filter_class = UserFilter
過濾用戶名的話,其實用SearchFilter也可以實現(xiàn),用兩行代碼就完成了
filter_backends = (DjangoFilterBackend, SearchFilter)
search_fields = ('name',)
還有一些更加強大的配置
The search behavior may be restricted by prepending various characters to the
search_fields.'^' Starts-with search.
'=' Exact matches.
'@' Full-text search. (Currently only supported Django's MySQL backend.)
'$' Regex search.
For example:
search_fields = ('=username', '=email')
還有一個排序的filter,例如我們想按照用戶的粉絲數(shù)量進行排序(升序和降序):
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
ordering_fields = ('fans_num',)
這樣已經(jīng)完成了...如果想要做更加復(fù)雜的過濾,可以查看django-filter的文檔
自定義分頁
from rest_framework.pagination import PageNumberPagination
class UsersPagination(PageNumberPagination):
# 指定每一頁的個數(shù)
page_size = 10
# 可以讓前端來設(shè)置page_szie參數(shù)來指定每頁個數(shù)
page_size_query_param = 'page_size'
# 設(shè)置頁碼的參數(shù)
page_query_param = 'page'
class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
# 設(shè)置分頁的class
pagination_class = UsersPagination
就這樣幾行代碼就搞定了,而且在返回的json中加了總數(shù),下一頁的鏈接和上一頁的鏈接.
回顧我們之前的代碼,在UserViewSet這個類里面,才寫了7行代碼,就已經(jīng)完成了
- 獲取用戶列表
- create一個用戶
- 分頁
- 搜索
- 顧慮
- 排序
這些功能,如果想要獲取指定用戶的具體信息,直接繼承mixins.RetrieveModelMixin就直接做好了...''
權(quán)限認(rèn)證
比如有一些API功能,是需要用戶登錄才能使用可以的
或者比如我要刪除我這篇博客,也要驗證我是作者才能刪除
驗證用戶是否登錄
from rest_framework.permissions import IsAuthenticated
class XXXViewSet(mixins.CreateModelMixin, mixins.DestroyModelMixin):
permission_classes = (IsAuthenticated,)
驗證操作是本人,需要自定義persssion
permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, object):
if request.method in permissions.SAFE_METHODS:
return True
return object.user == request.user
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
使用JWT的用戶認(rèn)證模式
第一步: pip install djangorestframework-jwt
第二步: 在url.py中配置
from rest_framework_jwt.views import obtain_jwt_token urlpatterns = [ ... url(r'^api-token-auth/', obtain_jwt_token), ... ]第三步: 在需要jwt認(rèn)證的ViewSet的類里面設(shè)置
from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.authentication import SessionAuthentication class XXXViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet): authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
加上SessionAuthentication是為了在網(wǎng)站上調(diào)試方便
現(xiàn)在注冊登錄有兩種方式:
- 用戶注冊之后跳轉(zhuǎn)到登錄頁面讓其登錄
- 用戶注冊之后自動幫他登錄了
第一種情況的話我們無需再做其他操作,第二種情況我們應(yīng)該在用戶注冊之后返回jwt token的字段給前臺,所以要做兩步:
- 因為返回字段是
mixins幫我們做好了,所以我們要重寫對應(yīng)的方法來修改返回字段 - 需要查看
djangorestframework-jwt的源碼找到生成jwt token的方法
from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler
class UserViewSet(CreateModelMixin, RetrieveModelMixin,UpdateModelMixin,viewsets.GenericViewSet):
# 重寫create方法
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = self.perform_create(serializer)
# 在新建用戶保存到數(shù)據(jù)庫之后
tmp_dict = serializer.data
# 生成JWT Token
payload = jwt_payload_handler(user)
tmp_dict['token'] = jwt_encode_handler(payload)
headers = self.get_success_headers(serializer.data)
return Response(tmp_dict, status=status.HTTP_201_CREATED, headers=headers)
更多jwt的相關(guān)操作可以查看文檔
動態(tài)serializers
這個使用之前說過的action屬性就可以很方便的實現(xiàn)
class UserViewSet(CreateModelMixin, RetrieveModelMixin,UpdateModelMixin,viewsets.GenericViewSet):
# 這個就不需要了
#serializer_class = XXXSerializer
def get_serializer_class(self):
if self.action == 'create':
return XXXSerializer
elif self.action == 'list':
return XXXSerializer
return XXXSerializer
一些實用的Serializer fields
比如說我要發(fā)布這篇文章,需要上傳我(用戶)的id才能和這篇文章建立關(guān)聯(lián),但我們這個可以不用前臺來上傳
serializer.py
class XXXSerializer(serializers.ModelSerializer):
# user默認(rèn)是當(dāng)前登錄的user
user = serializers.HiddenField(
default = serializers.CurrentUserDefault()
)
還有如果返回的字段邏輯比較復(fù)雜,可以用serializer.SerializerMethodField()來完成,例如:
class XXXSerializer(serializers.ModelSerializer):
xxx = serializer.SerializerMethodField()
# 把邏輯寫在get_的前綴加xxx(字段名),然后返回
def get_xxx(self, obj):
# 完成你的業(yè)務(wù)邏輯
return
自定義用戶認(rèn)證
Django自帶的登錄是通過username和password來做登錄的,但是現(xiàn)在很多網(wǎng)站或者app用手機號來來當(dāng)做賬號,這個時候就需要自定義用戶認(rèn)證:
from django.contrib.auth.backends import ModelBackend
class CustomBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
user = User.objects.get(Q(username=username)|Q(mobile=username))
# 驗證密碼是否正確
if user.check_password():
return user
except Exception as e:
return None
用戶注冊的時候,如果你在后臺查看的是明文,這是因為ModelSerializer在保存的時候直接明文保存了, 解決問題:
serializer.py
class UserRegSerializer(serializers.ModelSerializer):
#重寫create方法
def create():
user = super(UserRegSerializer,self).create(validated_data=validated_data)
user.set_password(validated_data["password"])
user.save()
return user
或者也可以用django的信號量也可以解決
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
User = get_user_model()
@receiver(post_save, sender=User)
def create_user(sender, instance=None, created=False, **kwargs):
if created:
password = instance.password
instance.set_password(password)
instance.save()
然后還要app.py里面配置
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
總結(jié):
人生苦短,我用Python!