Page MenuHomeSoftware Heritage
Paste P1344

Error related to D7491
ActivePublic

Authored by anirudhlakhotia on Apr 21 2022, 9:39 PM.
if has_add_forge_now_permission(request.user):
for item in page.object_list:
history = AddForgeNowRequestHistory.objects.filter(request=item ).filter(actor_role = "MODERATOR").order_by(
"id"
)
item.__dict__["actor_role"] = "SUBMITTER"
item.__dict__["actor"] = "No action taken yet"
item.__dict__["last_modified_date"] = item.submission_date
for history_item in history:
item.__dict__["actor_role"] = history_item.__dict__["actor_role"]
item.__dict__["actor"] = history_item.__dict__["actor"]
item.__dict__["last_modified_date"] = history_item.__dict__["date"]
requests = AddForgeNowRequestSerializer(page.object_list, many=True).data
else:
requests = AddForgeNowRequestPublicSerializer(page.object_list, many=True).data

Event Timeline

That code is too hackish from my point of view, modifying internal private member of a class from the outside is never a good idea in programming.

I think the best way to implement the feature is (as pointed by @vlorentz in a D7491 inline comment) to directly add the new moderator and last_modified_date
field in the output produced by the AddForgeNowRequest*Serializer classes.

Below is an example of adding the new fields to the output produced by AddForgeNowRequestPublicSerializer you can inspire from.

class AddForgeNowRequestPublicSerializer(serializers.ModelSerializer):
    """Serializes AddForgeRequest without private fields."""

    moderator = serializers.SerializerMethodField()
    last_modified_date = serializers.SerializerMethodField()
    history = None

    class Meta:
        model = AddForgeRequest
        fields = (
            "id",
            "forge_url",
            "forge_type",
            "status",
            "submission_date",
            "moderator",
            "last_modified_date",
        )

    def _get_history(self, request):
        if not self.history:
            self.history = AddForgeNowRequestHistory.objects.filter(
                request=request
            ).order_by("id")
        return self.history

    def get_moderator(self, request):
        last_history_with_moderator = (
            self._get_history(request).filter(actor_role="MODERATOR").last()
        )
        return (
            last_history_with_moderator.actor if last_history_with_moderator else None
        )

    def get_last_modified_date(self, request):
        last_history = self._get_history(request).last()
        return (
            last_history.date.isoformat().replace("+00:00", "Z")
            if last_history
            else None
        )

The adding of these fields should also be done for the private AddForgeNowRequestSerializer class (without too much code duplication if possible).

I'm having some trouble understanding the behaviour of serializers:

if has_add_forge_now_permission(request.user):

    for item in page.object_list:
        print(AddForgeNowRequestSerializer(item).data)
        print()

    requests = AddForgeNowRequestSerializer(page.object_list, many=True).data
 
    for req in requests:
        print(dict(req))
        print()
class AddForgeNowRequestSerializer(serializers.ModelSerializer):

    moderator = serializers.SerializerMethodField()
    last_modified_date = serializers.SerializerMethodField()
    history = None

    class Meta:
        model = AddForgeRequest
        fields = "__all__"
    
    def _gethistory(self, request):
        if not self.history:
            self.history = AddForgeNowRequestHistory.objects.filter(
                request=request
            ).order_by("id")
        return self.history
    
    def get_moderator(self, request):
        last_history_with_moderator = self._gethistory(request).filter(actor_role = "MODERATOR").last()
        print("MODERATOR: ", last_history_with_moderator.actor) if last_history_with_moderator else print("MODERATOR: None")
        return last_history_with_moderator.actor if last_history_with_moderator else None
    
    def get_last_modified_date(self, request):
        last_history = self._gethistory(request).last()
        return last_history.date.isoformat().replace("+00:00", "Z") if last_history else None

The output of this block is:

MODERATOR: None
{'id': 1, 'moderator': None, 'last_modified_date': '2022-04-01T15:58:40.348874Z', 'status': 'PENDING', 'submission_date': '2022-04-01T15:58:40.346816Z', 'submitter_name': 'admin', 'submitter_email': 'admin@swh-web.org', 'submitter_forward_username': True, 'forge_type': 'bitbucket', 'forge_url': 'http://examplelink.com', 'forge_contact_email': 'example@email.com', 'forge_contact_name': 'Admin', 'forge_contact_comment': 'Comment'}

