Django - переопределение метода Model.create ()?


87

В документации Django перечислены только примеры для переопределения save()и delete(). Однако я хотел бы определить дополнительную обработку для моих моделей только тогда, когда они созданы . Для любого, кто знаком с Rails, это будет эквивалентно созданию :before_createфильтра. Это возможно?

Ответы:


160

Переопределение __init__()приведет к тому, что код будет выполняться всякий раз, когда создается представление объекта Python. Я не знаю рельсов, но :before_createdдля меня фильтр звучит так, как будто это код, который должен выполняться при создании объекта в базе данных. Если вы хотите выполнить код при создании нового объекта в базе данных, вы должны переопределить save(), проверяя, имеет ли объект pkатрибут или нет. Код будет выглядеть примерно так:

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
Я действительно нашел решение, используя сигналы: docs.djangoproject.com/en/dev/topics/signals (в частности, сигнал pre_save). Однако это кажется гораздо более прагматичным решением. Огромное спасибо.
Ground5hark

4
Я полагаю, вы имеете в виду переопределить метод менеджера create? Это интересное решение, но оно не сработает в тех случаях, когда объект создается с использованием Object(**kwargs).save()или любых других его вариаций.
Zach

3
Не думаю, что это взлом. Это одно из официальных решений.
les

6
Не должно быть super(MyModel, self).save(*args, **kwargs)?
Марк Чакериан

1
Возможно, проверка self.pk- не лучший вариант, чтобы проверить, создается ли объект заново или просто обновляется. Иногда вы указываете идентификатор объекта во время создания (настраиваемое значение, не созданное базой данных, например KSUID), и это приведет к тому, что это предложение никогда не будет выполняться ... Есть self._state.addingзначение, чтобы убедиться, сохраняется ли он в первый раз или просто обновляется, что помогает в тех случаях.
Shahinism

22

пример того, как создать сигнал post_save (из 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)

вот вдумчивое обсуждение того, что лучше использовать: сигналы или пользовательские методы сохранения https://web.archive.org/web/20120815022107/http://www.martin-geber.com/ Thinkt/2007/10/29/ django-сигналы-vs-пользовательский-метод сохранения /

На мой взгляд, использование сигналов для этой задачи является более надежным, легким для чтения, но более длинным.


Это предпочтительный способ вместо того, чтобы возиться с внутренними компонентами объекта, однако, если вы вносите изменения в рассматриваемую модель, а не просто создаете другую в приведенном выше примере, не забудьте вызватьinstance.save() . Таким образом, в этом случае также наблюдается снижение производительности, так как будет один запрос INSERT и один запрос UPDATE к базе данных.
Майк Шульц,

Ссылка на сигналы и пользовательские методы сохранения не работает.
Sander Vanden Hautte

21

Это старый, есть принятый ответ, который работает (Зак), а также более идиоматический (Майкл Билстра), но поскольку это все еще первый результат в Google, который видит большинство людей, я думаю, нам нужно больше передовых методов современного django ответ в стиле здесь :

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)

Дело вот в чем:

  1. использовать сигналы (подробнее читайте здесь, в официальных документах )
  2. используйте метод для красивого пространства имен (если это имеет смысл) ... и я пометил его как @classmethodвместо, @staticmethodпотому что, скорее всего, вам понадобится ссылаться на статические члены класса в коде

Еще чище было бы, если бы ядро ​​Django имело реальный post_createсигнал. (Imho, если вам нужно передать логический аргумент, чтобы изменить поведение метода, это должно быть 2 метода.)


15

Чтобы ответить на вопрос буквально, createметод в менеджере модели - это стандартный способ создания новых объектов в Django. Чтобы переопределить, сделайте что-нибудь вроде

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

В этом примере я переопределяю метод createметода Manager, чтобы выполнить некоторую дополнительную обработку перед фактическим созданием экземпляра.

ПРИМЕЧАНИЕ: код вроде

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

выполнит этот модифицированный createметод, но код вроде

my_new_unsaved_instance = MyModel(my_field='my_field value')

не буду.


3

Переопределение __init__()позволит вам выполнить код при создании экземпляра модели. Не забудьте позвонить родителям __init__().


Ах да, это был ответ. Не знаю, как я это проглядел. Спасибо, Игнасио.
Ground5hark


1

Предпочтительный ответ правильный, но тест, определяющий, создается ли объект, не работает, если ваша модель является производной от UUIDModel. Поле pk уже будет иметь значение.

В этом случае вы можете сделать это:

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.