Django - Überschreiben der Model.create () -Methode?

87

In den Django-Dokumenten sind nur Beispiele zum Überschreiben von save()und aufgeführt delete(). Ich möchte jedoch eine zusätzliche Verarbeitung für meine Modelle nur dann definieren, wenn sie erstellt werden . Für alle, die mit Rails vertraut sind, entspricht dies dem Erstellen eines :before_createFilters. Ist das möglich?

ground5hark
quelle

Antworten:

160

Das Überschreiben __init__()würde dazu führen, dass Code immer dann ausgeführt wird, wenn die Python-Darstellung des Objekts instanziiert wird. Ich kenne keine Rails, aber ein :before_createdFilter klingt für mich so, als würde er ausgeführt, wenn das Objekt in der Datenbank erstellt wird. Wenn Sie Code ausführen möchten, wenn ein neues Objekt in der Datenbank erstellt wird, sollten Sie überschreiben save()und prüfen, ob das Objekt ein pkAttribut hat oder nicht. Der Code würde ungefähr so ​​aussehen:

def save(self, *args, **kwargs):
    if not self.pk:
        # This code only happens if the objects is
        # not in the database yet. Otherwise it would
        # have pk
    super(MyModel, self).save(*args, **kwargs)
Zach
quelle
7
Ich habe tatsächlich eine Lösung mit Signalen gefunden: docs.djangoproject.com/de/dev/topics/signals (speziell das Signal pre_save). Dies scheint jedoch eine viel pragmatischere Lösung zu sein. Vielen Dank.
Ground5Hark
4
Ich nehme an, Sie meinen, die Manager-Methode zu überschreiben create? Das ist eine interessante Lösung, aber es würde nicht funktionieren, wenn das Objekt mit Object(**kwargs).save()oder einer anderen Variation davon erstellt wird.
Zach
3
Ich denke nicht, dass es ein Hack ist. Es ist eine der offiziellen Lösungen.
Les
6
Sollte es nicht sein super(MyModel, self).save(*args, **kwargs)?
Mark Chackerian
1
Möglicherweise ist das Überprüfen nach self.pknicht die beste Option, um zu überprüfen, ob das Objekt neu erstellt oder nur aktualisiert wird. Manchmal geben Sie die Objekt-ID in der Erstellungszeit an (ein angepasster, nicht von der Datenbank generierter Wert wie KSUID), und dies führt dazu, dass diese Klausel niemals ausgeführt wird. Es gibt einen self._state.addingWert, der sicherstellt, ob sie zum ersten Mal gespeichert oder nur aktualisiert wird hilft in diesen Fällen.
Shahinismus
22

Ein Beispiel zum Erstellen eines post_save-Signals (von http://djangosnippets.org/snippets/500/ )

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    """Create a matching profile whenever a user object is created."""
    if created: 
        profile, new = UserProfile.objects.get_or_create(user=instance)

Hier finden Sie eine ausführliche Diskussion darüber, ob es am besten ist, Signale oder benutzerdefinierte Speichermethoden zu verwenden. https://web.archive.org/web/20120815022107/http://www.martin-geber.com/thought/2007/10/29/ Django-Signale-gegen-benutzerdefinierte-Speichermethode /

Meiner Meinung nach ist die Verwendung von Signalen für diese Aufgabe robuster, leichter zu lesen, aber länger.

Michael Bylstra
quelle
Dies ist die bevorzugte Methode, anstatt mit Objektinternalen herumzuspielen. Wenn Sie jedoch Änderungen am betreffenden Modell vornehmen und im obigen Beispiel nicht nur ein anderes erstellen, vergessen Sie nicht, den Aufruf durchzuführeninstance.save() . In diesem Fall gibt es also auch einen Leistungsverlust, da die Datenbank eine INSERT- und eine UPDATE-Abfrage enthält.
Mike Shultz
Die Verknüpfung zu den Signalen im Vergleich zu benutzerdefinierten Speichermethoden ist unterbrochen.
Sander Vanden Hautte
20

Dies ist alt, hat eine akzeptierte Antwort, die funktioniert (Zach's), und eine idiomatischere (Michael Bylstra's), aber da es immer noch das erste Ergebnis bei Google ist, das die meisten Leute sehen, denke ich, brauchen wir einen modernen Best-Django mit mehr Best Practices Stil Antwort hier :

from django.db.models.signals import post_save

class MyModel(models.Model):
    # ...
    @classmethod
    def post_create(cls, sender, instance, created, *args, **kwargs):
        if not created:
            return
        # ...what needs to happen on create

post_save.connect(MyModel.post_create, sender=MyModel)

Der Punkt ist folgender:

  1. Verwenden Sie Signale (lesen Sie mehr hier in den offiziellen Dokumenten )
  2. Verwenden Sie eine Methode für einen netten Namespace (wenn dies sinnvoll ist) ... und ich habe sie als @classmethodstatt markiert, da @staticmethodSie höchstwahrscheinlich statische Klassenmitglieder im Code referenzieren müssen

Noch sauberer wäre es, wenn Core Django ein tatsächliches post_createSignal hätte. (Imho, wenn Sie ein boolesches Argument übergeben müssen, um das Verhalten einer Methode zu ändern, sollten dies 2 Methoden sein.)

NeuronQ
quelle
15

Um die Frage wörtlich zu beantworten, ist die createMethode im Manager eines Modells eine Standardmethode zum Erstellen neuer Objekte in Django. Um dies zu überschreiben, machen Sie so etwas wie

from django.db import models

class MyModelManager(models.Manager):
    def create(self, **obj_data):
        # Do some extra stuff here on the submitted data before saving...
        # For example...
        obj_data['my_field'] = my_computed_value(obj_data['my_other_field'])

        # Now call the super method which does the actual creation
        return super().create(**obj_data) # Python 3 syntax!!

class MyModel(models.model):
    # An example model
    my_field = models.CharField(max_length=250)
    my_other_field = models.CharField(max_length=250)

    objects = MyModelManager()

In diesem Beispiel überschreibe ich die Methodenmethode des Managers create, um eine zusätzliche Verarbeitung durchzuführen, bevor die Instanz tatsächlich erstellt wird.

HINWEIS: Code wie

my_new_instance = MyModel.objects.create(my_field='my_field value')

führt diese modifizierte createMethode aus, aber Code wie

my_new_unsaved_instance = MyModel(my_field='my_field value')

wird nicht.

Mark Chackerian
quelle
3

Durch Überschreiben __init__()können Sie Code ausführen, wenn das Modell instanziiert wird. Vergessen Sie nicht, die Eltern anzurufen __init__().

Ignacio Vazquez-Abrams
quelle
Ah ja das war die Antwort. Ich weiß nicht, wie ich das übersehen habe. Danke Ignacio.
Ground5Hark
1

Die bevorzugte Antwort ist richtig, aber der Test, um festzustellen, ob das Objekt erstellt wird, funktioniert nicht, wenn Ihr Modell von UUIDModel abgeleitet ist. Das pk-Feld hat bereits einen Wert.

In diesem Fall können Sie Folgendes tun:

already_created = MyModel.objects.filter(pk=self.pk).exists()

Julio Carrera
quelle