working on permission is_contributor; checkpoint
This commit is contained in:
parent
776ba21695
commit
3636d4a72b
@ -1,4 +1,4 @@
|
||||
from rest_framework.serializers import ModelSerializer, SerializerMethodField, ValidationError
|
||||
from rest_framework.serializers import ModelSerializer, ValidationError
|
||||
from rest_framework import serializers
|
||||
from authentication.models import User
|
||||
|
||||
@ -50,7 +50,6 @@ class UserRegisterSerializer(ModelSerializer):
|
||||
"""
|
||||
Create and return a new `User` instance, given the validated data.
|
||||
"""
|
||||
#if self.validate(validated_data):
|
||||
user = User.objects.create_user(
|
||||
username=validated_data['username'],
|
||||
email=validated_data['email'],
|
||||
|
@ -133,6 +133,6 @@ REST_FRAMEWORK = {
|
||||
}
|
||||
|
||||
SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(days=10),
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(days=30),
|
||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=30),
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ Including another URLconf
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from authentication.views import (UserView, UserCreateView, PasswordUpdateView)
|
||||
from support.views import ProjectViewSet
|
||||
from support.views import ProjectViewSet, IssueViewSet, CommentViewSet, ContributorViewSet
|
||||
from rest_framework import routers
|
||||
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
|
||||
|
||||
@ -25,6 +25,9 @@ from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
|
||||
router = routers.SimpleRouter()
|
||||
#router.register('user', UserViewSet, basename='user')
|
||||
router.register('project', ProjectViewSet, basename='project')
|
||||
router.register('issue', IssueViewSet, basename='issue')
|
||||
router.register('comment', CommentViewSet, basename='comment')
|
||||
router.register('contributors', ContributorViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
|
@ -2,12 +2,20 @@ from django.contrib import admin
|
||||
from support.models import Project, Issue, Comment, ProjectContributor
|
||||
from authentication.models import User
|
||||
|
||||
class AdminUser:
|
||||
pass
|
||||
class AdminProject(admin.ModelAdmin):
|
||||
list_display = ('id', 'title', 'author', 'contributors')
|
||||
|
||||
@admin.display(description='contributors')
|
||||
def contributors(self, obj):
|
||||
return obj.contributor
|
||||
|
||||
class AdminIssue(admin.ModelAdmin):
|
||||
list_display = ('id', 'title', 'author', 'project')
|
||||
|
||||
|
||||
admin.site.register(User)
|
||||
admin.site.register(Project)
|
||||
admin.site.register(Issue)
|
||||
admin.site.register(Project, AdminProject)
|
||||
admin.site.register(Issue, AdminIssue)
|
||||
admin.site.register(Comment)
|
||||
admin.site.register(ProjectContributor)
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.2.1 on 2025-05-26 18:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('support', '0010_alter_comment_author_alter_issue_author_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='issue',
|
||||
name='priority',
|
||||
field=models.CharField(choices=[('L', 'Low'), ('M', 'Medium'), ('H', 'High')], max_length=15),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='issue',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('ToDo', 'Todo'), ('InProgress', 'Inprogress'), ('Finished', 'Finished')], max_length=15),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='issue',
|
||||
name='tag',
|
||||
field=models.CharField(choices=[('Bug', 'Bug'), ('Feature', 'Feature'), ('Task', 'Task')], max_length=15),
|
||||
),
|
||||
]
|
@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.2.1 on 2025-05-26 18:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('support', '0011_alter_issue_priority_alter_issue_status_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='issue',
|
||||
name='priority',
|
||||
field=models.CharField(choices=[('Low', 'Low'), ('Medium', 'Medium'), ('High', 'High')], max_length=15),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='issue',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('ToDo', 'Todo'), ('In Progress', 'Inprogress'), ('Finished', 'Finished')], max_length=15),
|
||||
),
|
||||
]
|
19
softdesk/support/migrations/0013_alter_issue_project.py
Normal file
19
softdesk/support/migrations/0013_alter_issue_project.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.2.1 on 2025-05-26 19:06
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('support', '0012_alter_issue_priority_alter_issue_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='issue',
|
||||
name='project',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='support.project'),
|
||||
),
|
||||
]
|
19
softdesk/support/migrations/0014_alter_issue_project.py
Normal file
19
softdesk/support/migrations/0014_alter_issue_project.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.2.1 on 2025-05-27 09:30
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('support', '0013_alter_issue_project'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='issue',
|
||||
name='project',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='support.project'),
|
||||
),
|
||||
]
|
@ -45,14 +45,14 @@ class ProjectContributor(models.Model):
|
||||
class Issue(models.Model):
|
||||
|
||||
class Priority(models.TextChoices):
|
||||
LOW = 'L'
|
||||
MEDIUM = 'M'
|
||||
HIGH = 'H'
|
||||
LOW = 'Low'
|
||||
MEDIUM = 'Medium'
|
||||
HIGH = 'High'
|
||||
|
||||
|
||||
class Status(models.TextChoices):
|
||||
TODO = 'ToDo'
|
||||
INPROGRESS = 'InProgress'
|
||||
INPROGRESS = 'In Progress'
|
||||
FINISHED = 'Finished'
|
||||
|
||||
|
||||
@ -65,13 +65,11 @@ class Issue(models.Model):
|
||||
title = models.CharField(max_length=255, verbose_name='title')
|
||||
date_created = models.DateTimeField(auto_now_add=True)
|
||||
description = models.TextField()
|
||||
status = models.CharField(Status.choices, max_length=15)
|
||||
priority = models.CharField(Priority.choices, max_length=15)
|
||||
tag = models.CharField(Tag.choices, max_length=15)
|
||||
status = models.CharField(choices=Status.choices, max_length=15)
|
||||
priority = models.CharField(choices=Priority.choices, max_length=15)
|
||||
tag = models.CharField(choices=Tag.choices, max_length=15)
|
||||
project = models.ForeignKey(Project,
|
||||
null=True,
|
||||
on_delete=models.CASCADE,
|
||||
blank=True)
|
||||
on_delete=models.CASCADE)
|
||||
author = models.ForeignKey(settings.AUTH_USER_MODEL,
|
||||
on_delete=models.DO_NOTHING,
|
||||
related_name='issue_author', null=True)
|
||||
|
@ -1,9 +1,19 @@
|
||||
from rest_framework.permissions import BasePermission
|
||||
|
||||
from support.models import Project
|
||||
|
||||
class IsAuthor(BasePermission):
|
||||
|
||||
def has_object_permission(self, request, view, project):
|
||||
def has_object_permission(self, request, view, object):
|
||||
return bool(request.user
|
||||
and request.user.is_authenticated
|
||||
and request.user==project.author)
|
||||
and request.user == object.author
|
||||
)
|
||||
|
||||
|
||||
class IsContributor(BasePermission):
|
||||
|
||||
def has_object_permission(self, request, view, object):
|
||||
return bool(request.user
|
||||
and request.user.is_authenticated
|
||||
and request.user in object.contributors.all()
|
||||
)
|
@ -1,6 +1,8 @@
|
||||
from rest_framework.serializers import (ModelSerializer,
|
||||
StringRelatedField,
|
||||
SlugRelatedField)
|
||||
SlugRelatedField,
|
||||
SerializerMethodField,
|
||||
ValidationError)
|
||||
from support.models import Project, ProjectContributor, Issue, Comment
|
||||
|
||||
|
||||
@ -11,26 +13,85 @@ class ContributorSerializer(ModelSerializer):
|
||||
fields = ['contributor', 'project', 'data']
|
||||
|
||||
|
||||
class ContributorListSerializer(ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = ProjectContributor
|
||||
fields = ['contributor']
|
||||
|
||||
|
||||
|
||||
class ProjectSerializer(ModelSerializer):
|
||||
|
||||
author = StringRelatedField(many=False)
|
||||
contributors = SlugRelatedField(many=True,
|
||||
read_only='True',
|
||||
slug_field='username')
|
||||
class Meta:
|
||||
model = Project
|
||||
fields = ['id', 'author', 'contributors', 'title', 'type', 'date_created']
|
||||
|
||||
def validate_title(self, value):
|
||||
if Project.objects.filter(title=value).exists():
|
||||
raise ValidationError("Project already exists.")
|
||||
return value
|
||||
|
||||
class ProjectDetailSerializer(ModelSerializer):
|
||||
|
||||
contributors = SlugRelatedField(many=True,
|
||||
read_only='True',
|
||||
slug_field='username')
|
||||
author = StringRelatedField(many=False)
|
||||
issues = SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Project
|
||||
fields = ['id', 'title', 'date_created', 'type', 'description', 'author',
|
||||
'contributors']
|
||||
fields = ['title',
|
||||
'date_created', 'type',
|
||||
'author', 'contributors', 'description', 'issues']
|
||||
|
||||
def get_issues(self, instance):
|
||||
queryset = Issue.objects.filter(project=instance.pk)
|
||||
serializer = IssueSerializer(queryset, many=True)
|
||||
return serializer.data
|
||||
|
||||
class ProjectDetailSerializer(ModelSerializer):
|
||||
pass
|
||||
|
||||
class IssueSerializer(ModelSerializer):
|
||||
|
||||
author = StringRelatedField(many=False)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Issue
|
||||
fields = ['title', 'date_created', 'priority', 'tag', 'status', 'author']
|
||||
fields = ['id', 'title', 'project', 'date_created', 'priority',
|
||||
'tag', 'status', 'author']
|
||||
read_only_field = ['author']
|
||||
|
||||
|
||||
def validate_author(self, data):
|
||||
if Project.objects.filter(contributors=data.author).exists():
|
||||
raise ValidationError("Requestor isn't contributor")
|
||||
return data
|
||||
|
||||
def validate_project(self, data):
|
||||
# if data['user'] not in data['project'].contributors:
|
||||
# raise ValidationError("User must be a contributor to the project")
|
||||
#print(data.project)
|
||||
#if self.context['request'].user not in data.contributors:
|
||||
# raise ValidationError("User must be a contributor to the project")
|
||||
#print(self.get_contributors(data))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class CommentListSerializer(ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = ['title', 'date_created', 'author']
|
||||
|
||||
class CommentDetailSerializer(ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = ['title', 'date_created', 'author', 'description']
|
@ -3,21 +3,33 @@ from rest_framework.serializers import raise_errors_on_nested_writes
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from support.models import Project, ProjectContributor, Issue, Comment
|
||||
from authentication.models import User
|
||||
from support.serializers import ProjectSerializer, ContributorSerializer
|
||||
from support.serializers import (ProjectSerializer,
|
||||
ProjectDetailSerializer,
|
||||
ContributorSerializer,
|
||||
IssueSerializer,
|
||||
CommentListSerializer,
|
||||
CommentDetailSerializer)
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.permissions import (IsAuthenticated,
|
||||
IsAuthenticatedOrReadOnly)
|
||||
from support.permissions import IsAuthor
|
||||
from support.permissions import IsAuthor, IsContributor
|
||||
from rest_framework.decorators import action
|
||||
|
||||
|
||||
class ProjectViewSet(ModelViewSet):
|
||||
permission_classes=[IsAuthenticatedOrReadOnly]
|
||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||
|
||||
serializer_class = ProjectSerializer
|
||||
detail_serializer_class = ProjectDetailSerializer
|
||||
|
||||
queryset = Project.objects.filter(active=True)
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
return self.detail_serializer_class
|
||||
return super().get_serializer_class()
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""set authenticated user as author and contributor on creation"""
|
||||
test = serializer.save(author=self.request.user)
|
||||
@ -27,7 +39,7 @@ class ProjectViewSet(ModelViewSet):
|
||||
contributor_serializer.save()
|
||||
|
||||
@action(detail=True, methods=['patch'],
|
||||
permission_classes=[IsAuthor],
|
||||
permission_classes=[IsContributor],
|
||||
basename='add_contributor')
|
||||
def add_contributor(self, request, pk):
|
||||
"""Create the user/project contributor's relation"""
|
||||
@ -47,4 +59,60 @@ class ProjectViewSet(ModelViewSet):
|
||||
|
||||
|
||||
class IssueViewSet(ModelViewSet):
|
||||
serializer =
|
||||
permission_classes = [IsContributor]
|
||||
|
||||
serializer_class = IssueSerializer
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
project_id = int(self.request.GET.get('project'))
|
||||
project = Project.objects.get(id=project_id)
|
||||
self.check_object_permissions(self.request, project)
|
||||
return Issue.objects.filter(project=project_id)
|
||||
|
||||
|
||||
def get_contributors(self, project):
|
||||
queryset = ProjectContributor.objects.filter(project=project)
|
||||
contributors_serializer = ContributorSerializer(queryset, many=True)
|
||||
return contributors_serializer.data
|
||||
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
print(request.data['project'])
|
||||
project = Project.objects.get(id=request.data['project'])
|
||||
serializer = IssueSerializer(data=request.data)
|
||||
|
||||
|
||||
print(request.data['project'], type(request.data['project']))
|
||||
print(self.get_contributors(request.data['project']))
|
||||
|
||||
if self.request.user in project.contributors:
|
||||
if serializer.is_valid(raise_exception=True):
|
||||
serializer.author = self.request.user
|
||||
serializer.save()
|
||||
response = {
|
||||
"message": f"Issue created for project {project}",
|
||||
"data": serializer.data
|
||||
}
|
||||
return Response(response, status = status.HTTP_201_CREATED)
|
||||
|
||||
#def perform_create(self, serializer):
|
||||
# """set authenticated user as author and contributor on creation"""
|
||||
# serializer.save(author=self.request.user)
|
||||
|
||||
|
||||
class ContributorViewSet(ModelViewSet):
|
||||
serializer_class = ContributorSerializer
|
||||
queryset = ProjectContributor.objects.all()
|
||||
|
||||
|
||||
class CommentViewSet(ModelViewSet):
|
||||
serializer_class = CommentListSerializer
|
||||
detail_serializer_class = CommentDetailSerializer
|
||||
|
||||
queryset = Comment.objects.all()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
return self.detail_serializer_class
|
||||
return super().get_serializer_class()
|
||||
|
Loading…
x
Reference in New Issue
Block a user