├── blog ├── __init__.py ├── wsgi.py ├── urls.py └── settings.py ├── post ├── __init__.py ├── api │ ├── __init__.py │ ├── paginations.py │ ├── permissions.py │ ├── urls.py │ ├── serializers.py │ └── views.py ├── migrations │ └── __init__.py ├── views.py ├── admin.py ├── apps.py ├── models.py └── tests.py ├── account ├── __init__.py ├── api │ ├── __init__.py │ ├── permissions.py │ ├── urls.py │ ├── throttles.py │ ├── serializers.py │ └── views.py ├── migrations │ ├── __init__.py │ ├── 0002_auto_20190722_1232.py │ └── 0001_initial.py ├── views.py ├── apps.py ├── admin.py ├── models.py └── tests.py ├── comment ├── __init__.py ├── api │ ├── __init__.py │ ├── paginations.py │ ├── permissions.py │ ├── urls.py │ ├── serializers.py │ └── views.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── views.py ├── apps.py ├── admin.py ├── models.py └── tests.py ├── favourite ├── __init__.py ├── api │ ├── __init__.py │ ├── paginations.py │ ├── urls.py │ ├── permissions.py │ ├── serializers.py │ └── views.py ├── migrations │ ├── __init__.py │ ├── 0002_auto_20190722_0158.py │ └── 0001_initial.py ├── views.py ├── apps.py ├── admin.py ├── models.py └── tests.py ├── README.md ├── requirements.txt ├── manage.py └── .gitignore /blog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /post/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /account/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /account/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /comment/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /comment/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /favourite/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /post/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /favourite/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /post/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /account/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /comment/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /favourite/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /account/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /favourite/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /post/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views.py here. 4 | -------------------------------------------------------------------------------- /comment/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views.py here. 4 | -------------------------------------------------------------------------------- /post/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from post.models import Post 3 | 4 | admin.site.register(Post) 5 | -------------------------------------------------------------------------------- /post/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PostConfig(AppConfig): 5 | name = 'post' 6 | -------------------------------------------------------------------------------- /account/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountConfig(AppConfig): 5 | name = 'account' 6 | -------------------------------------------------------------------------------- /comment/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CommentConfig(AppConfig): 5 | name = 'comment' 6 | -------------------------------------------------------------------------------- /favourite/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class FavouriteConfig(AppConfig): 5 | name = 'favourite' 6 | -------------------------------------------------------------------------------- /favourite/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Favourite 3 | # Register your models here. 4 | admin.site.register(Favourite) -------------------------------------------------------------------------------- /comment/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from comment.models import Comment 3 | 4 | # Register your models here. 5 | admin.site.register(Comment) -------------------------------------------------------------------------------- /post/api/paginations.py: -------------------------------------------------------------------------------- 1 | from rest_framework.pagination import PageNumberPagination 2 | 3 | class PostPagination(PageNumberPagination): 4 | page_size = 4 -------------------------------------------------------------------------------- /account/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from account.models import Profile 5 | 6 | admin.site.register(Profile) -------------------------------------------------------------------------------- /comment/api/paginations.py: -------------------------------------------------------------------------------- 1 | from rest_framework.pagination import PageNumberPagination 2 | 3 | class CommentPagination(PageNumberPagination): 4 | page_size = 4 -------------------------------------------------------------------------------- /favourite/api/paginations.py: -------------------------------------------------------------------------------- 1 | from rest_framework.pagination import PageNumberPagination 2 | 3 | class FavouritePagination(PageNumberPagination): 4 | page_size = 4 -------------------------------------------------------------------------------- /account/api/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import BasePermission 2 | 3 | class NotAuthenticated(BasePermission): 4 | message = "You already have an account." 5 | def has_permission(self, request, view): 6 | return not request.user.is_authenticated -------------------------------------------------------------------------------- /favourite/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from favourite.api.views import FavouriteListCreateAPIView, FavouriteAPIView 4 | 5 | app_name = "favourite" 6 | urlpatterns = [ 7 | path('list-create', FavouriteListCreateAPIView.as_view(), name='list-create'), 8 | path('update-delete/', FavouriteAPIView.as_view(), name='update-delete'), 9 | ] 10 | 11 | 12 | -------------------------------------------------------------------------------- /comment/api/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import BasePermission 2 | 3 | class IsOwner(BasePermission): 4 | def has_permission(self, request, view): 5 | return request.user and request.user.is_authenticated 6 | 7 | message = "You must be the owener of this object." 8 | 9 | def has_object_permission(self, request, view, obj): 10 | return obj.user == request.user -------------------------------------------------------------------------------- /favourite/api/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import BasePermission 2 | 3 | class IsOwner(BasePermission): 4 | def has_permission(self, request, view): 5 | return request.user and request.user.is_authenticated 6 | 7 | message = "You must be the owener of this object." 8 | 9 | def has_object_permission(self, request, view, obj): 10 | return (obj.user == request.user) -------------------------------------------------------------------------------- /post/api/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import BasePermission 2 | 3 | class IsOwner(BasePermission): 4 | def has_permission(self, request, view): 5 | return request.user and request.user.is_authenticated 6 | 7 | message = "You must be the owener of this object." 8 | 9 | def has_object_permission(self, request, view, obj): 10 | return (obj.user == request.user) or request.user.is_superuser -------------------------------------------------------------------------------- /blog/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for blog project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blog.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Udemy üzerinde vermiş olduğum **DJANGO REST FRAMEWORK EĞİTİM SETİ** eğitiminin kaynak kodlarıdır. Aşağıda ki link üzerinden eğitim setlerime ulaşabilirsiniz. 2 | 3 | 4 | DJANGO REST FRAMEWORK EĞİTİM LİNKİ 5 | https://www.udemy.com/course/django-rest-framework-egitim-seti/?couponCode=DJANGORESTFRAMEWORK 6 | 7 | Django 3 | Profesyonel Web Programlama 8 | https://www.udemy.com/course/django-3-profesyonel-web-programlama/?couponCode=DJANGOPROFESYONEL 9 | 10 | -------------------------------------------------------------------------------- /favourite/models.py: -------------------------------------------------------------------------------- 1 | # Create your models here. 2 | from django.db import models 3 | from django.contrib.auth.models import User 4 | from post.models import Post 5 | 6 | class Favourite(models.Model): 7 | user = models.ForeignKey(User, on_delete=models.CASCADE) 8 | post = models.ForeignKey(Post, on_delete=models.CASCADE) 9 | content = models.CharField(max_length=120) 10 | class Meta: 11 | ordering = ["-id"] 12 | 13 | def __str__(self): 14 | return self.user.username -------------------------------------------------------------------------------- /account/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from account.api.views import ( 3 | ProfileView, 4 | UpdatePassword, 5 | CreateUserView 6 | ) 7 | app_name = "account" 8 | urlpatterns = [ 9 | path('me', ProfileView.as_view(), name='me'), 10 | path('change-password', UpdatePassword.as_view(), name='change-password'), 11 | path('register', CreateUserView.as_view(), name='register'), 12 | ] 13 | 14 | -------------------------------------------------------------------------------- /account/api/throttles.py: -------------------------------------------------------------------------------- 1 | from rest_framework.throttling import SimpleRateThrottle 2 | 3 | class RegisterThrottle(SimpleRateThrottle): 4 | scope = 'registerthrottle' 5 | 6 | def get_cache_key(self, request, view): 7 | if request.user.is_authenticated or request.method == 'GET': 8 | return None # Only throttle unauthenticated requests. 9 | 10 | return self.cache_format % { 11 | 'scope': self.scope, 12 | 'ident': self.get_ident(request) 13 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2019.6.16 2 | chardet==3.0.4 3 | coreapi==2.3.3 4 | coreschema==0.0.4 5 | Django==2.2.3 6 | django-filter==2.2.0 7 | django-rest-swagger==2.2.0 8 | djangorestframework==3.10.1 9 | djangorestframework-simplejwt==4.3.0 10 | idna==2.8 11 | itypes==1.1.0 12 | Jinja2==2.10.1 13 | Markdown==3.1.1 14 | MarkupSafe==1.1.1 15 | openapi-codec==1.3.2 16 | Pillow==6.1.0 17 | PyJWT==1.7.1 18 | pytz==2019.1 19 | requests==2.22.0 20 | simplejson==3.16.0 21 | sqlparse==0.3.0 22 | uritemplate==3.0.0 23 | urllib3==1.25.3 24 | -------------------------------------------------------------------------------- /comment/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from comment.api.views import ( 3 | CommentCreateAPIView, 4 | CommentListAPIView, 5 | CommentUpdateAPIView 6 | ) 7 | app_name = "comment" 8 | urlpatterns = [ 9 | path('create', CommentCreateAPIView.as_view(), name='create'), 10 | path('list', CommentListAPIView.as_view(), name='list'), 11 | path('update/', CommentUpdateAPIView.as_view(), name='update'), 12 | ] 13 | 14 | 15 | -------------------------------------------------------------------------------- /account/migrations/0002_auto_20190722_1232.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-22 09:32 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('account', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='profile', 15 | name='note', 16 | field=models.CharField(blank=True, max_length=200, null=True), 17 | ), 18 | migrations.AlterField( 19 | model_name='profile', 20 | name='twitter', 21 | field=models.CharField(blank=True, max_length=200, null=True), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blog.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /post/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from django.views.decorators.cache import cache_page 3 | 4 | from post.api.views import ( 5 | PostListAPIView, 6 | PostDetailAPIView, 7 | PostUpdateAPIView, 8 | PostCreateAPIView, 9 | ) 10 | app_name = "post" 11 | urlpatterns = [ 12 | path('list', cache_page(60 * 1)(PostListAPIView.as_view()), name='list'), 13 | path('detail/', PostDetailAPIView.as_view(), name='detail'), 14 | path('update/', PostUpdateAPIView.as_view(), name='update'), 15 | path('create/', PostCreateAPIView.as_view(), name='create'), 16 | ] 17 | 18 | 19 | -------------------------------------------------------------------------------- /account/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.db import models 3 | 4 | from django.db.models.signals import post_save 5 | from django.dispatch import receiver 6 | 7 | class Profile(models.Model): 8 | user = models.OneToOneField(User, on_delete=models.CASCADE) 9 | note = models.CharField(max_length=200, null=True, blank=True) 10 | twitter = models.CharField(max_length=200, null=True, blank=True) 11 | 12 | def __str__(self): 13 | return self.user.username 14 | 15 | @receiver(post_save, sender= User) 16 | def create_user_profile(sender, instance, created, **kwargs): 17 | if not created: 18 | return 19 | Profile.objects.create(user=instance) 20 | instance.profile.save() -------------------------------------------------------------------------------- /favourite/api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from rest_framework.serializers import ModelSerializer 3 | 4 | from favourite.models import Favourite 5 | 6 | 7 | class FavouriteListCreateAPISerializer(ModelSerializer): 8 | class Meta: 9 | model = Favourite 10 | fields = '__all__' 11 | 12 | def validate(self, attrs): 13 | queryset = Favourite.objects.filter(post = attrs["post"], user = attrs["user"]) 14 | if queryset.exists(): 15 | raise serializers.ValidationError("Zaten favorilere eklendi.") 16 | return attrs 17 | 18 | 19 | class FavouriteAPISerializer(ModelSerializer): 20 | class Meta: 21 | model = Favourite 22 | fields = ('content',) 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | # C extensions 5 | *.so 6 | # Distribution / packaging 7 | bin/ 8 | build/ 9 | develop-eggs/ 10 | dist/ 11 | eggs/ 12 | lib/ 13 | lib64/ 14 | parts/ 15 | sdist/ 16 | var/ 17 | *.egg-info/ 18 | .installed.cfg 19 | *.egg 20 | # Installer logs 21 | pip-log.txt 22 | pip-delete-this-directory.txt 23 | # Unit test / coverage reports 24 | .tox/ 25 | .coverage 26 | .cache 27 | nosetests.xml 28 | coverage.xml 29 | # Translations 30 | *.mo 31 | # Mr Developer 32 | .mr.developer.cfg 33 | .project 34 | .pydevproject 35 | # Rope 36 | .ropeproject 37 | # Django stuff: 38 | *.log 39 | *.pot 40 | # Sphinx documentation 41 | docs/_build/ 42 | .idea 43 | db.sqlite3 44 | media 45 | blog/migrations/* 46 | post/migrations/* 47 | !post/migrations/__init__.py 48 | !blog/migrations/__init__.py -------------------------------------------------------------------------------- /favourite/migrations/0002_auto_20190722_0158.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-21 22:58 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('favourite', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='favourite', 17 | name='post', 18 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='post.Post'), 19 | ), 20 | migrations.AlterField( 21 | model_name='favourite', 22 | name='user', 23 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /blog/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | 4 | from django.conf import settings 5 | from django.conf.urls.static import static 6 | 7 | from rest_framework_simplejwt import views as jwt_views 8 | 9 | urlpatterns = [ 10 | path('admin/', admin.site.urls), 11 | path('api/post/', include('post.api.urls', namespace='post')), 12 | path('api/comment/', include('comment.api.urls', namespace='comment')), 13 | path('api/favourite/', include('favourite.api.urls', namespace='favourite')), 14 | path('api/user/', include('account.api.urls', namespace='acoount')), 15 | path('api/token/', jwt_views.TokenObtainPairView.as_view(), name='token_obtain_pair'), 16 | path('api/token/refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'), 17 | ] + static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT) 18 | 19 | -------------------------------------------------------------------------------- /post/api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from post.models import Post 3 | class PostSerializer(serializers.ModelSerializer): 4 | url = serializers.HyperlinkedIdentityField( 5 | view_name='post:detail', 6 | lookup_field='slug' 7 | ) 8 | username = serializers.SerializerMethodField() 9 | class Meta: 10 | model = Post 11 | fields = [ 12 | 'username', 13 | 'title', 14 | 'content', 15 | 'image', 16 | 'url', 17 | 'created', 18 | 'modified_by' 19 | ] 20 | 21 | def get_username(self, obj): 22 | return str(obj.user.username) 23 | 24 | class PostUpdateCreateSerializer(serializers.ModelSerializer): 25 | class Meta: 26 | model = Post 27 | fields = [ 28 | 'title', 29 | 'content', 30 | 'image', 31 | ] 32 | -------------------------------------------------------------------------------- /account/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-22 08:39 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Profile', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('note', models.CharField(max_length=200)), 22 | ('twitter', models.CharField(max_length=200)), 23 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 24 | ], 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /favourite/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-21 22:57 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ('post', '0006_auto_20190721_1259'), 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Favourite', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('content', models.CharField(max_length=120)), 23 | ('post', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='post.Post')), 24 | ('user', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 25 | ], 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /favourite/api/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView 2 | from rest_framework.permissions import IsAuthenticated 3 | 4 | from favourite.api.paginations import FavouritePagination 5 | from favourite.api.permissions import IsOwner 6 | from favourite.api.serializers import FavouriteListCreateAPISerializer, FavouriteAPISerializer 7 | from favourite.models import Favourite 8 | 9 | 10 | class FavouriteListCreateAPIView(ListCreateAPIView): 11 | serializer_class = FavouriteListCreateAPISerializer 12 | pagination_class = FavouritePagination 13 | permission_classes = [IsAuthenticated] 14 | def get_queryset(self): 15 | return Favourite.objects.filter(user = self.request.user) 16 | 17 | def perform_create(self, serializer): 18 | serializer.save(user = self.request.user) 19 | 20 | 21 | class FavouriteAPIView(RetrieveUpdateDestroyAPIView): 22 | queryset = Favourite.objects.all() 23 | serializer_class = FavouriteAPISerializer 24 | permission_classes = [IsOwner] 25 | lookup_field = 'pk' -------------------------------------------------------------------------------- /comment/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.db import models 3 | from django.utils import timezone 4 | 5 | from post.models import Post 6 | 7 | 8 | class Comment(models.Model): 9 | user = models.ForeignKey(User, on_delete=models.CASCADE) 10 | post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='post') 11 | content = models.TextField() 12 | parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='replies') 13 | created = models.DateTimeField(editable=False) 14 | 15 | class Meta: 16 | ordering = ('created', ) 17 | 18 | def __str__(self): 19 | return self.post.title + " " + self.user.username 20 | 21 | def save(self, *args, **kwargs): 22 | if not self.id: 23 | self.created = timezone.now() 24 | self.modified = timezone.now() 25 | return super(Comment, self).save(*args, **kwargs) 26 | 27 | def children(self): 28 | return Comment.objects.filter(parent=self) 29 | 30 | @property 31 | def any_children(self): 32 | return Comment.objects.filter(parent = self).exists() -------------------------------------------------------------------------------- /comment/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.3 on 2019-07-21 12:55 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('post', '0006_auto_20190721_1259'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Comment', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('content', models.TextField()), 23 | ('created', models.DateTimeField(editable=False)), 24 | ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='replies', to='comment.Comment')), 25 | ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='post', to='post.Post')), 26 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 27 | ], 28 | options={ 29 | 'ordering': ('created',), 30 | }, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /post/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | from django.utils import timezone 4 | from django.utils.text import slugify 5 | 6 | 7 | class Post(models.Model): 8 | user = models.ForeignKey(User, on_delete=models.CASCADE, default = 1) 9 | title = models.CharField(max_length=120) 10 | content = models.TextField() 11 | draft = models.BooleanField(default=False) 12 | created = models.DateTimeField(editable=False) 13 | modified = models.DateTimeField() 14 | slug = models.SlugField(unique=True, max_length=150, editable=False) 15 | image = models.ImageField(upload_to='post', null=True, blank=True) 16 | modified_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='modified_by') 17 | 18 | class Meta: 19 | ordering = ["-id"] 20 | 21 | def get_slug(self): 22 | slug = slugify(self.title.replace("ı","i")) 23 | unique = slug 24 | number = 1 25 | 26 | while Post.objects.filter(slug = unique).exists(): 27 | unique = '{}-{}'.format(slug, number) 28 | number += 1 29 | 30 | return unique 31 | 32 | def __str__(self): 33 | return self.title 34 | 35 | def save(self, *args, **kwargs): 36 | if not self.id: 37 | self.created = timezone.now() 38 | self.modified = timezone.now() 39 | self.slug = self.get_slug() 40 | return super(Post, self).save(*args,**kwargs) -------------------------------------------------------------------------------- /comment/api/serializers.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from rest_framework import serializers 3 | from rest_framework.serializers import ModelSerializer, SerializerMethodField 4 | 5 | from comment.models import Comment 6 | from post.models import Post 7 | 8 | 9 | class CommentCreateSerializer(ModelSerializer): 10 | class Meta: 11 | model = Comment 12 | exclude = ['created',] 13 | 14 | def validate(self, attrs): 15 | if(attrs["parent"]): 16 | if attrs["parent"].post != attrs["post"]: 17 | raise serializers.ValidationError("something went wrong") 18 | return attrs 19 | 20 | 21 | class UserSerializer(ModelSerializer): 22 | class Meta: 23 | model = User 24 | fields = ('first_name','last_name','id','email') 25 | 26 | 27 | class PostCommentSerializer(ModelSerializer): 28 | class Meta: 29 | model = Post 30 | fields = ('title','slug','id') 31 | 32 | class CommentListSerializer(ModelSerializer): 33 | replies = SerializerMethodField() 34 | user = UserSerializer() 35 | post = PostCommentSerializer() 36 | class Meta: 37 | model = Comment 38 | fields = '__all__' 39 | 40 | def get_replies(self, obj): 41 | if obj.any_children: 42 | return CommentListSerializer(obj.children(), many=True).data 43 | 44 | class CommentDeleteUpdateSerializer(serializers.ModelSerializer): 45 | class Meta: 46 | model = Comment 47 | fields = [ 48 | 'content' 49 | ] -------------------------------------------------------------------------------- /comment/api/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.generics import ( 2 | CreateAPIView, 3 | ListAPIView, 4 | UpdateAPIView, 5 | RetrieveAPIView 6 | ) 7 | #mixinler 8 | from rest_framework.mixins import DestroyModelMixin 9 | from rest_framework.permissions import IsAuthenticated 10 | from comment.api.paginations import CommentPagination 11 | from comment.api.permissions import IsOwner 12 | from comment.api.serializers import CommentCreateSerializer, CommentListSerializer, CommentDeleteUpdateSerializer 13 | from comment.models import Comment 14 | 15 | 16 | class CommentCreateAPIView(CreateAPIView): 17 | queryset = Comment.objects.all() 18 | serializer_class = CommentCreateSerializer 19 | permission_classes = [IsAuthenticated] 20 | 21 | def perform_create(self, serializer): 22 | serializer.save(user = self.request.user) 23 | 24 | 25 | class CommentListAPIView(ListAPIView): 26 | serializer_class = CommentListSerializer 27 | pagination_class = CommentPagination 28 | def get_queryset(self): 29 | queryset = Comment.objects.filter(parent = None) 30 | query = self.request.GET.get("q") 31 | if query: 32 | queryset = queryset.filter(post = query) 33 | return queryset 34 | 35 | 36 | 37 | class CommentUpdateAPIView(DestroyModelMixin, UpdateAPIView, RetrieveAPIView): 38 | queryset = Comment.objects.all() 39 | serializer_class = CommentDeleteUpdateSerializer 40 | lookup_field = 'pk' 41 | permission_classes = [IsOwner] 42 | 43 | def delete(self, request, *args, **kwargs): 44 | return self.destroy(request, *args, **kwargs) 45 | -------------------------------------------------------------------------------- /account/api/serializers.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.contrib.auth.password_validation import validate_password 3 | from rest_framework import serializers 4 | from rest_framework.serializers import ModelSerializer, Serializer 5 | 6 | from account.models import Profile 7 | 8 | 9 | class ProfileSerializer(ModelSerializer): 10 | class Meta: 11 | model = Profile 12 | fields = ('id', 'note','twitter') 13 | 14 | class UserSerializer(ModelSerializer): 15 | profile = ProfileSerializer() 16 | 17 | class Meta: 18 | model = User 19 | fields = ('id','first_name','last_name','profile') 20 | 21 | def update(self, instance, validated_data): 22 | profile = validated_data.pop('profile') 23 | profile_serializer = ProfileSerializer(instance.profile, data =profile ) 24 | profile_serializer.is_valid(raise_exception=True) 25 | profile_serializer.save() 26 | return super(UserSerializer, self).update(instance, validated_data) 27 | 28 | # changing password 29 | class ChangePasswordSerializer(Serializer): 30 | old_password = serializers.CharField(required=True) 31 | new_password = serializers.CharField(required=True) 32 | 33 | def validate_new_password(self, value): 34 | validate_password(value) 35 | return value 36 | 37 | 38 | class RegisterSerializer(ModelSerializer): 39 | password = serializers.CharField(write_only=True) 40 | class Meta: 41 | model = User 42 | fields = ('id', 'username', 'password') 43 | 44 | 45 | 46 | def validate(self, attr): 47 | validate_password(attr['password']) 48 | return attr 49 | 50 | def create(self, validated_data): 51 | user = User.objects.create( 52 | username = validated_data['username'] 53 | ) 54 | user.set_password(validated_data['password']) 55 | user.save() 56 | return user 57 | 58 | -------------------------------------------------------------------------------- /post/api/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.filters import SearchFilter, OrderingFilter 2 | from rest_framework.generics import (ListAPIView, 3 | RetrieveAPIView, 4 | DestroyAPIView, 5 | RetrieveUpdateAPIView, 6 | CreateAPIView,) 7 | #Create model mixin 8 | from rest_framework.mixins import DestroyModelMixin 9 | 10 | from account.api.throttles import RegisterThrottle 11 | from post.api.paginations import PostPagination 12 | from post.api.permissions import IsOwner 13 | 14 | from post.api.serializers import PostSerializer, PostUpdateCreateSerializer 15 | from post.models import Post 16 | from rest_framework.permissions import ( 17 | IsAuthenticated, 18 | ) 19 | 20 | 21 | class PostListAPIView(ListAPIView): 22 | #throttle_scope = 'hasan' 23 | serializer_class = PostSerializer 24 | filter_backends = [SearchFilter, OrderingFilter] 25 | search_fields = ['title','content'] 26 | pagination_class = PostPagination 27 | 28 | def get_queryset(self): 29 | queryset = Post.objects.filter(draft=False) 30 | return queryset 31 | 32 | 33 | class PostDetailAPIView(RetrieveAPIView): 34 | queryset = Post.objects.all() 35 | serializer_class = PostSerializer 36 | lookup_field = 'slug' 37 | 38 | 39 | class PostUpdateAPIView(RetrieveUpdateAPIView, DestroyModelMixin): 40 | queryset = Post.objects.all() 41 | serializer_class = PostUpdateCreateSerializer 42 | lookup_field = 'slug' 43 | permission_classes = [IsOwner] 44 | 45 | def perform_update(self, serializer): 46 | serializer.save(modified_by = self.request.user) 47 | 48 | def delete(self, request, *args, **kwargs): 49 | return self.destroy(request, *args, **kwargs) 50 | 51 | class PostCreateAPIView(CreateAPIView): 52 | queryset = Post.objects.all() 53 | serializer_class = PostUpdateCreateSerializer 54 | permission_classes = [IsAuthenticated] 55 | 56 | def perform_create(self, serializer): 57 | serializer.save(user = self.request.user) 58 | 59 | -------------------------------------------------------------------------------- /account/api/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import update_session_auth_hash 2 | from django.contrib.auth.models import User 3 | from rest_framework import status 4 | from rest_framework.generics import RetrieveUpdateAPIView, get_object_or_404, CreateAPIView 5 | from rest_framework.permissions import IsAuthenticated 6 | from rest_framework.response import Response 7 | 8 | from rest_framework.views import APIView 9 | 10 | from account.api.permissions import NotAuthenticated 11 | from account.api.serializers import UserSerializer, ChangePasswordSerializer, RegisterSerializer 12 | from account.api.throttles import RegisterThrottle 13 | 14 | 15 | class ProfileView(RetrieveUpdateAPIView): 16 | permission_classes = (IsAuthenticated,) 17 | serializer_class = UserSerializer 18 | queryset = User.objects.all() 19 | 20 | def get_object(self): 21 | queryset = self.get_queryset() 22 | obj = get_object_or_404(queryset, id = self.request.user.id) 23 | return obj 24 | 25 | def perform_update(self, serializer): 26 | serializer.save(user = self.request.user) 27 | 28 | 29 | class UpdatePassword(APIView): 30 | permission_classes = (IsAuthenticated,) 31 | 32 | def get_object(self): 33 | return self.request.user 34 | 35 | def put(self, request, *args, **kwargs): 36 | self.object = self.request.user 37 | 38 | serializer = ChangePasswordSerializer(data = request.data) 39 | 40 | if serializer.is_valid(): 41 | print(serializer.data) 42 | old_password = serializer.data.get("old_password") 43 | if not self.object.check_password(old_password): 44 | return Response({"old_password": "wrong_password"}, status=status.HTTP_400_BAD_REQUEST) 45 | 46 | self.object.set_password(serializer.data.get("new_password")) 47 | self.object.save() 48 | update_session_auth_hash(request, self.object) 49 | return Response(status = status.HTTP_204_NO_CONTENT) 50 | 51 | return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST) 52 | 53 | 54 | 55 | # Create user 56 | class CreateUserView(CreateAPIView): 57 | throttle_classes = [RegisterThrottle] 58 | model = User.objects.all() 59 | serializer_class = RegisterSerializer 60 | permission_classes = [NotAuthenticated] 61 | 62 | -------------------------------------------------------------------------------- /favourite/tests.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | import json 3 | from rest_framework.test import APITestCase 4 | from django.urls import reverse 5 | 6 | from favourite.models import Favourite 7 | from post.models import Post 8 | 9 | 10 | class FavouriteCreateList(APITestCase): 11 | url = reverse("favourite:list-create") 12 | url_login = reverse("token_obtain_pair") 13 | 14 | def setUp(self): 15 | self.username = "oguzhan" 16 | self.password = "test1234" 17 | self.post = Post.objects.create(title="Başlık", content="İçerik") 18 | self.user = User.objects.create_user(username= self.username, password=self.password) 19 | self.test_jwt_authentication() 20 | 21 | def test_jwt_authentication(self): 22 | response = self.client.post(self.url_login, data = {"username": self.username, "password": self.password} ) 23 | self.assertEqual(200, response.status_code) 24 | self.assertTrue("access" in json.loads(response.content)) 25 | self.token = response.data["access"] 26 | self.client.credentials(HTTP_AUTHORIZATION='Bearer '+ self.token) 27 | 28 | #favlama 29 | def test_add_favourite(self): 30 | data = { 31 | "content": "içerik güzel favla", 32 | "user": self.user.id, 33 | "post": self.post.id 34 | } 35 | 36 | response = self.client.post(self.url, data) 37 | self.assertEqual(201, response.status_code) 38 | # user favlarını kontrol etme. 39 | def test_user_favs(self): 40 | self.test_add_favourite() 41 | respose = self.client.get(self.url) 42 | self.assertTrue(len(json.loads(respose.content)["results"]) 43 | == Favourite.objects.filter(user=self.user).count()) 44 | 45 | 46 | class FavouriteUpdateDelete(APITestCase): 47 | login_url = reverse("token_obtain_pair") 48 | 49 | def setUp(self): 50 | self.username = "oguzhan" 51 | self.password = "test1234" 52 | self.user = User.objects.create_user(username=self.username, password=self.password) 53 | self.user2 = User.objects.create_user(username="oguzhan3", password=self.password) 54 | self.post = Post.objects.create(title="Başlık", content="İçerik") 55 | self.favourite = Favourite.objects.create(content="deneme", post=self.post, user=self.user) 56 | self.url = reverse("favourite:update-delete", kwargs={"pk": self.favourite.pk}) 57 | self.test_jwt_authentication() 58 | 59 | def test_jwt_authentication(self, username="oguzhan", password="test1234"): 60 | response = self.client.post(self.login_url, data = {"username": username, "password": password} ) 61 | self.assertEqual(200, response.status_code) 62 | self.assertTrue("access" in json.loads(response.content)) 63 | self.token = response.data["access"] 64 | self.client.credentials(HTTP_AUTHORIZATION='Bearer '+ self.token) 65 | 66 | # silme işlemi yapalım 67 | def test_fav_delete(self): 68 | response = self.client.delete(self.url) 69 | self.assertEqual(204, response.status_code) 70 | # başkası benim yerime silememeli 71 | def test_fav_delete_different_user(self): 72 | self.test_jwt_authentication("oguzhan3") 73 | response = self.client.delete(self.url) 74 | self.assertEqual(403, response.status_code) 75 | 76 | #düzenleme işlemi 77 | def test_fav_update(self): 78 | data = { 79 | "content": "içerik 123", 80 | } 81 | 82 | response = self.client.put(self.url, data) 83 | self.assertEqual(200, response.status_code) 84 | self.assertTrue(Favourite.objects.get(id=self.favourite.id).content == data["content"]) 85 | 86 | # başkası benim adıma işlem yaparsa. 87 | def test_fav_update_different_user(self): 88 | self.test_jwt_authentication("oguzhan3") 89 | data = { 90 | "content": "isdfsdf", 91 | "user": self.user2.id 92 | } 93 | 94 | response = self.client.put(self.url, data) 95 | self.assertTrue(403, response.status_code) 96 | 97 | # giriş yapmadan link görülemez. 98 | def test_unautherazation(self): 99 | self.client.credentials() 100 | response = self.client.get(self.url) 101 | self.assertEqual(401, response.status_code) 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /blog/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for blog project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.2.3. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.2/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | from datetime import timedelta 17 | 18 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 19 | 20 | 21 | # Quick-start development settings - unsuitable for production 22 | # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ 23 | 24 | # SECURITY WARNING: keep the secret key used in production secret! 25 | SECRET_KEY = '6z&%#bl^1_q_5ehdojkb$9-g4s$h(+3p9x5=98$h71bbt3x1g&' 26 | 27 | # SECURITY WARNING: don't run with debug turned on in production! 28 | DEBUG = True 29 | 30 | ALLOWED_HOSTS = [] 31 | 32 | REST_FRAMEWORK = { 33 | 'DEFAULT_AUTHENTICATION_CLASSES': [ 34 | 'rest_framework_simplejwt.authentication.JWTAuthentication', 35 | 'rest_framework.authentication.SessionAuthentication' 36 | ], 37 | 'DEFAULT_THROTTLE_CLASSES': ( 38 | 'rest_framework.throttling.ScopedRateThrottle', 39 | ), 40 | 'DEFAULT_THROTTLE_RATES': { 41 | 'registerthrottle': '15/hour', 42 | #'hasan' : '5/hour' 43 | } 44 | } 45 | 46 | SIMPLE_JWT = { 47 | 'ACCESS_TOKEN_LIFETIME' : timedelta(minutes=15) 48 | } 49 | 50 | # Application definition 51 | 52 | INSTALLED_APPS = [ 53 | 'django.contrib.admin', 54 | 'django.contrib.auth', 55 | 'django.contrib.contenttypes', 56 | 'django.contrib.sessions', 57 | 'django.contrib.messages', 58 | 'django.contrib.staticfiles', 59 | 'rest_framework', 60 | 'post', 61 | 'comment', 62 | 'favourite', 63 | 'account', 64 | ] 65 | 66 | 67 | 68 | MIDDLEWARE = [ 69 | 'django.middleware.security.SecurityMiddleware', 70 | 'django.contrib.sessions.middleware.SessionMiddleware', 71 | 'django.middleware.common.CommonMiddleware', 72 | 'django.middleware.csrf.CsrfViewMiddleware', 73 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 74 | 'django.contrib.messages.middleware.MessageMiddleware', 75 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 76 | ] 77 | 78 | ROOT_URLCONF = 'blog.urls' 79 | 80 | TEMPLATES = [ 81 | { 82 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 83 | 'DIRS': [], 84 | 'APP_DIRS': True, 85 | 'OPTIONS': { 86 | 'context_processors': [ 87 | 'django.template.context_processors.debug', 88 | 'django.template.context_processors.request', 89 | 'django.contrib.auth.context_processors.auth', 90 | 'django.contrib.messages.context_processors.messages', 91 | ], 92 | }, 93 | }, 94 | ] 95 | 96 | WSGI_APPLICATION = 'blog.wsgi.application' 97 | 98 | 99 | # Database 100 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases 101 | 102 | DATABASES = { 103 | 'default': { 104 | 'ENGINE': 'django.db.backends.sqlite3', 105 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 106 | } 107 | } 108 | 109 | 110 | # Password validation 111 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators 112 | 113 | AUTH_PASSWORD_VALIDATORS = [ 114 | { 115 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 116 | }, 117 | { 118 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 119 | }, 120 | { 121 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 122 | }, 123 | { 124 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 125 | }, 126 | ] 127 | 128 | 129 | # Internationalization 130 | # https://docs.djangoproject.com/en/2.2/topics/i18n/ 131 | 132 | LANGUAGE_CODE = 'en-us' 133 | 134 | TIME_ZONE = 'UTC' 135 | 136 | USE_I18N = True 137 | 138 | USE_L10N = True 139 | 140 | USE_TZ = True 141 | 142 | 143 | # Static files (CSS, JavaScript, Images) 144 | # https://docs.djangoproject.com/en/2.2/howto/static-files/ 145 | 146 | STATIC_URL = '/static/' 147 | MEDIA_URL = '/media/' 148 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 149 | 150 | -------------------------------------------------------------------------------- /post/tests.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | import json 3 | from rest_framework.test import APITestCase 4 | from django.urls import reverse 5 | 6 | from post.models import Post 7 | 8 | 9 | class PostCreateList(APITestCase): 10 | url_create = reverse("post:create") 11 | url_list = reverse("post:list") 12 | url_login = reverse("token_obtain_pair") 13 | 14 | def setUp(self): 15 | self.username = "oguzhan" 16 | self.password = "test1234" 17 | self.user = User.objects.create_user(username= self.username, password=self.password) 18 | self.test_jwt_authentication() 19 | 20 | def test_jwt_authentication(self): 21 | response = self.client.post(self.url_login, data = {"username": self.username, "password": self.password} ) 22 | self.assertEqual(200, response.status_code) 23 | self.assertTrue("access" in json.loads(response.content)) 24 | self.token = response.data["access"] 25 | self.client.credentials(HTTP_AUTHORIZATION='Bearer '+ self.token) 26 | 27 | def test_add_new_post(self): 28 | data = { 29 | 'content': 'icerik', 30 | 'title' :'baslik' 31 | } 32 | response = self.client.post(self.url_create, data) 33 | self.assertEqual(201, response.status_code) 34 | 35 | def test_add_new_post_unauthorization(self): 36 | self.client.credentials() 37 | data = { 38 | 'content': 'icerik', 39 | 'title' :'baslik' 40 | } 41 | response = self.client.post(self.url_create, data) 42 | self.assertEqual(401, response.status_code) 43 | 44 | # post list 45 | def test_posts(self): 46 | self.test_add_new_post() 47 | response = self.client.get(self.url_list) 48 | self.assertTrue(len(json.loads(response.content)["results"]) == Post.objects.all().count()) 49 | 50 | class PostUpdateDelete(APITestCase): 51 | login_url = reverse("token_obtain_pair") 52 | 53 | def setUp(self): 54 | self.username = "oguzhan" 55 | self.password = "test1234" 56 | self.user = User.objects.create_user(username=self.username, password=self.password) 57 | self.user2 = User.objects.create_user(username="oguzhan3", password=self.password) 58 | self.post = Post.objects.create(title="Başlık", content="İçerik") 59 | self.url = reverse("post:update", kwargs={"slug": self.post.slug}) 60 | self.url_detail = reverse("post:detail", kwargs={"slug": self.post.slug}) 61 | self.test_jwt_authentication() 62 | 63 | def test_jwt_authentication(self, username="oguzhan", password="test1234"): 64 | response = self.client.post(self.login_url, data={"username": username, "password": password}) 65 | self.assertEqual(200, response.status_code) 66 | self.assertTrue("access" in json.loads(response.content)) 67 | self.token = response.data["access"] 68 | self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + self.token) 69 | # silme işlemi 70 | def test_post_delete(self): 71 | response = self.client.delete(self.url) 72 | self.assertEqual(204, response.status_code) 73 | 74 | # farklı kullanıcı benim verilerimi silmek isterse 75 | def test_post_delete_different_user(self): 76 | self.test_jwt_authentication("oguzhan3") 77 | response = self.client.delete(self.url) 78 | self.assertEqual(403, response.status_code) 79 | 80 | # veri günelleme 81 | def test_post_update(self): 82 | data = { 83 | 'content': 'icerik', 84 | 'title': 'denme baslik' 85 | } 86 | 87 | response = self.client.put(self.url, data) 88 | self.assertEqual(200, response.status_code) 89 | self.assertTrue(Post.objects.get(id=self.post.id).content == data["content"]) 90 | 91 | 92 | # benim verimi başkası güncelleyemez. 93 | def test_post_update_different_user(self): 94 | self.test_jwt_authentication("oguzhan3") 95 | data = { 96 | 'content': 'icerik', 97 | 'title': 'denme baslik' 98 | } 99 | 100 | response = self.client.put(self.url, data) 101 | self.assertEqual(403, response.status_code) 102 | self.assertFalse(Post.objects.get(id=self.post.id).content == data["content"]) 103 | 104 | # giriş yapmamış kullanıcı bu siteye erişemez 105 | def test_unauthorization(self): 106 | self.client.credentials() 107 | response = self.client.get(self.url) 108 | self.assertEqual(401, response.status_code) -------------------------------------------------------------------------------- /comment/tests.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from rest_framework.test import APITestCase 3 | from django.urls import reverse 4 | 5 | from comment.models import Comment 6 | from post.models import Post 7 | import json 8 | 9 | class CommentCreate(APITestCase): 10 | login_url = reverse("token_obtain_pair") 11 | def setUp(self): 12 | self.url = reverse("comment:create") 13 | self.url_list = reverse("comment:list") 14 | self.username = "oguzhan1234" 15 | self.password = "sifre1234" 16 | self.post = Post.objects.create(title="deneme", content="içerik") 17 | self.user = User.objects.create_user(username=self.username, password=self.password) 18 | self.parent_comment = Comment.objects.create(content="içerik yorum", user=self.user, post=self.post) 19 | self.test_jwt_authentication() 20 | 21 | def test_jwt_authentication(self): 22 | response = self.client.post(self.login_url, data={"username": "oguzhan1234", "password": "sifre1234"}) 23 | self.assertEqual(200, response.status_code) 24 | self.assertTrue("access" in json.loads(response.content)) 25 | self.token = response.data["access"] 26 | self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + self.token) 27 | 28 | # create comment 29 | def test_create_comment(self): 30 | data = { 31 | "content": "yorum gelsin", 32 | "user": self.user.id, 33 | "post": self.post.id, 34 | "parent": "" 35 | } 36 | response = self.client.post(self.url, data) 37 | self.assertEqual(201, response.status_code) 38 | 39 | # create child comment 40 | def test_create_child_comment(self): 41 | data = { 42 | "content": "yorum gelsin", 43 | "user": self.user.id, 44 | "post": self.post.id, 45 | "parent": self.parent_comment.id 46 | } 47 | response = self.client.post(self.url, data) 48 | self.assertEqual(201, response.status_code) 49 | 50 | # test comment list 51 | def test_comment_list(self): 52 | self.test_create_comment() 53 | response = self.client.get(self.url_list, {'q': self.post.id}) 54 | self.assertTrue(response.data["count"] == Comment.objects.filter(post = self.post).count()) 55 | 56 | 57 | class CommentUpdateDeleteTest(APITestCase): 58 | login_url = reverse("token_obtain_pair") 59 | def setUp(self): 60 | self.username = "oguzhan1234" 61 | self.password = "sifre1234" 62 | self.post = Post.objects.create(title="deneme", content="içerik") 63 | self.user = User.objects.create_user(username=self.username, password=self.password) 64 | self.user2 = User.objects.create_user(username="oguzhan12345", password=self.password) 65 | self.comment = Comment.objects.create(content="içerik yorum", user=self.user, post=self.post) 66 | self.url = reverse("comment:update", kwargs={'pk': self.comment.pk}) 67 | self.test_jwt_authentication() 68 | 69 | def test_jwt_authentication(self, username = "oguzhan1234", password="sifre1234"): 70 | response = self.client.post(self.login_url, data={"username": username, "password": password}) 71 | self.assertEqual(200, response.status_code) 72 | self.assertTrue("access" in json.loads(response.content)) 73 | self.token = response.data["access"] 74 | self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + self.token) 75 | 76 | # yorum silme işlemi 77 | def test_delete_comment(self): 78 | response = self.client.delete(self.url) 79 | self.assertEqual(204, response.status_code) 80 | self.assertFalse(Comment.objects.filter(pk = self.comment.pk).exists()) 81 | 82 | # kendimizin olmayan bir değeri silmek 83 | def test_delete_other_user(self): 84 | self.test_jwt_authentication("oguzhan12345") 85 | response = self.client.delete(self.url) 86 | self.assertEqual(403, response.status_code) 87 | self.assertTrue(Comment.objects.get(pk=self.comment.pk)) 88 | 89 | #kendimizin kaydına update edelim 90 | def test_update_comment(self): 91 | response = self.client.put(self.url, data= {"content": "icerik"}) 92 | self.assertEqual(200, response.status_code) 93 | self.assertEqual(Comment.objects.get(pk=self.comment.id).content,"icerik") 94 | 95 | def test_update_comment_other_user(self): 96 | self.test_jwt_authentication("oguzhan12345") 97 | response = self.client.put(self.url, data={"content": "icerik"}) 98 | self.assertEqual(403, response.status_code) 99 | self.assertNotEqual(Comment.objects.get(pk=self.comment.id).content, "icerik") 100 | 101 | def test_unauthorization(self): 102 | self.client.credentials() 103 | response = self.client.get(self.url) 104 | self.assertEqual(401, response.status_code) 105 | -------------------------------------------------------------------------------- /account/tests.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from rest_framework.test import APITestCase 3 | from django.urls import reverse 4 | 5 | # doğru veriler ile kayıt işlemi yap. 6 | # şifre invalid olabilir. 7 | # kullanıcı adı kullanılmış olabilir. 8 | # üye girişi yaptıysak o sayfa gözükmemeli 9 | # token ile giriş işlemi yapıldığında 403 hatası 10 | from rest_framework.utils import json 11 | 12 | 13 | class UserRegistrationTestCase(APITestCase): 14 | url = reverse("account:register") 15 | url_login = reverse("token_obtain_pair") 16 | def test_user_registration(self): 17 | """ 18 | Doğru veriler ile kayıt işlemi. 19 | """ 20 | 21 | data = { 22 | "username" : "oguzhantest", 23 | "password": "deneme123" 24 | } 25 | 26 | response = self.client.post(self.url, data) 27 | self.assertEqual(201, response.status_code) 28 | 29 | def test_user_invalid_password(self): 30 | """ 31 | invalid password verisi ile kayıt işlemi. 32 | """ 33 | 34 | data = { 35 | "username" : "oguzhantest", 36 | "password": "1" 37 | } 38 | 39 | response = self.client.post(self.url, data) 40 | self.assertEqual(400, response.status_code) 41 | 42 | def test_unique_name(self): 43 | """ 44 | benzersiz isim testi. 45 | """ 46 | self.test_user_registration() 47 | data = { 48 | "username" : "oguzhantest", 49 | "password": "asıduaıduoasdu1" 50 | } 51 | 52 | response = self.client.post(self.url, data) 53 | self.assertEqual(400, response.status_code) 54 | 55 | def test_user_authenticated_registration(self): 56 | """ 57 | session ile giriş yapmış kullanıcı sayfayı görememeli. 58 | """ 59 | self.test_user_registration() 60 | self.client.login(username = 'oguzhantest', password = 'deneme123') 61 | response = self.client.get(self.url) 62 | self.assertEqual(403, response.status_code) 63 | 64 | 65 | def test_user_authenticated_token_registration(self): 66 | """ 67 | token ile giriş yapmış kullanıcı sayfayı görememeli. 68 | """ 69 | self.test_user_registration() 70 | 71 | data = { 72 | "username": "oguzhantest", 73 | "password": "deneme123" 74 | } 75 | response = self.client.post(self.url_login, data) 76 | self.assertEqual(200 , response.status_code) 77 | token = response.data["access"] 78 | self.client.credentials(HTTP_AUTHORIZATION= 'Bearer '+ token) 79 | response_2 = self.client.get(self.url) 80 | self.assertEqual(403, response_2.status_code) 81 | 82 | 83 | class UserLogin(APITestCase): 84 | url_login = reverse("token_obtain_pair") 85 | 86 | def setUp(self): 87 | self.username = "oguzhan" 88 | self.password = "sifre1234" 89 | self.user = User.objects.create_user(username = self.username, password=self.password) 90 | 91 | def test_user_token(self): 92 | response = self.client.post(self.url_login, {"username": "oguzhan", "password":"sifre1234"}) 93 | self.assertEqual(200, response.status_code) 94 | self.assertTrue("access" in json.loads(response.content)) 95 | 96 | def test_user_invalid_data(self): 97 | response = self.client.post(self.url_login, {"username": "asasdzxczxc", "password":"sifre1234"}) 98 | self.assertEqual(401, response.status_code) 99 | 100 | def test_user_empty_data(self): 101 | response = self.client.post(self.url_login, {"username": "", "password":""}) 102 | self.assertEqual(400, response.status_code) 103 | 104 | 105 | class UserPasswordChange(APITestCase): 106 | url = reverse("account:change-password") 107 | url_login = reverse("token_obtain_pair") 108 | def setUp(self): 109 | self.username = "oguzhan" 110 | self.password = "sifre1234" 111 | self.user = User.objects.create_user(username = self.username, password=self.password) 112 | 113 | def login_with_token(self): 114 | data = { 115 | "username" : "oguzhan", 116 | "password" : "sifre1234" 117 | } 118 | response = self.client.post(self.url_login, data) 119 | self.assertEqual(200, response.status_code) 120 | token = response.data["access"] 121 | self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) 122 | 123 | # oturum açılmadan girildiğinde hata 124 | def test_is_authenticated_user(self): 125 | response = self.client.get(self.url) 126 | self.assertEqual(401, response.status_code) 127 | 128 | 129 | def test_with_valid_informations(self): 130 | self.login_with_token() 131 | data = { 132 | "old_password": "sifre1234", 133 | "new_password": "asdasdas123456" 134 | } 135 | response = self.client.put(self.url, data) 136 | self.assertEqual(204, response.status_code) 137 | 138 | def test_with_wrong_informations(self): 139 | self.login_with_token() 140 | data = { 141 | "old_password": "asdasd", 142 | "new_password": "asdasdas123456" 143 | } 144 | response = self.client.put(self.url, data) 145 | self.assertEqual(400, response.status_code) 146 | 147 | def test_with_empty_informations(self): 148 | self.login_with_token() 149 | data = { 150 | "old_password": "", 151 | "new_password": "" 152 | } 153 | response = self.client.put(self.url, data) 154 | self.assertEqual(400, response.status_code) 155 | 156 | class UserProfileUpdate(APITestCase): 157 | url = reverse("account:me") 158 | url_login = reverse("token_obtain_pair") 159 | 160 | def setUp(self): 161 | self.username = "oguzhan" 162 | self.password = "sifre1234" 163 | self.user = User.objects.create_user(username=self.username, password=self.password) 164 | 165 | def login_with_token(self): 166 | data = { 167 | "username": "oguzhan", 168 | "password": "sifre1234" 169 | } 170 | response = self.client.post(self.url_login, data) 171 | self.assertEqual(200, response.status_code) 172 | token = response.data["access"] 173 | self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + token) 174 | 175 | # oturum açılmadan girildiğinde hata 176 | def test_is_authenticated_user(self): 177 | response = self.client.get(self.url) 178 | self.assertEqual(401, response.status_code) 179 | # valid informations 180 | def test_with_valid_informations(self): 181 | self.login_with_token() 182 | data = { 183 | "id" : 1, 184 | "first_name": "", 185 | "last_name": "", 186 | "profile": { 187 | "id": 1, 188 | "note": "", 189 | "twitter": "asdas" 190 | } 191 | } 192 | 193 | response = self.client.put(self.url, data, format = 'json') 194 | self.assertEqual(200, response.status_code) 195 | self.assertEqual(json.loads(response.content), data) 196 | 197 | def test_with_empty_informations(self): 198 | self.login_with_token() 199 | data = { 200 | "id": 1, 201 | "first_name": "", 202 | "last_name": "", 203 | "profile": { 204 | "id": 1, 205 | "note": "", 206 | "twitter": "" 207 | } 208 | } 209 | response = self.client.put(self.url, data, format='json') 210 | self.assertEqual(200, response.status_code) --------------------------------------------------------------------------------