web-dev-qa-db-fra.com

Django: Appeler .update () sur une seule instance de modèle récupérée par .get ()?

J'ai une fonction qui appelle actuellement Models.object.get(), qui renvoie 0 ou 1 objet de modèle. S'il renvoie 0, je crée une nouvelle instance de modèle dans la clause except DoesNotExist de la fonction. Sinon, j'aimerais mettre à jour les champs dans l'instance préexistante, sans en créer un nouveau. J'essayais à l'origine d'appeler .update() sur l'instance trouvée, mais .update() ne semble appelable que sur un ensemble de requêtes. Comment puis-je changer une douzaine de champs sans appeler .filter() et comparer les longueurs pour savoir si je dois créer ou mettre à jour une instance préexistante?

38
zhuyxn

Avec l'avènement de Django 1.7, il existe maintenant une nouvelle méthode update_or_create QuerySet qui devrait faire exactement ce que vous voulez. Faites juste attention aux conditions de concurrence potentielles si l'unicité n'est pas appliquée au niveau de la base de données.

Exemple de la documentation:

obj, created = Person.objects.update_or_create(
    first_name='John', last_name='Lennon', defaults=updated_values)

Pre-Django 1.7:

Modifiez les valeurs du champ de modèle selon vos besoins, puis appelez .save() pour conserver les modifications:

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
44
Platinum Azure

si vous voulez seulement mettre à jour le modèle s'il existe (sans le créer):

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

requête mysql:

UPDATE `model` SET `field1` = 2 WHERE `model`.`id` = 223
21
Eyal Ch

Je ne sais pas à quel point c'est bon ou mauvais, mais vous pouvez essayer quelque chose comme ceci:

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

Voici un mix que vous pouvez mélanger dans n'importe quelle classe de modèle et qui donne à chaque instance une méthode update:

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())

La vérification self._state.adding vérifie si le modèle est enregistré dans la base de données et, dans le cas contraire, génère une erreur.

(Remarque: Cette méthode update sert à mettre à jour un modèle et que vous savez que l'instance est déjà enregistrée dans la base de données, répondant directement à la question d'origine. La méthode intégrée update_or_create décrite dans la réponse de Platinum Azure couvre déjà l'autre cas d'utilisation.)

Vous l'utiliseriez comme ceci (après avoir mélangé ceci dans votre modèle utilisateur):

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

En plus d'avoir une API plus agréable, cette approche présente l'avantage d'appeler les hooks pre_save et post_save, tout en évitant les problèmes d'atomicité si un autre processus met à jour le même modèle.

5
Julien

Comme indiqué par @Nils, vous pouvez utiliser l'argument de mot clé update_fields de la méthode save() pour spécifier manuellement les champs à mettre à jour.

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

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

La valeur update_fields doit être une liste des champs à mettre à jour en tant que chaînes.

Voir https://docs.djangoproject.com/fr/2.1/ref/models/instances/#specifying-which-fields-to-save

0
Rémi Héneault

J'utilise le code suivant dans de tels cas:

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

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