- serializer --> Modele bağlı olarak otomatik fieldlar oluşturur. Database içeriisnde ki verileri Json ve XML formatına çevirir.
- Serializerler ModelSerializer ve normal serializer olarak ikiye ayırılır.
Başlamadan önce basit bir model yapısı oluşturalım.
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
title = models.CharField(max_length=120)
content = models.TextField()
draft = models.BooleanField(default=False)
created = models.DateTimeField(editable=False)
modified = models.DateTimeField()
slug = models.SlugField(unique=True, max_length=150, editable=False)
def save(self, *args, **kwargs):
if not self.id: ## idsi yoksa ilk defa oluşturuluyordur bu yüzden self.created
self.created = timezone.now()
self.modified = timezone.now()
return super(Post, self).save(*args, **kwargs)
from rest_framework import serializers
from models. import Post
class PostSerrializer(serializers.Serializer):
title = serializers.CharField(max_length=200)
content = serializers.CharField(max_length=200)
- model --> model
- fields --> çekmek istediğimiz fieldlar
from rest_framework import serializers
from models import Post
class PostSerrializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = [
'title', 'content'
]
- Listeleme işlemini gerçekleştirir.
from rest_framework.generics import ListAPIView
from models import Post
from serializers import PostSerrializer
class PostListAPIView(ListAPIView):
queryset = Post.object.all()
serializer_class = PostSerrializer
- Geriye tek bir model döndürür.
- lookup_field --> neye göre detay sayfasına gidilecek?
pk
,slug
vs default değeripk
from rest_framework.generics import RetrieveAPIView
from models import Post
from serializers import PostSerrializer
class PostDetailAPIView(RetrieveAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
lookup_field = 'slug'
- DestroyAPIView --> Silme işlemi gerçekleştirir
- UpdateAPIView --> Güncelleme işlemi gerçekleştirir.
from rest_framework.generics import DestroyAPIView, UpdateAPIView,
from post.api.serializers import PostSerializer
from post.models import Post
class PostDeleteAPIView(DestroyAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
lookup_field = 'slug'
class PostUpdateAPIView(UpdateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
lookup_field = 'slug'
Serializere yeni bir serializer class'ı ekliyoruz
class PostUpdateCreateSeralizer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['title', 'content', 'image']
- perform_create --> create işlemi gerçekleşeceği zaman tetiklenir mail gönderme celery worker bu kısımdan gönderebiliriz.
from rest_framework.generics import CreateAPIView
from serializer import PostUpdateCreateSeralizer
class PostCreateAPIView(CreateAPIView):
queryset = Post.objects.all()
serializer_class = PostUpdateCreateSeralizer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class PostUpdateAPIView(UpdateAPIView):
queryset = Post.objects.all()
serializer_class = PostUpdateCreateSeralizer
lookup_field = 'slug'
def perform_update(self, serializer):
...
- update işleminde değerler fieldların içerisine yerleşmiş bir şekilde geriye döner.
class PostUpdateAPIView(RetrieveUpdateAPIView):
queryset = Post.objects.all()
serializer_class = PostUpdateCreateSeralizer
lookup_field = 'slug'
def perform_update(self, serializer):
serializer.save(modified_by=self.request.user)
- Bu aşamaya kadar yazmış olduğumuz viewların url tanımları
from django.urls import path, include
from post.api.views import PostListAPIView, PostDetailAPIView, PostDeleteAPIView, PostUpdateAPIView, PostCreateAPIView
app_name = 'post'
urlpatterns = [
path('list', PostListAPIView.as_view(), name='list'),
path('detail/<slug>', PostDetailAPIView.as_view(), name='detail'),
path('update/<slug>', PostUpdateAPIView.as_view(), name='update'),
path('delete/<slug>', PostDeleteAPIView.as_view(), name='delete'),
path('create', PostCreateAPIView.as_view(), name='create'),
]
- permissions_classes --> Kullanıcı yetkileri burada ayarlarını.
from rest_framework.generics import CreateAPIView
from serializer import PostUpdateCreateSeralizer
from rest_framework.permissions import IsAuthenticated
class PostCreateAPIView(CreateAPIView):
queryset = Post.objects.all()
serializer_class = PostUpdateCreateSeralizer
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
serializer.save(user=self.request.user)
- Kendi yazdığımız permissionslardır.
- BasePermission Classından türetilir
from rest_framework.permissions import BasePermission
class IsOwner(BasePermission):
message = "You must be the owner of this object."
def has_object_permission(self, request, view, obj):
return obj.user == request.user or request.user.is_superuser
from rest_framework.generics import CreateAPIView
from serializer import PostUpdateCreateSeralizer
from rest_framework.permissions import IsAuthenticated
from post.api.permissions import IsOwner
class PostUpdateAPIView(RetrieveUpdateAPIView):
queryset = Post.objects.all()
serializer_class = PostUpdateCreateSeralizer
lookup_field = 'slug'
permission_classes = [IsAuthenticated, IsOwner]
def perform_update(self, serializer):
serializer.save(modified_by=self.request.user)
- has_permission --> İlk önce koşul tanımaksızın burası çalışır. Post yapma olanağı tanımaz.
- has_object_permission --> Delete methoduyla işlem yaptığımız zaman çalışır
from rest_framework.permissions import BasePermission
class IsOwner(BasePermission):
def has_permission(self, request, view):
return request.user and request.user.is_authenticated
message = "You must be the owner of this object."
def has_object_permission(self, request, view, obj):
return obj.user == request.user or request.user.is_superuser
- save(self, validated_data) --> create fonksiyonu mu yoksa update fonksiyonumu çalışacağına karar verir.
- create(self, validated_data) --> create işlemi yapıldığı zaman tetiklenir
- update (self, instance, validate_date) --> update işlemi tetikler
- validated_tittle(self, value) --> tittle a ait işlemleri burada gerçekleştiririz.
- validated(self,attrs) --> tüm fieldlar üzerinde çalışır
from rest_framework import serializers
from post.models import Post
class PostUpdateCreateSeralizer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['title', 'content']
def create(self, validated_data):
title = validated_data['title]
##return validated_data
return Post.objects.create(user=self.context['request'].user, **validated_data)
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.title = 'edited'
instance.content = validated_data.get('content', instance.content)
instance.save()
return instance
def validate_title(self, value):
if value == 'deger'
raise serializers.ValidationError('Bu değer girilemez.')
return value
def validate(self, attrs):
if attrs['title'] == 'deger':
attrs['title'] = 'gecersiz'
raise serializers.ValidationError('Bu değer girilemez.')
return attrs
- get_queryset(self) --> Bu method ile filtreleme yapabiliriz
- filter_backends --> Kullanacağımız filtre yöntemi
- search_fields --> neye gore arama yapacağımızı belirtiriz
- OrderingFilter --> neye göre sıralayacağımızı belirtiriz
- ....../api/post/list?search=Lorem&ordering=title
from rest_framework.filters import SearchFilter, OrderingFilter
class PostListAPIView(ListAPIView):
serializer_class = PostSerializer
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ['title', 'content']
def get_queryset(self):
queryset = Post.objects.filter(draft=False)
return queryset
from rest_framework.pagination import PageNumberPagination
class PostPagination(PageNumberPagination):
page_size = 2
from post.api.paginations import PostPagination
class PostListAPIView(ListAPIView):
...
pagination_class = PostPagination
- slug yerine ana sayfada link döndürmemize olanak sağlar
from rest_framework import serializers
from post.models import Post
class PostSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='namespace:name',
lookup_field='slug'
)
class Meta:
model = Post
fields = ['username', 'title', 'content', 'url', 'created', ]
- Serializer kısmında gösterilen değer üzerinde değişiklik yapmaya olanak sağlar
- obj --> serialize edilen obje
- method_name --> method ismi girilmelidir
- get_username --> method_name gerek kalmadan işlemleri gerçekleştirir.
from rest_framework import serializers
from post.models import Post
class PostSerializer(serializers.ModelSerializer):
...
# username= serializers.SerializerMethodField(method_name='username_new')
username = serializers.SerializerMethodField()
...
def get_username(self, obj):
return str(obj.user.username)
def username_new(self, obj):
return str(obj.user.username)
Yeni bir app oluşturup basit bir Model comment
yapısı oluşturalım
- def children() --> yorumun altındaki yorumları bulmamızı sağlayacak
- def any_children() --> parent altında herhangi bir yorum olup olmadığını kontrol edicek
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='post')
content = models.TextField()
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='replies')
created = models.DateTimeField(editable=False)
def save(self, *args, **kwargs):
if not self.id:
self.created = timezone.now()
self.modified = timezone.now()
return super(Comment, self).save(*args, **kwargs)
def children(self):
return Comment.objects.filter(parent=self)
@property
def any_children(self):
return Comment.objects.filter(parent=self).exists()
Serializer dosyamızı da oluşturalım
- exclude --> girilen parametre hariç tüm fieldları listeler
from comment.models import Comment
class CommentCreateSerializer(ModelSerializer):
class Meta:
model = Comment
exclude = ['created', ]
def validate(self, attrs):
if (attrs["parent"]):
if attrs['parent'].post != attrs['post']:
raise serializers.ValidationError('Yanlış birşeyler var')
return attrs
class CommentCreateAPIView(CreateAPIView):
queryset = Comment.objects.all()
serializer_class = CommentCreateSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
- fields = 'all' --> tüm fieldları listeler
- get_replies --> datayı Serializer e gönderiyoruz daha sonra serialize edilmiş halini geriye döndürüyoruz. Bu şekilde yorumlar iç içe bir şekilde listelenme işlemi gerçekleştiriyor.
class CommentListSerializer(ModelSerializer):
replies = SerializerMethodField()
user = UserSerializer()
post = PostCommentSerialize()
class Meta:
model = Comment
fields = '__all__'
# depth = 1 # Tüm verileri getirir
def get_replies(self, obj):
if obj.any_children:
return CommentListSerializer(obj.children(), many=True).data
class CommentListAPIView(ListAPIView):
serializer_class = CommentListSerializer
def get_queryset(self):
return Comment.objects.filter(parent=None)
- Hem postu hemde posta
- .......ap/comment/deneme?q=5
- ?q= 5' I back ende yollarız. Erişmek için:
self.request.GET.get("q")
- Bu şekilde 5 nolu id ye sahip olan yorumları döndürürüz.
class CommentListAPIView(ListAPIView):
serializer_class = CommentListSerializer
pagination_class = CommentPagination
def get_queryset(self):
queryset = Comment.objects.filter(parent=None)
query = self.request.GET.get("q")
if query:
queryset = Comment.objects.filter(post=query)
return queryset
- depth = 1 --> ForeignKey le bağlı olan tüm verilerin içeriğini açar, parola dahil.
class CommentListSerializer(ModelSerializer):
replies = SerializerMethodField()
class Meta:
model = Comment
fields = '__all__'
depth = 1
- Bir diğer yol, daha güvenli
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = ('last_name', 'first_name', 'id', 'email')
class CommentListSerializer(ModelSerializer):
replies = SerializerMethodField()
user = UserSerializer()
class Meta:
model = Comment
fields = '__all__'
- Yazdığımız viewların sadece kendi işlemini (delete, create gibi) değil extra olarak başka işleri de yapmamıza olanak sağlar.
from rest_framework.mixins import DestroyModelMixin, UpdateModelMixin
class CommentDeleteAPIView(DestroyAPIView, UpdateModelMixin, RetrieveModelMixin):
queryset = Comment.objects.all()
serializer_class = CommentDeleteUpdateSerializer
lookup_field = 'pk'
permission_classes = [IsOwner]
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
get self.retrieve(request, *args, **kwargs)
-
JWT kullanırız. JWT bir authorization token yöntemi.
-
Bir linke istek yapalır ve bu linkin headerında bulunan authorization kısmına token yazılıp sunucuya gönderilir.
-
access token --> Kısa ömürlü 5dk
-
refresh token --> 24 saat sürer
-
refresh token sayesinde yeni access token alınır. Refresh token sunucuya gönderilir ve o bize yeni access token döndürür.
-
refresh token süresi son bulursa kullanıcının yeniden giriş yapması gerekir.
-
JWT üç kısımdan oluşur
xxxxx.yyyyy.zzzzz header.payload.signature header = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 payload = eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQzODI4NDMxLCJqdGkiOiI3ZjU5OTdiNzE1MGQ0NjU3OWRjMmI0OTE2NzA5N2U3YiIsInVzZXJfaWQiOjF9 signature = Ju70kdcaHKn1Qaz8H42zrOYk0Jx9kIckTn9Xx7vhikY
-
signature
header
vepayload
kısımlarının base64 le şifrelenmiş halini ve settings dosyasında bulunanSECRET_KEY
'den oluşuyor. -
Kütüphaneyi kurmak için:
pip install djangorestframework_simplejwt
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
from django.urls import path from rest_framework_simplejwt import views as jwt_views
urlpatterns = [ # Your URLs...
path('api/token/', jwt_views.TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'),
]
- JWT Token süresini değiştirmek için
SIMPLE_JWT ={
'ACCES_TOKEN_LIFETIME' : timedelta(minutes=15)
}
- receiver --> tetikleme kısmında rol alır.
- @receiver(post_save, sender=User) --> User modelinde herhangi bir post_save işlemi olursa bu kısım tetiklenecektir.
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
note = models.CharField(max_length=200)
twitter = models.CharField(max_length=200)
def __str__(self):
return self.user.username
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
- validated_data.pop('profile') --> sadece profilin bulunduğu bilgileri çekeriz.
- raise_exception=True --> hata varsa hatayı fırlatır
- super(UserSerializer, self).update(instance, validated_data) --> Kullanıcı bilgilerini kayıt eder.
class ProfileSerializer(ModelSerializer):
class Meta:
model = Profile
fields = ('id', 'note', 'twitter')
class UserSerializer(ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'profile')
def update(self, instance, validated_data):
profile = validated_data.pop('profile')
profile_serializer = ProfileSerializer(instance=instance.profile, data=profile)
profile_serializer.is_valid(raise_exception=True)
profile_serializer.save()
return super(UserSerializer, self).update(instance, validated_data)
class ProfileView(RetrieveUpdateAPIView):
permission_classes = (IsAuthenticated,)
serializer_class = UserSerializer
queryset = User.objects.all()
def get_object(self):
queryset = self.get_queryset()
obj = get_object_or_404(queryset, id=self.request.user.id)
return obj
def perform_update(self, serializer):
serializer.save(user=self.request.user)
- validate_password --> girilen parolanın güçlü olup olmadığını kontrol eder.
class ChangePasswordSerializer(Serializer):
old_password = serializers.CharField(required=True)
new_password = serializers.CharField(required=True)
def validate_new_password(self, value):
validate_password(value)
return value
- get_object --> giriş yapan kullanıcıyı geriye döndürür.
- put --> update işleminde tetiklenir.
- self.object.check_password(old_password) --> eski parola doğru mu?
- update_session_auth_hash(request, self.object) --> login işlemi gerçekleştirir.
class UpdatePassword(APIView):
permission_classes = (IsAuthenticated,)
def get_object(self):
return self.request.user
def put(self, request, *args, **kwargs):
self.object = self.get_object()
serializer = ChangePasswordSerializer(data=request.data)
if serializer.is_valid():
old_password = serializer.data.get('old_password')
if not self.object.check_password(old_password):
return Response({'old_password': 'wrong_password'}, status=status.HTTP_400_BAD_REQUEST)
self.object.set_password(serializer.data.get('new_password'))
self.object.save()
update_session_auth_hash(request, self.object)
return Response(status=status.HTTP_204_NO_CONTENT)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)