Gostaria de fornecer dois serializadores diferentes e, ainda assim, poder me beneficiar de todas as facilidades de ModelViewSet
:
- Ao visualizar uma lista de objetos, gostaria que cada objeto tivesse um URL que redirecionasse para seus detalhes e todas as outras relações fossem exibidas usando
__unicode __
o modelo de destino;
exemplo:
{
"url": "http://127.0.0.1:8000/database/gruppi/2/",
"nome": "universitari",
"descrizione": "unitn!",
"creatore": "emilio",
"accesso": "CHI",
"membri": [
"emilio",
"michele",
"luisa",
"ivan",
"saverio"
]
}
- Ao visualizar os detalhes de um objeto, eu gostaria de usar o padrão
HyperlinkedModelSerializer
exemplo:
{
"url": "http://127.0.0.1:8000/database/gruppi/2/",
"nome": "universitari",
"descrizione": "unitn!",
"creatore": "http://127.0.0.1:8000/database/utenti/3/",
"accesso": "CHI",
"membri": [
"http://127.0.0.1:8000/database/utenti/3/",
"http://127.0.0.1:8000/database/utenti/4/",
"http://127.0.0.1:8000/database/utenti/5/",
"http://127.0.0.1:8000/database/utenti/6/",
"http://127.0.0.1:8000/database/utenti/7/"
]
}
Consegui fazer tudo isso funcionar da maneira que desejo:
serializers.py
# serializer to use when showing a list
class ListaGruppi(serializers.HyperlinkedModelSerializer):
membri = serializers.RelatedField(many = True)
creatore = serializers.RelatedField(many = False)
class Meta:
model = models.Gruppi
# serializer to use when showing the details
class DettaglioGruppi(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Gruppi
views.py
class DualSerializerViewSet(viewsets.ModelViewSet):
"""
ViewSet providing different serializers for list and detail views.
Use list_serializer and detail_serializer to provide them
"""
def list(self, *args, **kwargs):
self.serializer_class = self.list_serializer
return viewsets.ModelViewSet.list(self, *args, **kwargs)
def retrieve(self, *args, **kwargs):
self.serializer_class = self.detail_serializer
return viewsets.ModelViewSet.retrieve(self, *args, **kwargs)
class GruppiViewSet(DualSerializerViewSet):
model = models.Gruppi
list_serializer = serializers.ListaGruppi
detail_serializer = serializers.DettaglioGruppi
# etc.
Basicamente, detecto quando o usuário está solicitando uma exibição de lista ou uma exibição detalhada e altero serializer_class
para atender às minhas necessidades. No entanto, não estou realmente satisfeito com esse código, parece um hack sujo e, o mais importante, e se dois usuários solicitarem uma lista e um detalhe no mesmo momento?
Existe uma maneira melhor de conseguir isso usando ModelViewSets
ou eu tenho que voltar a usar GenericAPIView
?
EDIT:
Veja como fazê-lo usando uma base personalizada ModelViewSet
:
class MultiSerializerViewSet(viewsets.ModelViewSet):
serializers = {
'default': None,
}
def get_serializer_class(self):
return self.serializers.get(self.action,
self.serializers['default'])
class GruppiViewSet(MultiSerializerViewSet):
model = models.Gruppi
serializers = {
'list': serializers.ListaGruppi,
'detail': serializers.DettaglioGruppi,
# etc.
}
django
serialization
django-rest-framework
Urso preto
fonte
fonte
Respostas:
Substitua seu
get_serializer_class
método. Este método é usado em seus mixins de modelo para recuperar a classe Serializer adequada.Observe que também existe um
get_serializer
método que retorna uma instância do serializador corretofonte
if hasattr(self, 'action') and self.action == 'list'
pk
objeto of solicitado, se a ação éretrieve
?Você pode achar útil esse mix, ele substitui o método get_serializer_class e permite declarar um ditado que mapeia a ação e a classe do serializador ou o fallback para o comportamento usual.
fonte
Esta resposta é a mesma que a resposta aceita, mas prefiro fazer dessa maneira.
Visualizações genéricas
fonte
Em relação ao fornecimento de serializadores diferentes, por que ninguém está adotando a abordagem que verifica o método HTTP? É IMO mais claro e não requer verificações extras.
Créditos / fonte: https://github.com/encode/django-rest-framework/issues/1563#issuecomment-42357718
fonte
list
eretrieve
ações diferentes para , você tem o problema de que ambos usam oGET
método. É por isso que o django rest framework ViewSets usa o conceito de ações , que são semelhantes, mas um pouco diferentes dos métodos http correspondentes.Com base nas respostas @gonz e @ user2734679, criei este pequeno pacote python que fornece essa funcionalidade na forma de uma classe filho do ModelViewset. Aqui está como isso funciona.
fonte
Embora pré-definir vários serializadores de uma maneira ou de outra pareça ser o mais obviamente documentado , o FWIW existe uma abordagem alternativa que se baseia em outro código documentado e que permite passar argumentos para o serializador à medida que é instanciado. Eu acho que provavelmente tenderia a valer mais a pena se você precisasse gerar lógica com base em vários fatores, como níveis de administrador do usuário, a ação que está sendo chamada, talvez até atributos da instância.
A primeira peça do quebra-cabeça é a documentação sobre a modificação dinâmica de um serializador no ponto de instanciação . Essa documentação não explica como chamar esse código de um conjunto de visualizações ou como modificar o status somente leitura dos campos depois que eles foram iniciados - mas isso não é muito difícil.
A segunda parte - o método get_serializer também está documentado - (um pouco mais abaixo na página de get_serializer_class em 'outros métodos'), portanto, deve ser seguro confiar nela (e a fonte é muito simples, o que, com sorte, significa menos chances de ataques não intencionais). efeitos secundários resultantes da modificação). Verifique a fonte no GenericAPIView (o ModelViewSet - e todas as outras classes de conjunto de visualizações que parece - herdadas do GenericAPIView, que define get_serializer.
Ao juntar os dois, você poderia fazer algo assim:
Em um arquivo serializadores (para mim base_serializers.py):
Então, no seu conjunto de visualizações, você pode fazer algo assim:
E deve ser isso! Agora, o uso do MyViewSet deve instanciar o MyDynamicSerializer com os argumentos que você deseja - e assumindo que o serializador herda do DynamicFieldsModelSerializer, ele deve saber exatamente o que fazer.
Talvez valha a pena mencionar que pode fazer sentido especialmente se você quiser adaptar o serializador de outras maneiras ... por exemplo, para fazer coisas como pegar uma lista read_only_exceptions e usá-lo na lista de permissões em vez dos campos da lista negra (o que eu costumo fazer). Também acho útil definir os campos para uma tupla vazia, se não for aprovada, e apenas remover a verificação Nenhum ... e defino minhas definições de campos nos Serializadores herdados como ' all '. Isso significa que nenhum campo que não é passado ao instanciar o serializador sobrevive por acidente e também não preciso comparar a chamada do serializador com a definição de classe herdadora para saber o que foi incluído ... por exemplo, no init do DynamicFieldsModelSerializer:
Nota: se eu apenas quisesse duas ou três classes mapeadas para ações distintas e / ou não quisesse um comportamento serializador especialmente dinâmico, eu poderia usar uma das abordagens mencionadas por outras pessoas aqui, mas achei que vale a pena apresentar como alternativa , particularmente devido a seus outros usos.
fonte