Django - Sovrascrivi il metodo Model.create ()?


89

La documentazione di Django elenca solo esempi per sovrascrivere save()e delete(). Tuttavia, vorrei definire alcune elaborazioni extra per i miei modelli solo quando vengono creati . Per chiunque abbia familiarità con Rails, sarebbe l'equivalente di creare un :before_createfiltro. È possibile?

Risposte:


164

L'override __init__()provocherebbe l'esecuzione del codice ogni volta che viene istanziata la rappresentazione python dell'oggetto. Non conosco rails, ma un :before_createdfiltro mi suona come se fosse un codice da eseguire quando l'oggetto viene creato nel database. Se vuoi eseguire codice quando viene creato un nuovo oggetto nel database, devi sovrascriverlo save(), controllando se l'oggetto ha un pkattributo o meno. Il codice sarebbe simile a questo:

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)

7
In realtà ho trovato una soluzione utilizzando i segnali: docs.djangoproject.com/en/dev/topics/signals (il segnale pre_save, in particolare). Tuttavia, questa sembra essere una soluzione molto più pragmatica. Grazie mille.
ground5hark

4
Presumo che intendi ignorare il metodo manager create? Questa è una soluzione interessante, ma non funzionerebbe nei casi in cui l'oggetto viene creato utilizzando Object(**kwargs).save()o qualsiasi altra variazione su quello.
Zach

4
Non penso sia un hack. È una delle soluzioni ufficiali.
Les

6
Non dovrebbe essere super(MyModel, self).save(*args, **kwargs)?
Mark Chackerian

1
Forse controllare self.pknon è l'opzione migliore per verificare se l'oggetto è stato appena creato o semplicemente aggiornato. A volte si fornisce l'ID oggetto al momento della creazione (un valore personalizzato non generato dal database come KSUID) e questa clausola non verrà mai eseguita ... C'è un self._state.addingvalore per assicurarsi che si stia salvando per la prima volta o semplicemente aggiornando, il che aiuta in quei casi.
Shahinismo

22

Questo è vecchio, ha una risposta accettata che funziona (quella di Zach) e anche una più idiomatica (quella di Michael Bylstra), ma poiché è ancora il primo risultato su Google che la maggior parte delle persone vede, penso che abbiamo bisogno di un django moderno più best practice risposta di stile qui :

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)

Il punto è questo:

  1. usa i segnali (leggi di più qui nella documentazione ufficiale )
  2. usa un metodo per una buona spaziatura dei nomi (se ha senso) ... e l'ho contrassegnato come @classmethodinvece di @staticmethodperché molto probabilmente finirai per aver bisogno di fare riferimento ai membri della classe statica nel codice

Ancora più pulito sarebbe se il core Django avesse un post_createsegnale effettivo . (Imho se devi passare un argomento booleano per cambiare il comportamento di un metodo, dovrebbero essere 2 metodi.)


22

un esempio di come creare un segnale post_save (da 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)

ecco una discussione ponderata sull'opportunità di utilizzare segnali o metodi di salvataggio personalizzati https://web.archive.org/web/20120815022107/http://www.martin-geber.com/ Thought/ 2007/10/29/ django-signal-vs-custom-save-method /

Secondo me l'utilizzo di segnali per questo compito è più robusto, più facile da leggere ma più lungo.


Questo è il modo preferito invece di scherzare con gli interni degli oggetti, tuttavia, se apporti modifiche al modello in questione, e non solo creandone un altro nell'esempio precedente, non dimenticare di chiamareinstance.save() . Quindi, in questo caso, c'è anche una riduzione delle prestazioni poiché ci saranno una query INSERT e una UPDATE nel database.
Mike Shultz

Il collegamento ai segnali e ai metodi di salvataggio personalizzati è interrotto.
Sander Vanden Hautte

18

Per rispondere letteralmente alla domanda, il createmetodo nel gestore di un modello è un modo standard per creare nuovi oggetti in Django. Per ignorare, fai qualcosa di simile

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 questo esempio, sovrascrivo il metodo di Manager create per eseguire un'elaborazione aggiuntiva prima che l'istanza venga effettivamente creata.

NOTA: codice simile

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

eseguirà questo createmetodo modificato , ma un codice simile

my_new_unsaved_instance = MyModel(my_field='my_field value')

non lo farò.


3

L'override __init__()ti consentirà di eseguire il codice quando viene creata un'istanza del modello. Non dimenticare di chiamare i genitori __init__().


Ah sì, questa era la risposta. Non so come ho trascurato questo. Grazie Ignacio.
ground5hark


1

La risposta preferita è corretta ma il test per stabilire se l'oggetto viene creato non funziona se il modello deriva da UUIDModel. Il campo pk avrà già un valore.

In questo caso, puoi farlo:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.