Angular4 + Django1.11 vol.5
参考サイト
https://www.codingforentrepreneurs.com/projects/angular-django/ http://www.django-rest-framework.org/api-guide/generic-views/#listapiview http://qiita.com/kura07/items/c9fa858870ad56dfec12
Integrate Django API with Angular
const endpoint = '/api/videos/'
- お〜〜表示された!すげー!
Detail View
- detail viewを作成するために、まずは api/views.pyに、RetrieveAPIViewを追加する。
from django.contrib.auth.models import User from rest_framework import generics from rest_framework.permissions import IsAdminUser from videos.models import Video from .serializers import VideoSerializer class VideoList(generics.ListAPIView): queryset = Video.objects.all() serializer_class = VideoSerializer permission_classes = [] authentication_classes = [] class VideoDetail(generics.RetrieveAPIView): queryset = Video.objects.all() serializer_class = VideoSerializer permission_classes = [] authentication_classes = []
- 次に、urls.pyにdetail viewのurlを追加する。
from django.conf.urls import url from .views import VideoList, VideoDetail urlpatterns = [ url(r'^$', VideoList.as_view(), name='list'), url(r'^(?P<slug>[\w-]+)/$', VideoDetail.as_view()), ] # ここでは、slugからvideoDetailをgetするようにセットしてある
http://127.0.0.1:8000/api/videos/bound-method-videotitle-of-video-new-title/
にアクセスしてみる。。。error発生!
AssertionError at /api/videos/bound-method-videotitle-of-video-new-title/ Expected view VideoDetail to be called with a URL keyword argument named "pk". Fix your URL conf, or set the `.lookup_field` attribute on the view correctly.
- lookup_fieldのセットが必要ぽいので、セットする。
class VideoDetail(generics.RetrieveAPIView): queryset = Video.objects.all() serializer_class = VideoSerializer lookup_field = "slug" permission_classes = [] authentication_classes = []
これだけで修正完了(^^)すごい!
VideoDetail用のVideoDetailSerializerも作成する。
class VideoDetailSerializer(serializers.ModelSerializer): image = serializers.SerializerMethodField() is_promo = serializers.SerializerMethodField() class Meta: model = Video fields = [ 'name', 'slug', 'embed', 'featured', 'image', 'is_promo' ] def get_image(self,obj): return "/static/ang/assets/images/nature/4.jpg" def get_is_promo(self,obj): return False
VideoDetailに、
serializer_class=VideoDetailSerializer
を、セットすれば完了。videos.service.tsのget(slug)メソッドを修正する。
get(slug){ return this.http.get(endpoint + slug + "/") // endpoint + slug + "/"に設定する。 .map(response=>response.json()) .catch(this.handleError) // return this.http.get(endpoint) // .map(response=>{ // let data = response.json().filter(item=>{ // if (item.slug == slug) { // return item // } // }) // if (data.length == 1){ // return data[0] // } // return {} // }) // .catch(this.handleError) }
Backend API Search
- backendのdjangoに、Search後のqueryを出力する機能をつける。
class VideoList(generics.ListAPIView): queryset = Video.objects.all() serializer_class = VideoSerializer permission_classes = [] authentication_classes = [] def get_queryset(self): query = self.request.GET.get("q") if query: qs = Video.objects.filter(name__icontains=query) else: qs = Video.objects.all() return qs
http://127.0.0.1:8000/api/videos/?q=new
になどのようにアクセスして、検索結果が表示されればOK(^^)videos.service.ts のsearch(query)メソッドのendpointを修正する。以下のように予想した。
search(query){ return this.http.get(endpoint + "?q=" + query) .map(response=>response.json()) .catch(this.handleError) }
- 上記でも動く気がするんだけど、動画内では以下のように記載していた。
search(query){ let querystring = `?q=${query}` return this.http.get(endpoint + querystring) .map(response=>response.json()) .catch(this.handleError) // return this.http.get(endpoint) // .map(response=>{ // let data = [] // let req = response.json().filter(item=>{ // if (item.name.indexOf(query) >=0) { // data.push(item) // } // }) // // return data // }) // .catch(this.handleError) }
- videos.service.tsは以下の通りに全体を修正したある。
import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/catch'; // const endpoint = '/static/ang/assets/json/videos.json' // http://www.yourdomain.com/api/videos/ const endpoint = '/api/videos/' @Injectable() export class VideoService { constructor(private http: Http) { } list(){ return this.http.get(endpoint) .map(response=>response.json()) .catch(this.handleError) } get(slug){ return this.http.get(endpoint + slug + "/") .map(response=>response.json()) .catch(this.handleError) } search(query){ let querystring = `?q=${query}` return this.http.get(endpoint + querystring) .map(response=>response.json()) .catch(this.handleError) } private handleError(error:any, caught:any): any{ console.log(error, caught) } }
- home.component.ts の、featuredされたitemのみを表示するという機能もbackendに移行させる。
ngOnInit() { this.req = this._video.featured().subscribe(data=>{ //console.log(data.json()) //this.homeImageList this.homeImageList = data as [VideoItem] // data.filter(item=>{ // if(item.featured){ // let dataItem = item // this.homeImageList.push(dataItem) // } // }) // this.homeImageList = data.json(); }) }
- featuredメソッドは、まだ定義されていないので、videos.service.tsに追加する。
featured(){ return this.http.get(endpoint + "featured/") .map(response=>response.json()) .catch(this.handleError) }
- backendのdjangoに、featured のurlを作成していないので、設定する。
lass VideoFeatured(generics.ListAPIView): queryset = Video.objects.all() serializer_class = VideoSerializer permission_classes = [] authentication_classes = [] def get_queryset(self): query = self.request.GET.get("q") if query: qs = Video.objects.filter(name__icontains=query).filter(featured=True) else: qs = Video.objects.filter(featured=True) return qs
- urls.py に、featured viewのurlをセットする。
from django.conf.urls import url from .views import VideoList, VideoDetail, VideoFeatured urlpatterns = [ url(r'^$', VideoList.as_view(), name='list'), url(r'^featured/$', VideoFeatured.as_view(), name='featured'), url(r'^(?P<slug>[\w-]+)/$', VideoDetail.as_view()), ]
http://127.0.0.1:8000/api/videos/featured/
にアクセスして、rest_framework viewが表示されればOK(^^)
まとめ
endpointから必要なデータを取得して、client側では加工しないほうが、シンプルな実装ができそうだな。frontendとbackendを分けるメリットはこのへんにあるのか。
let querystring =
?q=${query}
については、ちゃんと意味を調べないとな。。とりあえず、バッククオートは、複数行のコメントをするときに便利らしい。あと${変数}の表現が使える。あと、${変数}で、
+
などの演算子を使わずに、文字列をつなげることができるらしい。なので、let querystring = `?q=${query}
は、let querystring = '"?q=" + query
と同じはず。でも書いてみて思ったけどたしかに文字列をつなげるときは、${}
のほうが使いやすいそう(^^)このqiitaの記事がわかりやすかったです! http://qiita.com/kura07/items/c9fa858870ad56dfec12
はじめから、apiを使う仕様で作成したほうが楽そうだなぁ(^^)