Créer un modèle Django ou mettre à jour s'il existe

115

Je veux créer un objet modèle, comme Person, si l'identifiant de la personne n'existe pas, ou j'obtiendrai cet objet de personne.

Le code pour créer une nouvelle personne comme suit:

class Person(models.Model):
    identifier = models.CharField(max_length = 10)
    name = models.CharField(max_length = 20)
    objects = PersonManager()

class PersonManager(models.Manager):
    def create_person(self, identifier):
        person = self.create(identifier = identifier)
        return person

Mais je ne sais pas où vérifier et obtenir l'objet Personne existant.

user1687717
la source

Réponses:

169

Si vous recherchez un cas d'utilisation "mettre à jour s'il existe sinon créer", veuillez consulter @Zags excellente réponse


Django a déjà un get_or_create, https://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create

Pour vous, cela pourrait être:

id = 'some identifier'
person, created = Person.objects.get_or_create(identifier=id)

if created:
   # means you have created a new person
else:
   # person just refers to the existing one
Bakkal
la source
J'utilise django 1.9, et ça me donne: AttributeError: l'objet 'QuerySet' n'a pas d'attribut 'update_or_create'
Ofek Gila
1
Ce @OfekGila est étrange, à la fois le code source et la documentation confirment QuerySetaupdate_or_create
Bakkal
2
Dans mon cas, à la place de l'identifiant, j'ai un groupe de champs qui forme unique_together. Comment est l'utilisation de ce cas?
Rakmo
192

On ne sait pas si votre question demande la méthode get_or_create (disponible au moins à partir de Django 1.3) ou la méthode update_or_create (nouveau dans Django 1.7). Cela dépend de la manière dont vous souhaitez mettre à jour l'objet utilisateur.

L'utilisation de l'échantillon est la suivante:

# In both cases, the call will get a person object with matching
# identifier or create one if none exists; if a person is created,
# it will be created with name equal to the value in `name`.

# In this case, if the Person already exists, its existing name is preserved
person, created = Person.objects.get_or_create(
        identifier=identifier, defaults={"name": name}
)

# In this case, if the Person already exists, its name is updated
person, created = Person.objects.update_or_create(
        identifier=identifier, defaults={"name": name}
)
Zags
la source
12
+1 update_or_createcar pour ce cas d'utilisation, il vaut mieux que de get_or_createsimplement appeler save()à nouveau l'objet.
bakkal
Dans mon cas, à la place de l'identifiant, j'ai un groupe de champs qui forme unique_together. Comment est l'utilisation de ce cas?
Rakmo
1
L'identifiant @OmkarDeshpande n'est qu'un exemple; vous pouvez transmettre n'importe quelle requête django valide, telle quePerson.objects.get_or_create(a=a, b=b, c=c, defaults={"name": name})
Zags
Que faire si je souhaite ajouter plus de champs à la personne créée que les champs par défaut?
Lo Bellin
@LoBell dans l' defaultsargument de ces fonctions est l'endroit où vous spécifiez les champs que vous souhaitez ajouter au modèle. Cela n'a rien à voir avec les valeurs par défaut spécifiées sur les champs de votre modèle
Zags le
10

Django prend en charge cela, vérifiez get_or_create

person, created = Person.objects.get_or_create(name='abc')
if created:
    # A new person object created
else:
    # person object already exists
Aamir Adnan
la source
4

Pour seulement une petite quantité d'objets, update_or_create fonctionne bien, mais si vous le faites sur une grande collection, il ne sera pas bien mis à l'échelle. update_or_create exécute toujours d'abord un SELECT puis un UPDATE.

for the_bar in bars:
    updated_rows = SomeModel.objects.filter(bar=the_bar).update(foo=100)
        if not updated_rows:
            # if not exists, create new
            SomeModel.objects.create(bar=the_bar, foo=100)

Cela n'exécutera au mieux que la première requête de mise à jour, et uniquement si elle ne correspondait à aucune ligne, exécutera une autre requête INSERT. Ce qui augmentera considérablement vos performances si vous vous attendez à ce que la plupart des lignes soient réellement existantes.

Tout dépend de votre cas d'utilisation. Si vous attendez principalement des insertions, la commande bulk_create () pourrait être une option.

Andreas Bergström
la source
3

J'ai pensé que j'ajouterais une réponse car le titre de votre question semble demander comment créer ou mettre à jour, plutôt que d'obtenir ou de créer comme décrit dans le corps de la question.

Si vous vouliez créer ou mettre à jour un objet, la méthode .save () a déjà ce comportement par défaut, à partir de la documentation :

Django évite la nécessité d'utiliser des instructions SQL INSERT ou UPDATE. Plus précisément, lorsque vous appelez save (), Django suit cet algorithme:

Si l'attribut de clé primaire de l'objet est défini sur une valeur qui prend la valeur True (c'est-à-dire une valeur autre que None ou la chaîne vide), Django exécute un UPDATE. Si l'attribut de clé primaire de l'objet n'est pas défini ou si UPDATE n'a rien mis à jour, Django exécute un INSERT.

Il convient de noter que lorsqu'ils disent «si la mise à jour n'a rien mis à jour», ils se réfèrent essentiellement au cas où l'id que vous avez donné à l'objet n'existe pas déjà dans la base de données.

Tom Manterfield
la source
1

Si l'une des entrées lors de la création est une clé primaire, cela suffira:

Person.objects.get_or_create(id=1)

Il sera automatiquement mis à jour s'il existe car deux données avec la même clé primaire ne sont pas autorisées.

Aminah Nuraini
la source