最近學習了REST方面的知識,瞭解了REST 的基礎、API 的設計規則等等。
在Django中,不需要自己去設計每一個API,因為djangorestframwork幫我們做了一些工作。其實設計Django REST API的框架不少,但是djangorestframwork風格更像Django,與django的整合度更高,更易上手。該框架分為model, serializer, views三層,支援許可權許可等功能。
現在就邊做邊學習這方面的知識。 首先是準備工作: 1、安裝djangorestframwork pip install djangorestframework;
2、安裝django-filter;
配置:
- 在INSTALLED_APPS中新增: ‘rest_framwork’
- 設定 REST_FRAMEWORK
12345678910REST_FRAMEWORK = {# Use Django's standard <code>django.contrib.auth</code> permissions,# or allow read-only access for unauthenticated users.'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',],'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination','PAGE_SIZE':2,'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',)}
在上面設定了許可權,設定了結果分頁,設定了過濾器。
上面配置完成,開始寫程式碼。
一、Model就是資料庫定義的模型本身 Model有article,user,category三個
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 |
class Category(models.Model): name = models.CharField(max_length=150,unique=True,verbose_name=u'類名') alias = models.CharField(max_length=150,verbose_name=u'英文名稱') status = models.IntegerField(default=0,choices=STATUS.items(),verbose_name=u'狀態') #Automatically set the field to now when the object is first created. create_time = models.DateTimeField(auto_now_add=True) parent = models.ForeignKey('self',default=None,blank=True,null=True,verbose_name=u'上級分類') class Article(models.Model): title = models.CharField(max_length=150,unique=True,verbose_name=u'標題') alias = models.CharField(max_length=150,verbose_name=u'英文標題') content = models.TextField(verbose_name=u'正文') #when editing article,content_html will be saved automatically from content.so it can be blank content_html = models.TextField(blank=True,verbose_name=u'正文html格式') abstract = models.TextField(blank=True,verbose_name=u'摘要') read_times = models.IntegerField(default=0,verbose_name=u'閱讀次數') tags = models.CharField(max_length=100,verbose_name=u'標籤',help_text='用逗號隔開') status = models.IntegerField(default=0,choices=STATUS.items(),verbose_name=u'文章狀態') #Automatically set the field to now when the object is first created create_time = models.DateTimeField(auto_now_add=True) pub_time = models.DateTimeField(default=datetime.now()) #Automatically set the field to now every time the object is saved. update_time = models.DateTimeField(auto_now=True) author = models.ForeignKey(User,verbose_name=u'作者') category = models.ForeignKey(Category,verbose_name=u'分類') access = models.IntegerField(default=100,choices=ACCESS.items(),verbose_name=u'文章許可權,公開或者私人可見') |
User Model是Django自帶的Model。
二、序列化Serializer
摘自官網: Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
The serializers in REST framework work very similarly to Django’s Form and ModelForm classes. We provide a Serializer class which gives you a powerful, generic way to control the output of your responses, as well as a ModelSerializer class which provides a useful shortcut for creating serializers that deal with model instances and querysets.
從以上文字可以看出,serializer的目的是將查詢集或模型例項轉化為Python資料型別,從而很方便得轉變為Json,XML等多種格式。
我的專案中的Serializer設計:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
from rest_framework import serializers from .models import Category,Article from django.contrib.auth.models import User from django.forms import widgets class CategorySerializer(serializers.ModelSerializer): # Serializers define the API representation. class Meta: model = Category fields = ('id','name','alias','parent','create_time','status') class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ('username','email','last_login') class ArticleSerializer(serializers.ModelSerializer): category = CategorySerializer() author = UserSerializer() class Meta: model = Article fields = ('title','content','author','category','pub_time') |
在上面每個序列器都指定了Model,這樣會自動生成序列化欄位,這樣比較方便。 在ArticleSerializer中,加入了author和category的序列化,這樣做是因為當我們瀏覽article的資訊時。會直接顯示author和category的具體資訊,否則就只是author和category的id。
三、View:
主要使用viewset。和route ViewSet為我們提供了預設的URL結構, 使得我們能更專注於API本身。而Route能夠輕鬆的幫我們實現URL和ViewSet之間的關聯。 原始碼:
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 |
views.py class CategoryViewset(viewsets.ModelViewSet): """ This viewset automatically provides `list`, `create`, `retrieve`, `update` and `destroy` actions. Additionally we also provide an extra `highlight` action. """ queryset = Category.objects.all() serializer_class = CategorySerializer def perform_create(self, serializer): serializer.save() class UserViewset(viewsets.ModelViewSet): queryset = User.objects.all() permission_classes = (IsAdminUser,) serializer_class = UserSerializer class ArticleViewset(viewsets.ModelViewSet): """ the viewset let us can get data from api url like:curl - H 'application/json;indent=4' http://localhost:8080/api/articles/ of course,we can get some author's article through: http://localhost:8080/api/articles/?author=2(author's id) Generally,people like to see json data,so when visit by browser,you should add ?format=json ,that is,http://localhost:8080/api/articles/?format=json """ queryset = Article.objects.all() serializer_class = ArticleSerializer filter_backends = (filters.DjangoFilterBackend,) filter_fields = ('author','category','access','status') |
上面使用了ModelSet是有原因的,因為它自動提供delete,create,update,list等操作,即增刪該查等操作。
其次說下filter,一般都用預設的DjangoFilterBackend,此外rest-framework 提供了幾個原生的 filter:
SearchFilter
filter_backends = (filters.SearchFilter,) search_fields = (‘username’, ’email’) # 指定搜尋的域
請求 http://example.com/api/users?search=russell。
OrderingFilter
filter_backends = (filters.OrderingFilter,) ordering_fields = (‘username’, ’email’)
請求 http://example.com/api/users?ordering=account,-username。
再說下許可權。
rest_framework 中提供了七種許可權
1 2 3 4 5 6 7 |
AllowAny # 無限制 IsAuthenticated # 登陸使用者 IsAdminUser # Admin 使用者 IsAuthenticatedOrReadOnly # 非登入使用者只讀 DjangoModelPermissions # 以下都是根據 Django 的 ModelPremissions DjangoModelPermissionsOrAnonReadOnly DjangoObjectPermissions |
把原始碼拿出來:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class IsAuthenticated(BasePermission): """ Allows access only to authenticated users. """ def has_permission(self, request, view): return request.user and request.user.is_authenticated() class IsAdminUser(BasePermission): """ Allows access only to admin users. """ def has_permission(self, request, view): return request.user and request.user.is_staff |
其實還是比較理解的,首先判斷使用者,如果滿足就返回True。要注意我們在Viewset中設定的許可權必須是一個元祖,否則會出現錯誤。正確寫法:permission_classes = (IsAdminUser,)。元祖裡可以寫多個permission,但注意,元祖裡的屬於與的關係,即任何一個不滿足都不行,而不是或的關係,具體可見如下原始碼:
1 2 3 4 5 6 7 8 9 10 |
def check_permissions(self, request): """ Check if the request should be permitted. Raises an appropriate exception if the request is not permitted. """ for permission in self.get_permissions(): if not permission.has_permission(request, self): self.permission_denied( request, message=getattr(permission, 'message', None) ) |
當然,除了預設的許可權我們也可以自定義許可權:
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 |
class UserPermisson(BasePermission): """ define one permission,which can limit the user operation """ def has_object_permission(self, request, view, obj): """ only the creater can delete,put or post :param request: :param view: :param obj: :return: """ return obj == request.user or request.user.is_staff class ArticlePermisson(BasePermission): def has_object_permission(self, request, view, obj): """ only the creater can delete,put or post :param request: :param view: :param obj: :return: """ if request.method in SAFE_METHODS: #only the authorised user can get data return request.user.is_authenticated return request.user == obj.author |
這裡要注意,對於一個request,會優先檢查permission的has_permission方法,它是對每一個request進行檢查。然後才檢查has_object_permission,這是對每一個object進行檢查。兩者的區別是前者是對每一個request請求設定的許可權,後者是對資料庫條目進行許可權設定的,但我理解她也包含了對request的控制。
如果不滿足許可權要求,則返回403或者401等錯誤程式碼。
然後定義路由:
1 2 3 4 5 6 7 8 9 |
urls.py from rest_framework import routers from .views import CategoryViewset,ArticleViewset,UserViewset router = routers.DefaultRouter() router.register(r'categories',CategoryViewset) router.register(r'articles',ArticleViewset) router.register(r'users',UserViewset) |
有幾點說明:
1、如果在viewset設定了許可權,那麼必須得帶上登入使用者名稱,密碼等資訊;
2、如果沒有設定許可權,那不帶使用者名稱可以使用GET方法,但是其他操作如POST,PUT,DELETE等,也要帶上登入使用者名稱,密碼等資訊;
測試:
curl http://localhost:8080/api/categoriese/10/?format=json(預設為get)
curl http://localhost:8080/api/categorieses/
curl -u username:password -X PUT -d “name=dwd&alias=dw” http://localhost:8080/api/categories/10/
curl -u username:password -X delete http://localhost:8080/api/categoriese/10/
curl -u username:password -X POST -d “name=dwd&alias=dw” http://localhost:8080/api/categories/10/
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式