├── .gitignore ├── A ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── README.md ├── accounts ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── tests.py ├── urls.py └── views.py ├── db.sqlite3 ├── home ├── __init__.py ├── admin.py ├── apps.py ├── custom_relational_fields.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_question_answer.py │ └── __init__.py ├── models.py ├── serializers.py ├── templates │ └── home │ │ └── home.html ├── tests.py ├── urls.py └── views.py ├── manage.py ├── permissions.py └── templates ├── base.html └── inc ├── messages.html └── navbar.html /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /A/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirbigg/django-rest-framework/86a604d283d55f7e7e651d2cb8141edc87f9cd52/A/__init__.py -------------------------------------------------------------------------------- /A/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for A project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'A.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /A/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for A project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.0. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.0/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'django-insecure-h&!eq@$bwhrpi^1s!y=@4xlh1kve&2a(u@-#a(cd3e%vwdhh^d' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'home.apps.HomeConfig', 41 | 'accounts.apps.AccountsConfig', 42 | 'rest_framework', 43 | 'drf_spectacular', 44 | ] 45 | 46 | MIDDLEWARE = [ 47 | 'django.middleware.security.SecurityMiddleware', 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'django.middleware.common.CommonMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | ] 55 | 56 | ROOT_URLCONF = 'A.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': [BASE_DIR / 'templates'], 62 | 'APP_DIRS': True, 63 | 'OPTIONS': { 64 | 'context_processors': [ 65 | 'django.template.context_processors.debug', 66 | 'django.template.context_processors.request', 67 | 'django.contrib.auth.context_processors.auth', 68 | 'django.contrib.messages.context_processors.messages', 69 | ], 70 | }, 71 | }, 72 | ] 73 | 74 | WSGI_APPLICATION = 'A.wsgi.application' 75 | 76 | 77 | # Database 78 | # https://docs.djangoproject.com/en/4.0/ref/settings/#databases 79 | 80 | DATABASES = { 81 | 'default': { 82 | 'ENGINE': 'django.db.backends.sqlite3', 83 | 'NAME': BASE_DIR / 'db.sqlite3', 84 | } 85 | } 86 | 87 | 88 | # Password validation 89 | # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators 90 | 91 | AUTH_PASSWORD_VALIDATORS = [ 92 | { 93 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 94 | }, 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 100 | }, 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 103 | }, 104 | ] 105 | 106 | 107 | # Internationalization 108 | # https://docs.djangoproject.com/en/4.0/topics/i18n/ 109 | 110 | LANGUAGE_CODE = 'en-us' 111 | 112 | TIME_ZONE = 'UTC' 113 | 114 | USE_I18N = True 115 | 116 | USE_TZ = True 117 | 118 | 119 | # Static files (CSS, JavaScript, Images) 120 | # https://docs.djangoproject.com/en/4.0/howto/static-files/ 121 | 122 | STATIC_URL = 'static/' 123 | 124 | # Default primary key field type 125 | # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field 126 | 127 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 128 | 129 | # REST_FRAMEWORK 130 | REST_FRAMEWORK = { 131 | 'DEFAULT_AUTHENTICATION_CLASSES': [ 132 | 'rest_framework_simplejwt.authentication.JWTAuthentication', 133 | ], 134 | 'DEFAULT_THROTTLE_CLASSES': [ 135 | 'rest_framework.throttling.ScopedRateThrottle', 136 | ], 137 | 'DEFAULT_THROTTLE_RATES': { 138 | 'questions': '5/minute' 139 | }, 140 | 'DEFAULT_RENDERER_CLASSES': [ 141 | 'rest_framework.renderers.JSONRenderer', 142 | ] 143 | } 144 | 145 | SPECTACULAR_SETTINGS = { 146 | 'TITLE': 'Shop rest', 147 | 'DESCRIPTION': 'Your project description', 148 | 'VERSION': '1.0.0', 149 | } 150 | -------------------------------------------------------------------------------- /A/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView 4 | 5 | 6 | urlpatterns = [ 7 | path('admin/', admin.site.urls), 8 | path('', include('home.urls', namespace='home')), 9 | path('accounts/', include('accounts.urls', namespace='accounts')), 10 | path('schema/', SpectacularAPIView.as_view(), name='schema'), 11 | path('schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), 12 | path('schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), 13 | ] 14 | -------------------------------------------------------------------------------- /A/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for A 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/4.0/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', 'A.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## This repository belongs to drf training on [Mongrad](https://www.mongard.ir) site 2 | 3 | ### Course information 4 | 5 | Course Link : [DRF](https://www.mongard.ir/courses/drf/) 6 | ### Things learned : 7 | 1. response 8 | 2. request 9 | 3. serializer 10 | 4. user register 11 | 5. custom validators 12 | 6. model serializer 13 | 7. overriding create 14 | 8. status codes 15 | 9. authentication 16 | 10. permissions 17 | 11. read 18 | 12. update/delete 19 | 13. method fields 20 | 14. custom permissions 21 | 15. serializer relations 22 | 16. viewset 23 | 17. throttling 24 | 18. jwt 25 | 19. swagger 26 | 20. renderer/parser 27 | 21. pagination 28 | 22. metadata -------------------------------------------------------------------------------- /accounts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirbigg/django-rest-framework/86a604d283d55f7e7e651d2cb8141edc87f9cd52/accounts/__init__.py -------------------------------------------------------------------------------- /accounts/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /accounts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountsConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'accounts' 7 | -------------------------------------------------------------------------------- /accounts/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirbigg/django-rest-framework/86a604d283d55f7e7e651d2cb8141edc87f9cd52/accounts/migrations/__init__.py -------------------------------------------------------------------------------- /accounts/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /accounts/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from django.contrib.auth.models import User 3 | 4 | 5 | def clean_email(value): 6 | if 'admin' in value: 7 | raise serializers.ValidationError('admin cant be in email') 8 | 9 | class UserRegisterSerializer(serializers.ModelSerializer): 10 | password2 = serializers.CharField(write_only=True, required=True) 11 | 12 | class Meta: 13 | model = User 14 | fields = ('username', 'email', 'password', 'password2') 15 | extra_kwargs = { 16 | 'password': {'write_only':True}, 17 | 'email': {'validators': (clean_email,)} 18 | } 19 | 20 | def create(self, validated_data): 21 | del validated_data['password2'] 22 | return User.objects.create_user(**validated_data) 23 | 24 | def validate_username(self, value): 25 | if value == 'admin': 26 | raise serializers.ValidationError('username cant be `admin`') 27 | return value 28 | 29 | def validate(self, data): 30 | if data['password'] != data['password2']: 31 | raise serializers.ValidationError('passwords must match') 32 | return data 33 | 34 | 35 | class UserSerializer(serializers.ModelSerializer): 36 | class Meta: 37 | model = User 38 | fields = '__all__' 39 | -------------------------------------------------------------------------------- /accounts/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /accounts/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | from rest_framework import routers 4 | from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView 5 | 6 | 7 | app_name = 'accounts' 8 | urlpatterns = [ 9 | path('register/', views.UserRegister.as_view()), 10 | path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), 11 | path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), 12 | ] 13 | 14 | router = routers.SimpleRouter() 15 | router.register('user', views.UserViewSet) 16 | urlpatterns += router.urls 17 | -------------------------------------------------------------------------------- /accounts/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.views import APIView 2 | from rest_framework.response import Response 3 | from .serializers import UserRegisterSerializer, UserSerializer 4 | from rest_framework import status 5 | from rest_framework import viewsets 6 | from rest_framework.permissions import IsAuthenticated 7 | from django.contrib.auth.models import User 8 | from django.shortcuts import get_object_or_404 9 | 10 | 11 | class UserRegister(APIView): 12 | def post(self, request): 13 | ser_data = UserRegisterSerializer(data=request.POST) 14 | if ser_data.is_valid(): 15 | ser_data.create(ser_data.validated_data) 16 | return Response(ser_data.data, status=status.HTTP_201_CREATED) 17 | return Response(ser_data.errors, status=status.HTTP_400_BAD_REQUEST) 18 | 19 | 20 | class UserViewSet(viewsets.ViewSet): 21 | permission_classes = [IsAuthenticated,] 22 | queryset = User.objects.all() 23 | 24 | def list(self, request): 25 | srz_data = UserSerializer(instance=self.queryset, many=True) 26 | return Response(data=srz_data.data) 27 | 28 | def retrieve(self, request, pk=None): 29 | user = get_object_or_404(self.queryset, pk=pk) 30 | srz_data = UserSerializer(instance=user) 31 | return Response(data=srz_data.data) 32 | 33 | def partial_update(self, request, pk=None): 34 | user = get_object_or_404(self.queryset, pk=pk) 35 | if user != request.user: 36 | return Response({'permission denied': 'you are not the owner'}) 37 | srz_data = UserSerializer(instance=user, data=request.POST, partial=True) 38 | if srz_data.is_valid(): 39 | srz_data.save() 40 | return Response(data=srz_data.data) 41 | return Response(data=srz_data.errors) 42 | 43 | def destroy(self, request, pk=None): 44 | user = get_object_or_404(self.queryset, pk=pk) 45 | if user != request.user: 46 | return Response({'permission denied': 'you are not the owner'}) 47 | user.is_active = False 48 | user.save() 49 | return Response({'message':'user deactivated'}) 50 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirbigg/django-rest-framework/86a604d283d55f7e7e651d2cb8141edc87f9cd52/db.sqlite3 -------------------------------------------------------------------------------- /home/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirbigg/django-rest-framework/86a604d283d55f7e7e651d2cb8141edc87f9cd52/home/__init__.py -------------------------------------------------------------------------------- /home/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Person, Question, Answer 3 | 4 | 5 | admin.site.register(Person) 6 | admin.site.register(Question) 7 | admin.site.register(Answer) 8 | -------------------------------------------------------------------------------- /home/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class HomeConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'home' 7 | -------------------------------------------------------------------------------- /home/custom_relational_fields.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | 4 | class UserEmailNameRelationalField(serializers.RelatedField): 5 | def to_representation(self, value): 6 | return f'{value.username} - {value.email}' 7 | -------------------------------------------------------------------------------- /home/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.3 on 2022-04-01 09:47 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Person', 16 | fields=[ 17 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=30)), 19 | ('age', models.PositiveSmallIntegerField()), 20 | ('email', models.EmailField(max_length=254)), 21 | ], 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /home/migrations/0002_question_answer.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.3 on 2022-04-14 07:25 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 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('home', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Question', 18 | fields=[ 19 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('title', models.CharField(max_length=200)), 21 | ('slug', models.SlugField(max_length=200)), 22 | ('body', models.TextField()), 23 | ('created', models.DateTimeField(auto_now_add=True)), 24 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to=settings.AUTH_USER_MODEL)), 25 | ], 26 | ), 27 | migrations.CreateModel( 28 | name='Answer', 29 | fields=[ 30 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 31 | ('body', models.TextField()), 32 | ('created', models.DateTimeField(auto_now_add=True)), 33 | ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='home.question')), 34 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to=settings.AUTH_USER_MODEL)), 35 | ], 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /home/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirbigg/django-rest-framework/86a604d283d55f7e7e651d2cb8141edc87f9cd52/home/migrations/__init__.py -------------------------------------------------------------------------------- /home/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | 4 | 5 | class Person(models.Model): 6 | name = models.CharField(max_length=30) 7 | age = models.PositiveSmallIntegerField() 8 | email = models.EmailField() 9 | 10 | def __str__(self): 11 | return self.name 12 | 13 | 14 | class Question(models.Model): 15 | user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='questions') 16 | title = models.CharField(max_length=200) 17 | slug = models.SlugField(max_length=200) 18 | body = models.TextField() 19 | created = models.DateTimeField(auto_now_add=True) 20 | 21 | def __str__(self): 22 | return f'{self.user} - {self.title[:20]}' 23 | 24 | 25 | class Answer(models.Model): 26 | user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='answers') 27 | question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='answers') 28 | body = models.TextField() 29 | created = models.DateTimeField(auto_now_add=True) 30 | 31 | def __str__(self): 32 | return f'{self.user} - {self.question.title[:20]}' 33 | -------------------------------------------------------------------------------- /home/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from .models import Question, Answer 3 | from .custom_relational_fields import UserEmailNameRelationalField 4 | 5 | 6 | class PersonSerializer(serializers.Serializer): 7 | id = serializers.IntegerField() 8 | name = serializers.CharField() 9 | age = serializers.IntegerField() 10 | email = serializers.EmailField() 11 | 12 | 13 | class QuestionSerializer(serializers.ModelSerializer): 14 | answers = serializers.SerializerMethodField() 15 | user = UserEmailNameRelationalField(read_only=True) 16 | 17 | class Meta: 18 | model = Question 19 | fields = '__all__' 20 | 21 | def get_answers(self, obj): 22 | result = obj.answers.all() 23 | return AnswerSerializer(instance=result, many=True).data 24 | 25 | 26 | class AnswerSerializer(serializers.ModelSerializer): 27 | class Meta: 28 | model = Answer 29 | fields = '__all__' 30 | -------------------------------------------------------------------------------- /home/templates/home/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block content %} 4 |

Home

5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /home/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /home/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | 5 | app_name = 'home' 6 | urlpatterns = [ 7 | path('', views.Home.as_view(), name='home'), # endpoint 8 | path('questions/', views.QuestionListView.as_view()), 9 | path('question/create/', views.QuestionCreateView.as_view()), 10 | path('question/update//', views.QuestionUpdateView.as_view()), 11 | path('question/delete//', views.QuestionDeleteView.as_view()), 12 | ] -------------------------------------------------------------------------------- /home/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.views import APIView 2 | from rest_framework.response import Response 3 | from .models import Person, Question 4 | from .serializers import PersonSerializer, QuestionSerializer 5 | from rest_framework.permissions import AllowAny, IsAuthenticated 6 | from rest_framework import status 7 | from permissions import IsOwnerOrReadOnly 8 | 9 | 10 | class Home(APIView): 11 | permission_classes = [IsAuthenticated,] 12 | 13 | def get(self, request): 14 | persons = Person.objects.all() 15 | ser_data = PersonSerializer(instance=persons, many=True) 16 | return Response(data=ser_data.data) 17 | 18 | 19 | class QuestionListView(APIView): 20 | throttle_scope = 'questions' 21 | 22 | def get(self, request): 23 | questions = Question.objects.all() 24 | srz_data = QuestionSerializer(instance=questions, many=True).data 25 | return Response(srz_data, status=status.HTTP_200_OK) 26 | 27 | 28 | class QuestionCreateView(APIView): 29 | """ 30 | Create a new question 31 | """ 32 | permission_classes = [IsAuthenticated,] 33 | serializer_class = QuestionSerializer 34 | 35 | def post(self, request): 36 | srz_data = QuestionSerializer(data=request.data) 37 | if srz_data.is_valid(): 38 | srz_data.save() 39 | return Response(srz_data.data, status=status.HTTP_201_CREATED) 40 | return Response(srz_data.errors, status=status.HTTP_400_BAD_REQUEST) 41 | 42 | 43 | class QuestionUpdateView(APIView): 44 | permission_classes = [IsOwnerOrReadOnly,] 45 | 46 | def put(self, request, pk): 47 | question = Question.objects.get(pk=pk) 48 | self.check_object_permissions(request, question) 49 | srz_data = QuestionSerializer(instance=question, data=request.data, partial=True) 50 | if srz_data.is_valid(): 51 | srz_data.save() 52 | return Response(srz_data.data, status=status.HTTP_200_OK) 53 | return Response(srz_data.errors, status=status.HTTP_400_BAD_REQUEST) 54 | 55 | 56 | class QuestionDeleteView(APIView): 57 | permission_classes = [IsOwnerOrReadOnly,] 58 | 59 | def delete(self, request, pk): 60 | question = Question.objects.get(pk=pk) 61 | question.delete() 62 | return Response({'message': 'question deleted'}, status=status.HTTP_200_OK) 63 | -------------------------------------------------------------------------------- /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 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'A.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework.permissions import BasePermission, SAFE_METHODS 2 | 3 | 4 | class IsOwnerOrReadOnly(BasePermission): 5 | message = 'permission denied, you are not the owner' 6 | 7 | def has_permission(self, request, view): 8 | return request.user.is_authenticated and request.user 9 | 10 | def has_object_permission(self, request, view, obj): 11 | if request.method in SAFE_METHODS: 12 | return True 13 | return obj.user == request.user -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Rest 6 | 7 | 14 | 15 | 16 | 17 | {% include 'inc/navbar.html' %} 18 |
19 | {% include 'inc/messages.html' %} 20 | {% block content %} {% endblock %} 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /templates/inc/messages.html: -------------------------------------------------------------------------------- 1 | {% if messages %} 2 | 3 | {% for msg in messages %} 4 |

{{ msg }}

5 | {% endfor %} 6 | 7 | {% endif %} -------------------------------------------------------------------------------- /templates/inc/navbar.html: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------