Вложенный сериализатор Django

Я довольно новичок в DRF/Django и хочу создать конечную точку, которая возвращает вложенный json из нескольких моделей в формате:

    { 
        "site": {  
            "uuid": "99cba2b8-ddb0-11eb-bd58-237a8c3c3fe6",
            "domain_name": "hello.org"
            },

       "status": "live",

       "configuration": {   
            "secrets": [  
            {
             "name": "SEGMENT_KEY", # Configuration.name
             "value": [...] # Configuration.value
            },
           {
             "name": "CONFIG_KEY",
             "value": [...]
            },

        "admin_settings": {
            'tier'='trail',
            'subscription_ends'='some date',
            'features'=[]
        }

Вот модели:

class Site(models.Model):
    uuid = models.UUIDField(
        default=uuid.uuid4,
        editable=False,
        unique=True)
    domain_name = models.CharField(max_length=255, unique=True)
    created = models.DateTimeField(editable=False, auto_now_add=True)
    modified = models.DateTimeField(editable=False, auto_now=True)

class AdminConfiguration(models.Model):
    TRIAL = 'trial'
    PRO = 'pro'
    TIERS = [
        (TRIAL, 'Trial'), 
        (PRO, 'Professional'),
    ]
    site = models.OneToOneField(
        Site,
        null=False,
        blank=False,
        on_delete=models.CASCADE)
    tier = models.CharField(
        max_length=255,
        choices=TIERS,
        default=TRIAL)
    subscription_ends = models.DateTimeField(
        default=set_default_expiration)
    features = models.JSONField(default=list)


class Configuration(models.Model):
    CSS = 'css'
    SECRET = 'secret'
    TYPES = [
        (CSS, 'css'),
        (SECRET, 'secret')
    ]
    LIVE = 'live'
    DRAFT = 'draft'
    STATUSES = [
        (LIVE, 'Live'),
        (DRAFT, 'Draft'),
    ]
    site = models.ForeignKey(Site, on_delete=models.CASCADE)
    name = models.CharField(max_length=255, blank=False)
    type = models.CharField(
        max_length=255,
        choices=TYPES)
    value = models.JSONField(
        null=True)
    status = models.CharField(
        max_length=20,
        choices=STATUSES)

Логика работы сериализатора/viewset для достижения упомянутого json:

  • извлекает lookup_field: uuid
  • фильтрует параметры запроса: Configuration.status (либо live, либо draft
  • )
  • фильтры AdminConfiguration по id сайта (что-то вроде AdminConfiguration.objects.get(Site.objects.get(uuid))
  • )
  • фильтры Конфигурация на type = secret

Вот мои сериализаторы:

class SiteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Site
        fields = [
            'uuid',
            'domain_name'
        ]

class AdminSerializer(serializers.ModelSerializer):
    class Meta:
        model = AdminConfiguration
        fields = [
            'tier',
            'subscription_ends',
            'features'
        ]

class ConfigurationSubSerializer(serializers.ModelSerializer):    
    class Meta:
        model = Configuration
        fields = [
            'name',
            'value',
        ]

class SecretsConfigSerializer(serializers.ModelSerializer):
    site = SiteSerializer()
    admin_settings = AdminSerializer()
    status = serializers.CharField()
    configuration = ConfigurationSubSerializer(many=True, source='get_secret_config')

    class Meta:
        model = Configuration
        fields = [
            'site',
            'admin_settings',
            'status'
            'configuration'
        ]

    def get_secret_config(self, uuid):
        site = Site.objects.get(uuid=self.context['uuid'])
        if self.context['status'] == 'live' or self.context['status'] == 'draft':
            return Configuration.objects.filter(
                        site=site,
                      status=self.context['status'],
                        type='secret'
                    )

Viewset:

class SecretsViewSet(viewsets.ReadOnlyModelViewSet):
    model = Site
    lookup_field = 'uuid'
    serializer_class = SecertsConfigSerializer
    filter_backends = (DjangoFilterBackend,)
    filterset_fields = ['status'] #query params

    def get_serializer_context(self):
        return {
            'status': self.request.GET['status'],
            'uuid': self.request.GET['uuid']
        }

    def get_serializer(self, *args, **kwargs):
        kwargs['context'] = self.get_serializer_context()
        return CombinedConfigSerializer(*args, **kwargs)

Чего мне не хватает для достижения желаемого результата?

Почему бы вам не попробовать что-то более общее и не построить свой ответ, разделяя сериализаторы следующим образом (возможно, вы сможете использовать те же сериализаторы в другом месте):

def get(self, request, *args, **kwargs):
   resp = {
           'site': None,
           'status': None,
           'configuration': None,
           'admin_settings': None,
        }
   sites = models.Site.objects.all()
   resp['site'] = serializers.SitesSerializer(sites, many=True).data
   admin_settings = models.AdminConfiguration.objects.all()
   resp['admin_settings'] = serializers.AdminConfigurationSerializer(admin_settings, many=True).data
   # and so
   return Response(resp, status=status.HTTP_200_OK)
Вернуться на верх