Giriş.
Merhabalar.
Django Rest Framework ile çalışırken bazı durumlarda bir viewset’in birden fazla serializer ile çalışabilmesini ve yapılan isteğe göre dönecek veya validasyondan geçirilecek verinin değişkenlik göstermesini isteyebiliriz.
Bildiğiniz üzere de viewsetler varsayılan olarak rest_framework.mixins altında yaşayan mixinlerden türetilmiş ve gelen HTTP isteğine ve bir queryset ya da instance üzerinde çalışma durumuna bağlı olarak bir aksiyon dispatch ederek ilgili mixinin çalıştırılmasını sağlayan bir yapıya sahip. Ayrıca ilgili resource’ı yöneten viewset üzerine yeni aksiyonlar yazılarak genişletilebilmekte.
Nasıl yapılacağını öğrenmek isterseniz link burada.
Varsayılan aksiyonların farklı serializerlar ile çalıştırılması.
Konuya dönecek olursak, blog gönderilerimizi sunan bir resource’ımız olsun. Listeleme endpointinde nispeten daha az bilgi dönmek, detay endpointlerinde ise, yazarın da bilgilerinin dahil olduğu daha detaylı bir cevap dönmek istiyoruz. Bu çok olası bir senaryo.
Viewsetler aksi belirtilmediği sürece serialize edilecek veri için viewset gövdesinde tanımlı olan serializer_class sınıfını kullanır. Bu durum get_serializer_class metodu override edilerek değiştirilebilir ki bizde öyle yapacağız. Bir mixin yazarak birden fazla serializer’ın kullanılacağı viewsetlerde bu özelliği ilgili viewsete mixin üzerinden kazandırarak dinamik ve kendimizi tekrar etmeyen bir yapı ortaya çıkaracağız.
class MultipleSerializerMixin: def get_serializer_class(self): try: return self.serializers_by_actions[self.action] except (KeyError, AttributeError): return super(MultipleSerializerMixin, self).get_serializer_class()
Viewset üzerinde işlenen her isteğin bir aksiyon dispatch ettiğinden bahsettik, ilgili aksiyonu ilk olarak yine viewset gövdesinde tanımlı olan serializer_by_actions dict içinde arıyor, tanımsız olması ya da aksiyonun bulunamaması durumunda viewsetın get_serializer_class metodunu çağırıyoruz.
Örneklediğimiz blog gönderileri senaryosunda bu işlemi görecek olursak;
class PostViewset(MultipleSerializerMixin, viewsets.ModelViewSet): queryset = Post.objects.all() serializer_class = PostDefaultSerializer serializers_by_actions = { "list": PostSerializer, "retrieve": PostDetailedSerializer, "create": PostCreateSerializer, }
Nispeten daha az detay döndügümüz listeleme cevabı (list aksiyonu dispatch edildi);
$ http --body :1234/api/posts/ [ { "author": 1, "content": "test content", "id": 1, "title": "lorem ipsum", "url": "http://localhost:1234/api/posts/1/" } ]
Daha detaylı cevabın dönüldüğü tekil görüntüleme (retrieve aksiyonu dispatch edildi);
$ http --body :1234/api/posts/1/ { "author": { "date_joined": "2021-06-17T12:04:07.373718Z", "email": "", "last_login": null, "username": "egehan" }, "content": "test content", "created_at": "2021-06-19T12:05:58.024044Z", "id": 1, "modified_at": "2021-06-19T12:05:58.024093Z", "title": "lorem ipsum" }
Varsayılan viewset aksiyonlarına karşılık gelcek şekilde farklı serializerların nasıl kullanılabileceğimizi artık biliyoruz.
Custom aksiyonların farklı serializerlar ile çalıştırılması.
Yazının başında da bahsettiğimiz üzere viewsete yeni bir aksiyon yazarak genişletebileceğimizden bahsettik fakat kullanılacak olan serializerı nasıl belirleyeceğiz?
Burada kullandığınız DRF versiyonuna bağlı iki durum bulunmakta.
- DRF versiyonu olarak 3.8.0 ve altını kullanmakta iseniz yeni bir aksiyon eklemek için detail_route veya list_route decoratorlerini kullanmanız gerekmekte.
@list_route(methods=["GET"]) def detailed_list(self, request, *args, **kwargs): qs = self.get_queryset() serializer = self.get_serializer(qs, many=True) ...
Örnek olması açısından yukarıdaki gibi bir aksiyon yazımında viewset gövdesinde bulunan serializer_by_actions’a ilgili aksiyon ismini(tanımladığınız metod ismi) ve karşılık gelecek serializer sınıfını yazmamız gerekmekte.
# same viewset. serializers_by_actions = { "list": PostListSerializer, "retrieve": PostDetailedSerializer, "create": PostCreateSerializer, 'detailed_list':PostListDetailedSerializer }
- Eğer yukarıdaki DRF sürümünden daha güncel bir sürüm kullanıyor iseniz detail_route ve list_route decoratorlerinin kaldırılarak sadece action decoratorunun bulunduğunu ve detail(bool) parametresi ile qs yada instance üzerine yeni aksiyonun maplendiğini farketmiş olmalısınız.
@action(methods=["GET"], detail=False, serializer_class=PostListDetailedSerializer) def detailed_list(self, request, *args, **kwargs): qs = self.get_queryset() serializer = self.get_serializer(qs, many=True) ...
action decoratoru ile beraber vereceğimiz **kwargs lar aksiyon üzerinden bir istek işleneceği zaman viewset seviyesinde override edilerek kullanılabilmekte. Böylece herhangi bir maplemeyi manuel yapmadan doğrudan parametre olarak geçebilmekteyiz.
Ek bilgi. Viewset gövdesinde tanımlı olan ve isteğin değerlendirilmesi sırasında kullanılacak olan tüm özellikler action decoratorune parametre olarak geçirilirse override edilir. (authentication, permission veya parser classess vs)
Geçenlerde bir çalışma arkadaşıma anlattığım bu yapıyı, belki başka birilerinin daha işine yarar diye buraya iliştiriyorum. Yine hızlı bir yazı oldu imla ve anlam kaymalarını hoş görmeniz dileğiyle 🙂
Viewsetler hakkında daha fazla bilgi için API doc.
Kolaylıklar!
İlk Yorumu Siz Yapın