Skip to content
This repository was archived by the owner on Sep 28, 2022. It is now read-only.

serialization_spec is not expanded recursively #38

Open
pmg103 opened this issue Dec 2, 2019 · 1 comment
Open

serialization_spec is not expanded recursively #38

pmg103 opened this issue Dec 2, 2019 · 1 comment

Comments

@pmg103
Copy link
Contributor

pmg103 commented Dec 2, 2019

expand_nested_specs() only expands specs nested one level.

This means that if you use serialization_spec = in a SerializationSpecPlugin to specify the prefetching to be done, it will not be executed if you use this plugin below the top level.

@pmg103
Copy link
Contributor Author

pmg103 commented Aug 5, 2020

Example for reference:

Here, we want to return the calculated list of available products on an activity's organisation:

class ActivitySummary(SerializationSpecMixin, generics.RetrieveAPIView):
    queryset = Activity.objects.all()

    serialization_spec = [
        'id',
        'name',
        {'organisation': [
            'id',
            'name',
            {'available_products': OrganisationAvailableProducts()},
        ]},

We try to implement that as a plugin:

class OrganisationAvailableProducts(SerializationSpecPlugin):
    """
    An organisations available products are a union of:
    1. All associated products which don't require accreditation
    2. All associated products which require accreditation, and at least 1 admin is accredited in
    """
    ADMIN_ROLES = [
        User.ROLE_CHOICES.ORGADMIN,
        User.ROLE_CHOICES.ACCOUNT_SUPERUSER,
        User.ROLE_CHOICES.ACCOUNT_ORG_ADMIN,
        User.ROLE_CHOICES.ACCOUNT_PROFESSIONAL_PRACTITIONER,
    ]

    serialization_spec = [
        {'_admins': Filtered('users', Q(groups__name__in=ADMIN_ROLES), [
            'id',
            {'accredited_products': [
                'id'
            ]}
        ])},
        {'_products': [
            'id',
            'requires_accreditation',
        ]}
    ]

    def get_value(self, instance):
        accredited_products = set(sum([[product.id for product in admin.accredited_products.all()] for admin in instance._admins], []))
        return [
            product.id
            for product in instance._products
            if not product.requires_accreditation or product.id in accredited_products
        ]

But sadly this does not work as this serialization_spec is two levels down and is not expanded so cannot be accessed inside our get_value(). You are forced to do the prefetch yourself which is error-prone:

class OrganisationAvailableProducts(SerializationSpecPlugin):
    """
    An organisations available products are a union of:
    1. All associated products which don't require accreditation
    2. All associated products which require accreditation, and at least 1 admin is accredited in
    """
    ADMIN_ROLES = [
        User.ROLE_CHOICES.ORGADMIN,
        User.ROLE_CHOICES.ACCOUNT_SUPERUSER,
        User.ROLE_CHOICES.ACCOUNT_ORG_ADMIN,
        User.ROLE_CHOICES.ACCOUNT_PROFESSIONAL_PRACTITIONER,
    ]

    def modify_queryset(self, queryset):
        return queryset.prefetch_related(
            Prefetch(
                'users',
                queryset=User.objects.filter(
                    groups__name__in=self.ADMIN_ROLES
                ).prefetch_related(
                    Prefetch('accredited_products', queryset=Product.objects.only('id'))
                ).only(
                    'accredited_products', 'organisation'
                ),
                to_attr='_admins'
            ),
            Prefetch(
                'products',
                queryset=Product.objects.only('id', 'requires_accreditation'),
                to_attr='_products'
            ),
        )

    def get_value(self, instance):
        accredited_products = set(sum([[product.id for product in admin.accredited_products.all()] for admin in instance._admins], []))
        return [
            product.id
            for product in instance._products
            if not product.requires_accreditation or product.id in accredited_products
        ]

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant