Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API: Récupérer des WasteActions avec leur ResourceActions (si l'utilisateur est connecté) #4544

Open
wants to merge 5 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@
from .communityevent import CommunityEventSerializer # noqa: F401
from .partner import PartnerSerializer, PartnerShortSerializer, PartnerContactSerializer # noqa: F401
from .videotutorial import VideoTutorialSerializer # noqa: F401
from .wasteaction import WasteActionSerializer # noqa: F401
from .resourceaction import ResourceActionSerializer # noqa: F401
from .wasteaction import WasteActionSerializer, WasteActionWithActionsSerializer # noqa: F401
from .resourceaction import ResourceActionSerializer, ResourceActionWithCanteenSerializer # noqa: F401
8 changes: 8 additions & 0 deletions api/serializers/resourceaction.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from rest_framework import serializers

from api.serializers.canteen import MinimalCanteenSerializer
from data.models import ResourceAction


Expand All @@ -12,3 +13,10 @@ class Meta:
"canteen_id",
"is_done",
)


class ResourceActionWithCanteenSerializer(ResourceActionSerializer):
canteen = MinimalCanteenSerializer(read_only=True)

class Meta(ResourceActionSerializer.Meta):
fields = ResourceActionSerializer.Meta.fields + ("canteen",)
18 changes: 17 additions & 1 deletion api/serializers/wasteaction.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from rest_framework import serializers
from rest_framework.fields import Field

from data.models import WasteAction
from api.serializers.resourceaction import ResourceActionWithCanteenSerializer
from data.models import ResourceAction, WasteAction


class WagtailImageSerializedField(Field):
Expand Down Expand Up @@ -32,3 +33,18 @@ class Meta:
"waste_origins",
"lead_image",
)


class WasteActionWithActionsSerializer(WasteActionSerializer):
actions = ResourceActionWithCanteenSerializer(many=True, read_only=True)

class Meta(WasteActionSerializer.Meta):
fields = WasteActionSerializer.Meta.fields + ("actions",)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
user = kwargs.pop("user", None)
if user:
self.fields["actions"].queryset = ResourceAction.objects.for_user(user)
else:
self.fields["actions"].queryset = ResourceAction.objects.none()
55 changes: 47 additions & 8 deletions api/tests/test_waste_actions.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
from django.urls import reverse
from rest_framework.test import APITestCase

from data.factories import WasteActionFactory
from data.factories import (
CanteenFactory,
ResourceActionFactory,
UserFactory,
WasteActionFactory,
)
from data.models import WasteAction


class TestWasteActionsApi(APITestCase):
class TestWasteActionsListApi(APITestCase):
@classmethod
def setUpTestData(cls):
cls.waste_action = WasteActionFactory.create()
Expand All @@ -15,13 +20,8 @@ def test_get_waste_actions_list(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data["results"]), 1)

def test_get_waste_action_detail(self):
response = self.client.get(reverse("waste_action_detail", kwargs={"pk": self.waste_action.id}))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["id"], self.waste_action.id)


class TestWasteActionsFiltersApi(APITestCase):
class TestWasteActionsListFiltersApi(APITestCase):
def test_effort_filter(self):
WasteActionFactory.create(effort=WasteAction.Effort.LARGE)
WasteActionFactory.create(effort=WasteAction.Effort.MEDIUM)
Expand Down Expand Up @@ -55,3 +55,42 @@ def test_text_search(self):
response = self.client.get(f"{reverse('waste_actions_list')}?search=evaluation")
results = response.data["results"]
self.assertEqual(len(results), 2)


class TestWasteActionsDetailApi(APITestCase):
@classmethod
def setUpTestData(cls):
cls.waste_action = WasteActionFactory.create()
cls.user = UserFactory()
cls.user_with_canteen = UserFactory()
CanteenFactory()
cls.canteen = CanteenFactory()
cls.canteen.managers.add(cls.user_with_canteen)
cls.resource_action = ResourceActionFactory.create(
resource=cls.waste_action, canteen=cls.canteen, is_done=True
)

def test_get_waste_action_detail(self):
# anonymous
response = self.client.get(reverse("waste_action_detail", kwargs={"pk": self.waste_action.id}))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["id"], self.waste_action.id)
self.assertTrue("actions" not in response.data)
# logged in user (without canteen)
self.client.force_login(user=self.user)
response = self.client.get(reverse("waste_action_detail", kwargs={"pk": self.waste_action.id}))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["id"], self.waste_action.id)
self.assertTrue("actions" in response.data)
self.assertEqual(len(response.data["actions"]), 0)
# logged in user with canteen & resource action
self.client.force_login(user=self.user_with_canteen)
response = self.client.get(reverse("waste_action_detail", kwargs={"pk": self.waste_action.id}))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["id"], self.waste_action.id)
self.assertTrue("actions" in response.data)
self.assertEqual(len(response.data["actions"]), 1)
self.assertEqual(response.data["actions"][0]["canteen_id"], self.canteen.id)
self.assertEqual(response.data["actions"][0]["canteen"]["id"], self.canteen.id)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pourquoi avoir les deux canteen_id et canteen: {id} ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

