From 776ba21695550af2498e240710eb03b2e905b644 Mon Sep 17 00:00:00 2001 From: yann Date: Mon, 26 May 2025 20:08:34 +0200 Subject: [PATCH] project model create and add contributors --- softdesk/authentication/serializers.py | 1 - softdesk/authentication/views.py | 5 -- softdesk/support/admin.py | 4 +- .../migrations/0004_alter_project_author.py | 19 +++++++ .../migrations/0005_alter_project_author.py | 19 +++++++ .../migrations/0006_alter_project_author.py | 19 +++++++ ...contributor_active_alter_project_author.py | 26 ++++++++++ ...ename_user_contributor_contributor_user.py | 18 +++++++ ...e_contributor_user_contributor_username.py | 18 +++++++ ...ment_author_alter_issue_author_and_more.py | 52 +++++++++++++++++++ softdesk/support/models.py | 33 ++++++++---- softdesk/support/permissions.py | 9 ++++ softdesk/support/serializers.py | 31 ++++++++--- softdesk/support/views.py | 49 +++++++++++++++-- 14 files changed, 273 insertions(+), 30 deletions(-) create mode 100644 softdesk/support/migrations/0004_alter_project_author.py create mode 100644 softdesk/support/migrations/0005_alter_project_author.py create mode 100644 softdesk/support/migrations/0006_alter_project_author.py create mode 100644 softdesk/support/migrations/0007_contributor_active_alter_project_author.py create mode 100644 softdesk/support/migrations/0008_rename_user_contributor_contributor_user.py create mode 100644 softdesk/support/migrations/0009_rename_contributor_user_contributor_username.py create mode 100644 softdesk/support/migrations/0010_alter_comment_author_alter_issue_author_and_more.py create mode 100644 softdesk/support/permissions.py diff --git a/softdesk/authentication/serializers.py b/softdesk/authentication/serializers.py index 09dfa7f..6fd6e7a 100644 --- a/softdesk/authentication/serializers.py +++ b/softdesk/authentication/serializers.py @@ -1,6 +1,5 @@ from rest_framework.serializers import ModelSerializer, SerializerMethodField, ValidationError from rest_framework import serializers -from support.models import Project, Issue, Comment, Contributor from authentication.models import User diff --git a/softdesk/authentication/views.py b/softdesk/authentication/views.py index c1908d3..451c83d 100644 --- a/softdesk/authentication/views.py +++ b/softdesk/authentication/views.py @@ -22,12 +22,8 @@ class UserCreateView(APIView): #TODELETE : for testing purpose def get(self, request, *args, **kwargs): user = User.objects.all() - print(request.user) serializer = UserSerializer(user, many=True) - print(serializer.data) - #if serializer.is_valid(): return Response(serializer.data) - #return Response("prout", status=status.HTTP_226_IM_USED) def post(self, request): """ @@ -73,7 +69,6 @@ class UserView(APIView): def put(self, request): user = request.user - print("coucou", request.data['user']) serializer = UserUpdateSerializer(user, data=request.data) print(serializer.initial_data) if serializer.is_valid(): diff --git a/softdesk/support/admin.py b/softdesk/support/admin.py index 50d541c..e084721 100644 --- a/softdesk/support/admin.py +++ b/softdesk/support/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from support.models import Project, Issue, Comment, Contributor +from support.models import Project, Issue, Comment, ProjectContributor from authentication.models import User class AdminUser: @@ -9,5 +9,5 @@ admin.site.register(User) admin.site.register(Project) admin.site.register(Issue) admin.site.register(Comment) -admin.site.register(Contributor) +admin.site.register(ProjectContributor) diff --git a/softdesk/support/migrations/0004_alter_project_author.py b/softdesk/support/migrations/0004_alter_project_author.py new file mode 100644 index 0000000..8b8d645 --- /dev/null +++ b/softdesk/support/migrations/0004_alter_project_author.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.1 on 2025-05-25 19:36 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('support', '0003_rename_contributor_contributor_user'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='author', + field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='author', to='support.contributor'), + ), + ] diff --git a/softdesk/support/migrations/0005_alter_project_author.py b/softdesk/support/migrations/0005_alter_project_author.py new file mode 100644 index 0000000..ed7587b --- /dev/null +++ b/softdesk/support/migrations/0005_alter_project_author.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.1 on 2025-05-25 19:37 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('support', '0004_alter_project_author'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='author', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='author', to='support.contributor'), + ), + ] diff --git a/softdesk/support/migrations/0006_alter_project_author.py b/softdesk/support/migrations/0006_alter_project_author.py new file mode 100644 index 0000000..e0671c6 --- /dev/null +++ b/softdesk/support/migrations/0006_alter_project_author.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.1 on 2025-05-25 19:37 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('support', '0005_alter_project_author'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='author', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='author', to='support.contributor'), + ), + ] diff --git a/softdesk/support/migrations/0007_contributor_active_alter_project_author.py b/softdesk/support/migrations/0007_contributor_active_alter_project_author.py new file mode 100644 index 0000000..e76a42a --- /dev/null +++ b/softdesk/support/migrations/0007_contributor_active_alter_project_author.py @@ -0,0 +1,26 @@ +# Generated by Django 5.2.1 on 2025-05-25 19:49 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('support', '0006_alter_project_author'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='contributor', + name='active', + field=models.BooleanField(default=True), + ), + migrations.AlterField( + model_name='project', + name='author', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='author', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/softdesk/support/migrations/0008_rename_user_contributor_contributor_user.py b/softdesk/support/migrations/0008_rename_user_contributor_contributor_user.py new file mode 100644 index 0000000..28befee --- /dev/null +++ b/softdesk/support/migrations/0008_rename_user_contributor_contributor_user.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.1 on 2025-05-25 19:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('support', '0007_contributor_active_alter_project_author'), + ] + + operations = [ + migrations.RenameField( + model_name='contributor', + old_name='user', + new_name='contributor_user', + ), + ] diff --git a/softdesk/support/migrations/0009_rename_contributor_user_contributor_username.py b/softdesk/support/migrations/0009_rename_contributor_user_contributor_username.py new file mode 100644 index 0000000..5094d4e --- /dev/null +++ b/softdesk/support/migrations/0009_rename_contributor_user_contributor_username.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.1 on 2025-05-26 05:26 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('support', '0008_rename_user_contributor_contributor_user'), + ] + + operations = [ + migrations.RenameField( + model_name='contributor', + old_name='contributor_user', + new_name='username', + ), + ] diff --git a/softdesk/support/migrations/0010_alter_comment_author_alter_issue_author_and_more.py b/softdesk/support/migrations/0010_alter_comment_author_alter_issue_author_and_more.py new file mode 100644 index 0000000..3d64343 --- /dev/null +++ b/softdesk/support/migrations/0010_alter_comment_author_alter_issue_author_and_more.py @@ -0,0 +1,52 @@ +# Generated by Django 5.2.1 on 2025-05-26 05:53 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('support', '0009_rename_contributor_user_contributor_username'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='comment', + name='author', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='comment_author', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='issue', + name='author', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='issue_author', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='project', + name='author', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='project_author', to=settings.AUTH_USER_MODEL), + ), + migrations.CreateModel( + name='ProjectContributor', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('active', models.BooleanField(default=True)), + ('data', models.CharField(blank=True, max_length=255)), + ('contributor', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)), + ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project', to='support.project')), + ], + options={ + 'unique_together': {('contributor', 'project')}, + }, + ), + migrations.AlterField( + model_name='project', + name='contributors', + field=models.ManyToManyField(related_name='contribution', through='support.ProjectContributor', to=settings.AUTH_USER_MODEL), + ), + migrations.DeleteModel( + name='Contributor', + ), + ] diff --git a/softdesk/support/models.py b/softdesk/support/models.py index 85fb7f3..588a3b9 100644 --- a/softdesk/support/models.py +++ b/softdesk/support/models.py @@ -16,23 +16,31 @@ class Project(models.Model): type = models.CharField(choices=Type.choices, max_length=10) active = models.BooleanField(default=True) description = models.CharField(max_length=4000) - author = models.ForeignKey('Contributor', + author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, - related_name='author') + related_name='project_author', null=True) contributors = models.ManyToManyField(settings.AUTH_USER_MODEL, - through='Contributor', + through='ProjectContributor', related_name='contribution') + def __str__(self): + return self.title -class Contributor(models.Model): - user = models.ForeignKey(settings.AUTH_USER_MODEL, +class ProjectContributor(models.Model): + contributor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING) + active = models.BooleanField(default=True) project = models.ForeignKey('Project', on_delete=models.CASCADE, related_name='project') data = models.CharField(max_length=255, blank=True) + class Meta: + unique_together = ('contributor', 'project') + + def __str__(self): + return self.contributor.username class Issue(models.Model): @@ -57,21 +65,24 @@ 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) project = models.ForeignKey(Project, null=True, on_delete=models.CASCADE, blank=True) - status = models.CharField(Status.choices, max_length=15) - priority = models.CharField(Priority.choices, max_length=15) - tag = models.CharField(Tag.choices, max_length=15) - - author = models.ForeignKey('Contributor', on_delete=models.DO_NOTHING) + author = models.ForeignKey(settings.AUTH_USER_MODEL, + on_delete=models.DO_NOTHING, + related_name='issue_author', null=True) class Comment(models.Model): title = models.CharField(max_length=255) date_created = models.DateTimeField(auto_now_add=True) description = models.CharField(max_length=4000) - author = models.ForeignKey('Contributor', on_delete=models.DO_NOTHING) issue = models.ForeignKey(Issue, on_delete=models.CASCADE) + author = models.ForeignKey(settings.AUTH_USER_MODEL, + on_delete=models.DO_NOTHING, + related_name='comment_author', null=True) diff --git a/softdesk/support/permissions.py b/softdesk/support/permissions.py new file mode 100644 index 0000000..d4b4293 --- /dev/null +++ b/softdesk/support/permissions.py @@ -0,0 +1,9 @@ +from rest_framework.permissions import BasePermission + + +class IsAuthor(BasePermission): + + def has_object_permission(self, request, view, project): + return bool(request.user + and request.user.is_authenticated + and request.user==project.author) \ No newline at end of file diff --git a/softdesk/support/serializers.py b/softdesk/support/serializers.py index 2f67e1f..431bf95 100644 --- a/softdesk/support/serializers.py +++ b/softdesk/support/serializers.py @@ -1,17 +1,36 @@ -from rest_framework.serializers import ModelSerializer -from support.models import Project, Contributor, Issue, Comment +from rest_framework.serializers import (ModelSerializer, + StringRelatedField, + SlugRelatedField) +from support.models import Project, ProjectContributor, Issue, Comment + + +class ContributorSerializer(ModelSerializer): + + class Meta: + model = ProjectContributor + fields = ['contributor', 'project', 'data'] class ProjectSerializer(ModelSerializer): + contributors = SlugRelatedField(many=True, + read_only='True', + slug_field='username') + author = StringRelatedField(many=False) + class Meta: model = Project - fields = ['title', 'date_created', 'type', 'description', 'author', + fields = ['id', 'title', 'date_created', 'type', 'description', 'author', 'contributors'] +class ProjectDetailSerializer(ModelSerializer): + pass -class ContributorSerialier(ModelSerializer): +class IssueSerializer(ModelSerializer): class Meta: - model = Contributor - Fields = [''] \ No newline at end of file + model = Issue + fields = ['title', 'date_created', 'priority', 'tag', 'status', 'author'] + + + diff --git a/softdesk/support/views.py b/softdesk/support/views.py index cf309ba..6481296 100644 --- a/softdesk/support/views.py +++ b/softdesk/support/views.py @@ -1,11 +1,50 @@ from django.shortcuts import render +from rest_framework.serializers import raise_errors_on_nested_writes from rest_framework.viewsets import ModelViewSet -from support.models import Project, Contributor, Issue, Comment -from support.serializers import ProjectSerializer +from support.models import Project, ProjectContributor, Issue, Comment +from authentication.models import User +from support.serializers import ProjectSerializer, ContributorSerializer +from rest_framework.response import Response +from rest_framework import status +from rest_framework.permissions import (IsAuthenticated, + IsAuthenticatedOrReadOnly) +from support.permissions import IsAuthor +from rest_framework.decorators import action class ProjectViewSet(ModelViewSet): - serializer_class = ProjectSerializer + permission_classes=[IsAuthenticatedOrReadOnly] - def get_queryset(self): - return Project.objects.all() \ No newline at end of file + serializer_class = ProjectSerializer + queryset = Project.objects.filter(active=True) + + def perform_create(self, serializer): + """set authenticated user as author and contributor on creation""" + test = serializer.save(author=self.request.user) + data = {'contributor': self.request.user.id, 'project': test.id} + contributor_serializer = ContributorSerializer(data=data) + if contributor_serializer.is_valid(): + contributor_serializer.save() + + @action(detail=True, methods=['patch'], + permission_classes=[IsAuthor], + basename='add_contributor') + def add_contributor(self, request, pk): + """Create the user/project contributor's relation""" + if 'contributor' in request.data: + contributor = User.objects.get(username=request.data['contributor']) + data = {'contributor': contributor.id, 'project': pk} + serializer = ContributorSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return Response(f"User {contributor} added", + status=status.HTTP_202_ACCEPTED) + return Response("This user is already contributing", + status=status.HTTP_226_IM_USED) + return Response(f"Key error;`contributor` is expected, " + f"not `{list(request.data)[0]}`", + status=status.HTTP_400_BAD_REQUEST) + + +class IssueViewSet(ModelViewSet): + serializer = \ No newline at end of file