Como se converte um objeto Django Model em um ditado com todos os seus campos? Tudo idealmente inclui chaves estrangeiras e campos comeditable=False
.
Deixe-me elaborar. Digamos que eu tenho um modelo Django como o seguinte:
from django.db import models
class OtherModel(models.Model): pass
class SomeModel(models.Model):
normal_value = models.IntegerField()
readonly_value = models.IntegerField(editable=False)
auto_now_add = models.DateTimeField(auto_now_add=True)
foreign_key = models.ForeignKey(OtherModel, related_name="ref1")
many_to_many = models.ManyToManyField(OtherModel, related_name="ref2")
No terminal, fiz o seguinte:
other_model = OtherModel()
other_model.save()
instance = SomeModel()
instance.normal_value = 1
instance.readonly_value = 2
instance.foreign_key = other_model
instance.save()
instance.many_to_many.add(other_model)
instance.save()
Quero converter isso no seguinte dicionário:
{'auto_now_add': datetime.datetime(2015, 3, 16, 21, 34, 14, 926738, tzinfo=<UTC>),
'foreign_key': 1,
'id': 1,
'many_to_many': [1],
'normal_value': 1,
'readonly_value': 2}
Perguntas com respostas insatisfatórias:
Django: Convertendo um conjunto inteiro de objetos de um modelo em um único dicionário
Como transformar objetos do Django Model em um dicionário e ainda ter suas chaves estrangeiras?
to_dict
e manipulá-lo da maneira que desejar._meta
definições do modelo para encontrar os campos associados ao modelo e recuperar seus valores na instância.Respostas:
Existem várias maneiras de converter uma instância em um dicionário, com graus variados de manipulação de casos de canto e proximidade com o resultado desejado.
1
instance.__dict__
que retorna
Essa é de longe a mais simples, mas está faltando
many_to_many
,foreign_key
está com o nome errado e tem duas coisas extras indesejadas.2)
model_to_dict
que retorna
Este é o único com
many_to_many
, mas faltam os campos não editáveis.3)
model_to_dict(..., fields=...)
que retorna
Isso é estritamente pior que a
model_to_dict
invocação padrão .4)
query_set.values()
que retorna
É a mesma saída,
instance.__dict__
mas sem os campos extras.foreign_key_id
ainda está errado emany_to_many
ainda está faltando.5. Função Personalizada
O código para django's
model_to_dict
teve a maior parte da resposta. Ele removeu explicitamente os campos não editáveis, portanto, remover essa verificação e obter os IDs de chaves estrangeiras de muitos a muitos campos resulta no seguinte código, que se comporta conforme o desejado:Embora essa seja a opção mais complicada, a chamada
to_dict(instance)
nos dá exatamente o resultado desejado:6. Use serializadores
O ModelSerialzer do Django Rest Framework permite criar um serializador automaticamente a partir de um modelo.
retorna
Isso é quase tão bom quanto a função personalizada, mas auto_now_add é uma sequência em vez de um objeto datetime.
Rodada de bônus: melhor impressão do modelo
Se você deseja um modelo de django que tenha uma melhor exibição da linha de comando python, faça com que seus modelos sejam da classe filho da seguinte maneira:
Então, por exemplo, se definirmos nossos modelos como tal:
A chamada
SomeModel.objects.first()
agora produz resultados como este:fonte
isinstance
teste na solução 5 (e o bônus) paraif f.many_to_many
.model_to_dict
, que usaisinstance
. Eu não sei por que eles fizeram essa escolha, mas pode haver uma boa razão para isso (como amany_to_many
propriedade que está sendo introduzido em uma versão posterior)@property
campos?Encontrei uma solução interessante para obter o resultado:
Suponha que você tenha um objeto de modelo
o
:Apenas ligue:
fonte
A solução @Zags foi maravilhosa!
Eu acrescentaria, no entanto, uma condição para os campos de data para torná-lo compatível com JSON.
Rodada de Bônus
Se você deseja um modelo django que tenha uma melhor exibição da linha de comando python, faça com que seus modelos filhem a seguinte classe:
Então, por exemplo, se definirmos nossos modelos como tal:
A chamada
SomeModel.objects.first()
agora produz resultados como este:fonte
Maneira mais simples,
Se sua consulta for Model.Objects.get ():
get () retornará uma instância única para que você possa usar diretamente
__dict__
da sua instânciamodel_dict =
Model.Objects.get().__dict__
para filter () / all ():
all () / filter () retornará uma lista de instâncias para que você possa usar
values()
para obter a lista de objetos.model_values = Model.Objects.all (). values ()
fonte
apenas
vars(obj)
, indicará todos os valores do objetoVocê pode adicionar isso também
fonte
Atualizar
A resposta agregada mais recente publicada por @zags é mais completa e elegante que a minha. Por favor, consulte essa resposta.
Original
Se você estiver disposto a definir seu próprio método to_dict como o @karthiker sugeriu, isso resume o problema a um problema de conjuntos.
Precisamos remover as chaves estrangeiras rotuladas incorretamente de otherDict .
Para fazer isso, podemos usar um loop que cria um novo dicionário que possui todos os itens, exceto aqueles com sublinhados. Ou, para economizar tempo, podemos apenas adicioná-los ao ditado original, pois os dicionários são apenas conjuntos escondidos.
Assim, ficamos com o seguinte ditado :
E você acabou de devolver isso.
No lado negativo, você não pode usar sublinhados em seus nomes de campo editáveis = falsos. No lado positivo, isso funcionará para qualquer conjunto de campos em que os campos criados pelo usuário não contenham sublinhados.
Essa não é a melhor maneira de fazer isso, mas pode funcionar como uma solução temporária até que um método mais direto seja encontrado.
Para o exemplo abaixo, dict seria formado com base em model_to_dict e otherDict seria formado pelo método de valores do filtro. Eu teria feito isso com os próprios modelos, mas não consigo fazer com que minha máquina aceite outroModelo.
Isso deve colocá-lo em uma estimativa aproximada da resposta à sua pergunta, espero.
fonte
re
aqui. Se é para filtrar as chaves com sublinhados, isso não é um código correto nem um comportamento correto.re.match("_", "reference1_id")
retornosNone
e colunas legítimas no banco de dados podem ter sublinhados em seus nomes.editable=false
campos. Eu só estava tentando fornecer algo com o qual você possa trabalhar até que uma solução mais cânone possa ser fornecida."_" in string
mais do quere
nesse caso.in
vez dere
.Muitas soluções interessantes aqui. Minha solução foi adicionar um método as_dict ao meu modelo com uma compreensão de dict.
Como um bônus, esta solução combinada com uma compreensão da lista em uma consulta é uma boa solução se você deseja exportar seus modelos para outra biblioteca. Por exemplo, despejando seus modelos em um dataframe do pandas:
fonte
(não quis fazer o comentário)
Ok, isso realmente não depende de tipos dessa maneira. Eu posso ter entendido errado a pergunta original aqui, então me perdoe se for esse o caso. Se você criar o serliazers.py, criará classes que possuem meta classes.
Então, quando você obtém os dados na classe view, pode:
Isso é praticamente o que eu tenho em vários locais e retorna JSON agradável por meio do JSONRenderer.
Como eu disse, isso é cortesia do DjangoRestFramework, então vale a pena investigar isso.
fonte
A maneira mais fácil é usar apenas
pprint
, que está no Python baseIsso fornece uma saída semelhante,
json.dumps(..., indent = 4)
mas lida corretamente com os tipos de dados estranhos que podem ser incorporados à instância do modelo, comoModelState
eUUID
, etc.Testado em Python 3.7
fonte
Talvez isso ajude você. Que isso não oculte muitos ou muitos relacionamentos, mas é bastante útil quando você deseja enviar seu modelo em formato json.
fonte
A melhor solução que você já viu.
Converta a instância django.db.models.Model e todos os campos de função ForeignKey, ManyToManyField e @Property relacionados em dict.
https://gist.github.com/shuge/f543dc2094a3183f69488df2bfb51a52
fonte
A resposta do @zags é abrangente e deve ser suficiente,
mas o método nº 5 (que é o melhor IMO) gera um erro, por isso aprimorei a função auxiliar.Como o OP solicitou a conversão de
many_to_many
campos em uma lista de chaves primárias, e não em uma lista de objetos, aprimorei a função para que o valor de retorno agora seja JSON serializável - convertendodatetime
objetos emstr
emany_to_many
objetos em uma lista de IDs.fonte
concrete_fields
em2m_fields
parece idêntica, assumindo que om2m_fields
loop tenha uma implementação incorreta aqui.AttributeError: 'list' object has no attribute 'values_list'
que não consegui encontrar a razão por trás disso. Estou usando o Django 2.1.1field.value_from_object
e, como resultado, demodel_to_dict
. Atualizei a seção 5 da minha resposta para refletir isso.