le serializer ResourceActionWithCanteenSerializer hérite de ResourceActionSerializer du coup il a les deux (l'id, et l'objet). ca ne me choque pas ^^

self.assertEqual(response.data["actions"][0]["canteen"]["name"], self.canteen.name)
self.assertTrue(response.data["actions"][0]["is_done"])
9 changes: 8 additions & 1 deletion api/views/wasteaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from rest_framework.generics import ListAPIView, RetrieveAPIView
from rest_framework.pagination import LimitOffsetPagination

from api.serializers import WasteActionSerializer
from api.serializers import WasteActionSerializer, WasteActionWithActionsSerializer
from data.models import WasteAction

from .utils import UnaccentSearchFilter
Expand Down Expand Up @@ -39,3 +39,10 @@ class WasteActionView(RetrieveAPIView):
model = WasteAction
queryset = WasteAction.objects.all()
serializer_class = WasteActionSerializer

def get_serializer(self, *args, **kwargs):
if self.request.user.is_authenticated:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vu qu'on a besoin de passer le user au serializer, je n'ai pas trouvé mieux que les kwargs (j'ai vu que c'était déjà utilisé ailleurs (canteen, "action"))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

est-ce que c'est possible de passer actions directe ? C'est-à-dire mettre la logique de user et queryset qui est maintenant dans le serializer ici ?

Copy link
Member Author

@raphodn raphodn Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

j'ai essayé mais je n'arrive pas à filtrer le queryset de la FK.
ici dans le vue ca permet seulement de filtrer le queryset de WasteAction, pas ResourceAction.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mais en fait ca bloque sur le fait que je n'arrive pas à passer un queryset custom à serializer custom 🤔
le queryset custom dans WasteActionWithActionsSerializer pour le champ actions est ignoré..

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok je crois que j'ai trouvé une solution avec SerializerMethodField

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

finally : cd8b85a

kwargs["context"] = self.get_serializer_context()
kwargs["context"]["user"] = self.request.user
return WasteActionWithActionsSerializer(*args, **kwargs)
return super().get_serializer(*args, **kwargs)
7 changes: 7 additions & 0 deletions data/models/resourceaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@
from .wasteaction import WasteAction


class ResourceActionQuerySet(models.QuerySet):
def for_user(self, user):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

si vous trouvez un meilleur nom de for_user let me know ^^

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for_managed_canteens/for_user_canteens ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renommé ici : 111a86d

return self.filter(canteen__in=user.canteens.all())


class ResourceAction(models.Model):
class Meta:
unique_together = ("resource", "canteen")
verbose_name = "ressource : action (cantine)"
verbose_name_plural = "ressources : actions (cantines)"

objects = models.Manager.from_queryset(ResourceActionQuerySet)()

creation_date = models.DateTimeField(auto_now_add=True)
modification_date = models.DateTimeField(auto_now=True)
history = HistoricalRecords()
Expand Down
21 changes: 20 additions & 1 deletion data/tests/test_resource_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,34 @@
UserFactory,
WasteActionFactory,
)
from data.models import ResourceAction


class ResourceActionModelSaveTest(TestCase):
class ResourceActionQuerySetTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = UserFactory()
cls.user_with_canteen = UserFactory()
cls.canteen = CanteenFactory()
cls.canteen_with_manager = CanteenFactory()
cls.canteen_with_manager.managers.add(cls.user_with_canteen)
cls.waste_action = WasteActionFactory()
ResourceActionFactory(resource=cls.waste_action, canteen=cls.canteen, is_done=True)
ResourceActionFactory(resource=cls.waste_action, canteen=cls.canteen_with_manager, is_done=True)

def test_for_user(self):
self.assertEqual(ResourceAction.objects.count(), 2)
self.assertEqual(ResourceAction.objects.for_user(self.user).count(), 0)
self.assertEqual(ResourceAction.objects.for_user(self.user_with_canteen).count(), 1)


class ResourceActionModelSaveTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = UserFactory()
cls.canteen = CanteenFactory()
cls.canteen.managers.add(cls.user)
cls.waste_action = WasteActionFactory()

def test_resource_action_validation(self):
# NOT OK: missing required field
Expand Down
Loading