Open pmg103 opened 4 years ago
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
]
expand_nested_specs()
only expands specs nested one level.This means that if you use
serialization_spec =
in aSerializationSpecPlugin
to specify the prefetching to be done, it will not be executed if you use this plugin below the top level.