В моем приложении пользователь может взять на себя одну из трех разных ролей.
Этих пользователей можно назначить программам, и им можно назначить три разных поля, каждое из которых предназначено исключительно для роль.
В своем API я пытаюсь опросить всех своих пользователей и аннотировать программы, в которых они работают:
Код: Выделить всё
def get_queryset(self):
queryset = (
User.objects
.select_related('profile')
.prefetch_related('managed_programs', 'supervised_programs', 'case_managed_programs')
.annotate(
programs=Case(
When(
role=RoleChoices.PROGRAM_MANAGER,
then=ArraySubquery(
Program.objects.filter(program_managers=OuterRef('pk'), is_active=True)
.values('id', 'name')
.annotate(
data=Func(
Value('{"id": '),
F('id'),
Value(', "name": "'),
F('name'),
Value('"}'),
function='concat',
output_field=CharField(),
)
)
.values('data')
),
),
When(
role=RoleChoices.SUPERVISOR,
then=ArraySubquery(
Program.objects.filter(supervisors=OuterRef('pk'), is_active=True)
.values('id', 'name')
.annotate(
data=Func(
Value('{"id": '),
F('id'),
Value(', "name": "'),
F('name'),
Value('"}'),
function='concat',
output_field=CharField(),
)
)
.values('data')
),
),
When(
role=RoleChoices.CASE_MANAGER,
then=ArraySubquery(
Program.objects.filter(case_managers=OuterRef('pk'), is_active=True)
.values('id', 'name')
.annotate(
data=Func(
Value('{"id": '),
F('id'),
Value(', "name": "'),
F('name'),
Value('"}'),
function='concat',
output_field=CharField(),
)
)
.values('data')
),
),
default=Value(list()),
output_field=ArrayField(base_field=JSONField()),
)
)
.order_by('id')
)
return queryset
The problem is that I'm using Django HashID fields for the Program PK, and this query returns the pure integer value for each Program.
I've tried a more "normal" approach, by getting the data using a
Код: Выделить всё
SerializerMethodField
Код: Выделить всё
@staticmethod
def get_programs(obj):
role_attr = {
RoleChoices.PROGRAM_MANAGER: 'managed_programs',
RoleChoices.SUPERVISOR: 'supervised_programs',
RoleChoices.CASE_MANAGER: 'case_managed_programs',
}
try:
programs = getattr(obj, role_attr[obj.role], None).values_list('id', 'name')
return [{'id': str(id), 'name': name} for id, name in programs]
except (AttributeError, KeyError):
return []
Код: Выделить всё
prefetch_related
So, I have two options here:
- Use the annotations but having the HashID returning, instead of integer PK
- Have the SerializerMethodField reuse the prefetched data, instead of requerying
EDIT:
A small heads-up, I've decided to use the first approach and Hash the ID manually inside the serializer
Код: Выделить всё
programs = serializers.SerializerMethodField()
@staticmethod
def get_programs(obj):
return [
{"id": str(Hashid(value=program['id'], salt=settings.SECRET_KEY, min_length=13)), "name": program['name']} for program in obj.programs
]
Источник: https://stackoverflow.com/questions/781 ... ngo-hashid