MODERATOR:  admin
{'id': 2, 'moderator': 'admin', 'last_modified_date': '2022-04-27T14:29:21.215613Z', 'status': 'WAITING_FOR_FEEDBACK', 'submission_date': '2022-04-01T16:00:07.419837Z', 'submitter_name': 'admin', 'submitter_email': 'admin@swh-web.org', 'submitter_forward_username': True, 'forge_type': 'bitbucket', 'forge_url': 'http://examplelink2.com', 'forge_contact_email': 'example@email.com', 'forge_contact_name': 'Admin', 'forge_contact_comment': 'a'}

MODERATOR:  admin
{'id': 3, 'moderator': 'admin', 'last_modified_date': '2022-04-27T15:34:34.263599Z', 'status': 'WAITING_FOR_FEEDBACK', 'submission_date': '2022-04-03T08:17:43.355030Z', 'submitter_name': 'admin', 'submitter_email': 'admin@swh-web.org', 'submitter_forward_username': True, 'forge_type': 'bitbucket', 'forge_url': 'https://google.com', 'forge_contact_email': 'testemail@gmail.com', 'forge_contact_name': 'Anirudh', 'forge_contact_comment': 'This is the final test entry'}

MODERATOR: None
MODERATOR: None
MODERATOR: None
{'id': 1, 'moderator': None, 'last_modified_date': '2022-04-01T15:58:40.348874Z', 'status': 'PENDING', 'submission_date': '2022-04-01T15:58:40.346816Z', 'submitter_name': 'admin', 'submitter_email': 'admin@swh-web.org', 'submitter_forward_username': True, 'forge_type': 'bitbucket', 'forge_url': 'http://examplelink.com', 'forge_contact_email': 'example@email.com', 'forge_contact_name': 'Admin', 'forge_contact_comment': 'Comment'}

{'id': 2, 'moderator': None, 'last_modified_date': '2022-04-01T15:58:40.348874Z', 'status': 'WAITING_FOR_FEEDBACK', 'submission_date': '2022-04-01T16:00:07.419837Z', 'submitter_name': 'admin', 'submitter_email': 'admin@swh-web.org', 'submitter_forward_username': True, 'forge_type': 'bitbucket', 'forge_url': 'http://examplelink2.com', 'forge_contact_email': 'example@email.com', 'forge_contact_name': 'Admin', 'forge_contact_comment': 'a'}

{'id': 3, 'moderator': None, 'last_modified_date': '2022-04-01T15:58:40.348874Z', 'status': 'WAITING_FOR_FEEDBACK', 'submission_date': '2022-04-03T08:17:43.355030Z', 'submitter_name': 'admin', 'submitter_email': 'admin@swh-web.org', 'submitter_forward_username': True, 'forge_type': 'bitbucket', 'forge_url': 'https://google.com', 'forge_contact_email': 'testemail@gmail.com', 'forge_contact_name': 'Anirudh', 'forge_contact_comment': 'This is the final test entry'}

Going by this,
The individually serialized objects work as expected, with the explicitly defined fields having the right values.
When the many=True flag is passed, the explicitly defined fields have wrong/default values.
I'm unable to find the cause for this behaviour, and request the help of the subscribers to ascertain the same.

This is due to the history cache that does not support multiple requests, use this to make it compatible with the many case:

class AddForgeNowRequestSerializer(serializers.ModelSerializer):

    moderator = serializers.SerializerMethodField()
    last_modified_date = serializers.SerializerMethodField()
    history: Dict[int, QuerySet] = {}

    class Meta:
        model = AddForgeRequest
        fields = "__all__"

    def _gethistory(self, request):
        if request.id not in self.history:
            self.history[request.id] = AddForgeNowRequestHistory.objects.filter(
                request=request
            ).order_by("id")
        return self.history[request.id]

    def get_moderator(self, request):
        last_history_with_moderator = (
            self._gethistory(request).filter(actor_role="MODERATOR").last()
        )
        print(
            "MODERATOR: ", last_history_with_moderator.actor
        ) if last_history_with_moderator else print("MODERATOR: None")
        return (
            last_history_with_moderator.actor if last_history_with_moderator else None
        )

    def get_last_modified_date(self, request):
        last_history = self._gethistory(request).last()
        return (
            last_history.date.isoformat().replace("+00:00", "Z")
            if last_history
            else None
        )