94 votes

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 soit 0 soit 1 objet modèle. S'il renvoie 0, je crée une nouvelle instance de modèle dans le fichier except DoesNotExist de la fonction. Sinon, je voudrais mettre à jour les champs dans l'instance préexistante, sans en créer une nouvelle. À l'origine, j'essayais d'appeler .update() sur l'instance qui a été trouvée, mais .update() semble ne pouvoir être appelé que sur un QuerySets. Comment puis-je modifier une douzaine de champs, sans appeler .filter() et en comparant les longueurs pour savoir si je dois créer ou mettre à jour une instance préexistante ?

108voto

Platinum Azure Points 22380

Avec l'arrivée de la version 1.7 de Django, il existe désormais une nouvelle fonction update_or_create La méthode QuerySet, qui devrait faire exactement ce que vous voulez. Faites simplement attention aux conditions de course potentielles si l'unicité n'est pas appliquée au niveau de la base de données.

Exemple tiré de la documentation :

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

En update_or_create La méthode tente de récupérer un objet dans la base de données. en fonction de l'information kwargs . Si une correspondance est trouvée, il met à jour le champs transmis dans la méthode defaults dictionnaire.


Pré-Django 1.7 :

Modifiez les valeurs des champs du modèle comme il convient, puis appelez .save() pour maintenir les changements :

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

55voto

Eyal Ch Points 186

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

44voto

Nils Points 1885

À partir de Django 1.5, il existe une propriété update_fields sur l'enregistrement du modèle, par exemple :

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

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

Je préfère cette approche car elle ne crée pas de problème d'atomicité si vous avez plusieurs instances d'applications Web qui modifient différentes parties d'une instance de modèle.

25voto

Rohan Points 22386

Je ne sais pas si c'est bon ou mauvais, mais vous pouvez essayer quelque chose comme ça :

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

10voto

Julien Points 15

Voici une mixine que vous pouvez intégrer à n'importe quelle classe de modèle et qui donne à chaque instance un nom d'utilisateur et un mot de passe. update méthode :

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

En self._state.adding vérifie si le modèle est enregistré dans la base de données et, si ce n'est pas le cas, soulève une erreur.

_(Note : Cette update est utilisée lorsque vous souhaitez mettre à jour un modèle et que vous savez que l'instance est déjà enregistrée dans la base de données, ce qui répond directement à la question initiale. La méthode intégrée update_or_create présentée dans la réponse de Platinum Azure couvre déjà l'autre cas d'utilisation)._

Vous l'utiliserez comme suit (après l'avoir intégré à votre modèle d'utilisateur) :

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

En plus d'avoir une API plus agréable, un autre avantage de cette approche est qu'elle appelle la fonction pre_save y post_save tout en évitant les problèmes d'atomicité si un autre processus met à jour le même modèle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X