working on permission is_contributor; checkpoint

This commit is contained in:
2025-05-30 10:24:01 +02:00
parent 776ba21695
commit 3636d4a72b
12 changed files with 268 additions and 32 deletions

View File

@@ -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)

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View 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'),
),
]

View 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'),
),
]

View File

@@ -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)

View File

@@ -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()
)

View File

@@ -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']

View File

@@ -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()