¿Cómo actualizar () una instancia de modelo único recuperada por get () en Django ORM?

5 minutos de lectura

avatar de usuario de zhuyxn
zhuyxn

Tengo una función que actualmente llama Models.object.get()que devuelve 0 o 1 objetos modelo:

  • si devuelve 0, creo una nueva instancia de modelo en el except DoesNotExist cláusula de la función.
  • De lo contrario, me gustaría actualizar los campos en la instancia preexistente, sin crear una nueva.

Originalmente estaba tratando de llamar .update() en la instancia que se encontró, pero .update()
parece ser solo invocable en un QuerySets. ¿Cómo me las arreglo para cambiar una docena de campos, sin llamar .filter() y comparar las longitudes para saber si tengo que crear o actualizar una instancia preexistente?

  • Primero puedes usar get_or_create, luego podrías mostrar algo de código…

    – Jingo

    12 de septiembre de 2012 a las 5:39

Avatar de usuario de Platinum Azure
Platino azur

Con la llegada de Django 1.7, ahora hay un nuevo update_or_create método QuerySet, que debería hacer exactamente lo que desea. Solo tenga cuidado con las posibles condiciones de carrera si la unicidad no se aplica en el nivel de la base de datos.

Ejemplo de la documentación:

obj, created = Person.objects.update_or_create(
    first_name="John", last_name="Lennon",
    defaults={'first_name': 'Bob'},
)

los update_or_create El método intenta obtener un objeto de la base de datos en función de lo dado. kwargs. Si se encuentra una coincidencia, actualiza los campos pasados ​​en el defaults diccionario.


Antes de Django 1.7:

Cambie los valores del campo del modelo según corresponda, luego llame .save() para persistir los cambios:

try:
    obj = Model.objects.get(field=value)
    obj.field = new_value
    obj.save()
except Model.DoesNotExist:
    obj = Model.objects.create(field=new_value)
# do something else with obj if need be

  • Pero supongamos que tengo 20 campos, ¿hay alguna forma más fácil de actualizarlos? Con .update() supongo que puedo pasar **kwargs?

    – zhuyxn

    12 de septiembre de 2012 a las 5:39

  • Intente buscar aquí: stackoverflow.com/questions/1576664/…

    – Platino Azur

    12 de septiembre de 2012 a las 5:44

  • Este enfoque tiene problemas de atomicidad: si dos instancias de la aplicación cambian la misma instancia en la base de datos, una podría golpear a la otra.

    – ceros

    10 de octubre de 2013 a las 6:22

  • También use esto solo si trabaja con el objeto. Si solo desea actualizar, sería mucho mejor usar el filtro con el atributo pk correspondiente. Esto tiene la ventaja de que solo se emite una consulta. Si usa get para una actualización, se usan dos consultas.

    – Chris

    7 febrero 2014 a las 11:44

  • @zhuyxn for attr, val in kwargs.iteritems(): setattr(obj, attr, val)

    – DylanYoung

    22/09/2016 a las 16:33

si solo desea actualizar el modelo si existe (sin crearlo):

Model.objects.filter(id = 223).update(field1 = 2)

consulta mysql:

UPDATE `model` SET `field1` = 2 WHERE `model`.`id` = 223

  • él está preguntando usando .get() específicamente no .filter()

    – bucle cerebral

    26 de septiembre de 2018 a las 5:27

A partir de Django 1.5, existe una propiedad update_fields al guardar el modelo. p.ej:

obj.save(update_fields=['field1', 'field2', ...])

https://docs.djangoproject.com/en/dev/ref/models/instances/

Prefiero este enfoque porque no crea un problema de atomicidad si tiene varias instancias de aplicaciones web que cambian diferentes partes de una instancia de modelo.

  • updated_fields es una lista, no un diccionario, entonces, ¿cómo pasaremos el valor actualizado para el campo?

    – bucle cerebral

    26 de septiembre de 2018 a las 5:34

  • @brainLoop, no pasaría los valores por separado. Los colocaría en el objeto mismo. obj.field1=valor antes de llamar a guardar en el obj.

    – Codescribblr

    21 de noviembre de 2019 a las 14:20

  • El argumento update_fields evita las condiciones de carrera al forzar una actualización del modelo para los campos especificados. De esta manera evitamos cargar el modelo en la memoria y luego guardarlo, lo que significa que incluso si existe otra instancia de la aplicación, no puede hacer nada en el medio.

    – dimyG

    2 de junio de 2020 a las 9:23

No sé qué tan bueno o malo es esto, pero puedes intentar algo como esto:

try:
    obj = Model.objects.get(id=some_id)
except Model.DoesNotExist:
    obj = Model.objects.create()
obj.__dict__.update(your_fields_dict) 
obj.save()

Avatar de usuario de Julien
julián

Aquí hay una combinación que puede mezclar en cualquier clase de modelo que le da a cada instancia un update método:

class UpdateMixin(object):
    def update(self, **kwargs):
        if self._state.adding:
            raise self.DoesNotExist
        for field, value in kwargs.items():
            setattr(self, field, value)
        self.save(update_fields=kwargs.keys())

los self._state.adding check comprueba si el modelo se guarda en la base de datos y, si no, genera un error.

(Nota: este update El método es para cuando desea actualizar un modelo y sabe que la instancia ya está guardada en la base de datos, respondiendo directamente a la pregunta original. el incorporado update_or_create El método presentado en la respuesta de Platinum Azure ya cubre el otro caso de uso).

Lo usaría así (después de mezclar esto en su modelo de usuario):

user = request.user
user.update(favorite_food="ramen")

Además de tener una API más agradable, otra ventaja de este enfoque es que llama al pre_save y post_save ganchos, mientras se evitan problemas de atomicidad si otro proceso está actualizando el mismo modelo.

Avatar de usuario de Rémi Héneault
Rémi Heneault

Como mencionó @Nils, puede usar el update_fields argumento de palabra clave del save() para especificar manualmente los campos a actualizar.

obj_instance = Model.objects.get(field=value)
obj_instance.field = new_value
obj_instance.field2 = new_value2

obj_instance.save(update_fields=['field', 'field2'])

los update_fields El valor debe ser una lista de los campos para actualizar como cadenas.

Ver https://docs.djangoproject.com/en/2.1/ref/models/instances/#especificando-que-campos-guardar

Avatar de usuario de Aragón
Aragón

Estoy usando el siguiente código en tales casos:

obj, created = Model.objects.get_or_create(id=some_id)

if not created:
   resp= "It was created"
else:
   resp= "OK"
   obj.save()

¿Ha sido útil esta solución?