python - queryset - um para muitos django




Como faço para clonar um objeto de instância do modelo Django e salvá-lo no banco de dados? (8)

A documentação do Django para consultas de banco de dados inclui uma seção sobre como copiar instâncias do modelo . Supondo que suas chaves primárias são geradas automaticamente, você obtém o objeto que deseja copiar, define a chave primária como None e salva o objeto novamente:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

Nesse fragmento, o primeiro save() cria o objeto original e o segundo save() cria a cópia.

Se você continuar lendo a documentação, há também exemplos sobre como lidar com dois casos mais complexos: (1) copiar um objeto que é uma instância de uma subclasse de modelo e (2) também copiar objetos relacionados, incluindo objetos em muitos - para - muitas relações

Nota sobre a resposta de miah: Colocar o pk em None é mencionado na resposta de miah, embora não seja apresentado na frente e no centro. Então, minha resposta serve principalmente para enfatizar esse método como a maneira recomendada pelo Django de fazê-lo.

Nota histórica: Isso não foi explicado nos documentos do Django até a versão 1.4. Foi possível desde antes de 1.4, no entanto.

Possível funcionalidade futura: A alteração de documentos acima mencionada foi feita neste ticket . No encadeamento de comentários do ticket, também houve alguma discussão sobre a adição de uma função de copy integrada para as classes de modelo, mas, até onde eu sei, eles decidiram não resolver esse problema ainda. Portanto, essa maneira "manual" de copiar provavelmente terá que ser feita por enquanto.

Foo.objects.get(pk="foo")
<Foo: test>

No banco de dados, quero adicionar outro objeto que é uma cópia do objeto acima.

Suponha que minha tabela tenha uma linha. Eu quero inserir o primeiro objeto de linha em outra linha com uma chave primária diferente. Como eu posso fazer isso?


Basta alterar a chave primária do seu objeto e executar save ().

obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()

Se você quiser a chave gerada automaticamente, defina a nova chave como Nenhum.

Mais em UPDATE / INSERT here .


Esta é mais uma maneira de clonar a instância do modelo:

d = Foo.objects.filter(pk=1).values().first()   
d.update({'id': None})
duplicate = Foo.objects.create(**d)

Eu me deparei com algumas pegadinhas com a resposta aceita. Aqui está a minha solução.

import copy

def clone(instance):
    cloned = copy.copy(instance) # don't alter original instance
    cloned.pk = None
    try:
        delattr(cloned, '_prefetched_objects_cache')
    except AttributeError:
        pass
    return cloned

Nota: isso usa soluções que não são sancionadas oficialmente nos documentos do Django, e elas podem parar de funcionar em versões futuras. Eu testei isso em 1.9.13.

A primeira melhoria é que ele permite que você continue usando a instância original, usando copy.copy . Mesmo que você não pretenda reutilizar a instância, pode ser mais seguro fazer essa etapa se a instância que você está clonando foi passada como um argumento para uma função. Caso contrário, o chamador inesperadamente terá uma instância diferente quando a função retornar.

copy.copy parece produzir uma cópia superficial de uma instância de modelo do Django da maneira desejada. Esta é uma das coisas que eu não encontrei documentadas, mas funciona por decapagem e desarrolhação, por isso é provavelmente bem suportado.

Em segundo lugar, a resposta aprovada deixará todos os resultados pré-buscados anexados à nova instância. Esses resultados não devem ser associados à nova instância, a menos que você copie explicitamente os relacionamentos many-to-many. Se você percorrer os relacionamentos pré-buscados, obterá resultados que não correspondem ao banco de dados. Quebrar o código de trabalho quando você adiciona uma pré-busca pode ser uma surpresa desagradável.

A exclusão de _prefetched_objects_cache é uma maneira rápida e suja de eliminar todas as pré-buscas. Subseqüentes a muitos acessos funcionam como se nunca houvesse uma pré-busca. Usar uma propriedade não documentada que começa com um sublinhado provavelmente está solicitando problemas de compatibilidade, mas funciona por enquanto.


Para clonar um modelo com vários níveis de herança, ou seja,> = 2 ou ModelC abaixo

class ModelA(models.Model):
    info1 = models.CharField(max_length=64)

class ModelB(ModelA):
    info2 = models.CharField(max_length=64)

class ModelC(ModelB):
    info3 = models.CharField(max_length=64)

Por favor, consulte a questão here .


Tenha cuidado aqui. Isso pode ser extremamente caro se você estiver em um loop de algum tipo e você estiver recuperando objetos um por um. Se você não quiser a chamada para o banco de dados, faça:

from copy import deepcopy

new_instance = deepcopy(object_you_want_copied)
new_instance.id = None
new_instance.save()

Ele faz o mesmo que algumas dessas outras respostas, mas não faz a chamada do banco de dados para recuperar um objeto. Isso também é útil se você quiser fazer uma cópia de um objeto que ainda não existe no banco de dados.


Use o código abaixo:

from django.forms import model_to_dict

instance = Some.objects.get(slug='something')

kwargs = model_to_dict(instance, exclude=['id'])
new_instance = Some.objects.create(**kwargs)

definindo pk para None é melhor, sinse Django pode criar corretamente um pk para você

object_copy = MyObject.objects.get(pk=...)
object_copy.pk = None
object_copy.save()




django-models