Recarregar objeto django do banco de dados

159

É possível atualizar o estado de um objeto django do banco de dados? Quero dizer comportamento aproximadamente equivalente a:

new_self = self.__class__.objects.get(pk=self.pk)
for each field of the record:
    setattr(self, field, getattr(new_self, field))

ATUALIZAÇÃO: Foi encontrada uma guerra de reabertura / correção de erros no rastreador: http://code.djangoproject.com/ticket/901 . Ainda não entendo por que os mantenedores não gostam disso.

grep
fonte
Em um contexto SQL comum, isso não faz sentido. O objeto de banco de dados só pode ser alterado após a conclusão da transação e a commmit. Depois de fazer isso, você terá que aguardar a confirmação da próxima transação SQL. Por que fazer isso? Quanto tempo você espera pela próxima transação?
precisa saber é o seguinte
Isso parece uma função desnecessária; já é possível apenas procurar novamente o objeto no banco de dados.
Stephan
Gostaria que esta bem, mas foi desligado várias vezes aqui
eruciform
2
Não é apropriado porque os objetos de modelo do Django são proxies. Se você obtiver a mesma linha da tabela em dois objetos - x1 = X.objects.get (id = 1); x2 = X.objects.get (id = 1), eles serão testados como iguais, mas são objetos diferentes e o estado não é compartilhado. Você pode alterar os dois independentemente e salvá-los - o último salvo determina o estado da linha no banco de dados. Portanto, é correto recarregar com atribuição simples - x1 = X.objects.get (id = 1). Ter um método de recarga levaria muitas pessoas a inferir erroneamente que x1.f = 'novo valor'; (x1.f == x2.f) é True.
Paul Whipp 06/02

Respostas:

258

A partir do Django 1.8, a atualização de objetos é incorporada. Link para docs .

def test_update_result(self):
    obj = MyModel.objects.create(val=1)
    MyModel.objects.filter(pk=obj.pk).update(val=F('val') + 1)
    # At this point obj.val is still 1, but the value in the database
    # was updated to 2. The object's updated value needs to be reloaded
    # from the database.
    obj.refresh_from_db()
    self.assertEqual(obj.val, 2)
Tim Fletcher
fonte
@ fcracker79 Sim, foi implementado apenas no 1.8. Para versões anteriores do Django, é melhor seguir uma das outras respostas.
Tim Fletcher
1
Não sabe o que significa "Todos os campos não diferidos são atualizados" mencionados nos documentos significa?
Yunti 13/11/2015
1
@Yunti Você pode adiar os campos ou solicitar explicitamente apenas um subconjunto de campos, e o objeto resultante será preenchido apenas parcialmente. refresh_from_dbatualizará apenas esses campos já preenchidos.
301_Moved_Permanently
Não foi possível encontrar detalhes nos documentos, mas gera uma DoesNotExistexceção corretamente se o objeto subjacente foi excluído ao chamar refresh_from_db. PARA SUA INFORMAÇÃO.
Tim Tisdall 17/01
28

Achei relativamente fácil recarregar o objeto do banco de dados da seguinte forma:

x = X.objects.get(id=x.id)
Rory
fonte
19
Sim, mas ... depois disso, você deve atualizar todas as referências a este objeto. Não é muito prático e propenso a erros.
grep
2
Achei isso necessário quando o Celery atualizou meu objeto no db fora do django, aparentemente o django manteve um cache do objeto, pois não fazia ideia de que havia mudado.
precisa saber é o seguinte
3
do django.db.models.loading import get_model; = exemplo get_model (exemplo) .objects.get (pk = instance.pk)
Erik
1
O @grep acabou de perder 2 horas escrevendo um teste para este caso de uso: 1: Inicialize um modelo; 2: Atualize o modelo através de um formulário; 3: Teste se o novo valor está atualizado .... Então, sim, propenso a erros.
Vlad-ardelean
3
Eu acho que refresh_from_dbresolve todos esses problemas.
Flimm
16

Em referência ao comentário do @ grep, não deveria ser possível:

# Put this on your base model (or monkey patch it onto django's Model if that's your thing)
def reload(self):
    new_self = self.__class__.objects.get(pk=self.pk)
    # You may want to clear out the old dict first or perform a selective merge
    self.__dict__.update(new_self.__dict__)

# Use it like this
bar.foo = foo
assert bar.foo.pk is None
foo.save()
foo.reload()
assert bar.foo is foo and bar.foo.pk is not None
Eloff
fonte
Obrigado pela solução. Se apenas isso permitisse vários votos positivos!
User590028 de
11
O Django agora fornece refresh_from_dbmétodo.
Flimm
9

Como o @Flimm apontou, esta é uma solução realmente impressionante:

foo.refresh_from_db()

Isso recarrega todos os dados do banco de dados no objeto.

Ron
fonte