Django - การแทนที่เมธอด Model.create ()?


89

เอกสาร Djangoเพียงตัวอย่างรายการเอาชนะและsave() delete()อย่างไรก็ตามฉันต้องการกำหนดการประมวลผลพิเศษสำหรับโมเดลของฉันเมื่อสร้างขึ้นเท่านั้น สำหรับทุกคนที่คุ้นเคยกับ Rails มันจะเทียบเท่ากับการสร้าง:before_createตัวกรอง เป็นไปได้หรือไม่

คำตอบ:


164

การลบล้าง__init__()จะทำให้โค้ดถูกเรียกใช้งานเมื่อใดก็ตามที่มีการสร้างอินสแตนซ์การเป็นตัวแทนของวัตถุไพ ธ อน ฉันไม่รู้จักราง แต่: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
ฉันคิดว่าคุณหมายถึงการลบล้างเมธอด manager create? นั่นเป็นวิธีแก้ปัญหาที่น่าสนใจ แต่จะใช้ไม่ได้ในกรณีที่วัตถุกำลังถูกสร้างขึ้นโดยใช้Object(**kwargs).save()หรือรูปแบบอื่น ๆ ในสิ่งนั้น
Zach

4
ฉันไม่คิดว่ามันเป็นการแฮ็ก เป็นหนึ่งในวิธีแก้ปัญหาอย่างเป็นทางการ
เล

6
ควรsuper(MyModel, self).save(*args, **kwargs)หรือไม่?
Mark Chackerian

1
บางทีการตรวจสอบอาจself.pkไม่ใช่ตัวเลือกที่ดีที่สุดในการตรวจสอบว่าวัตถุกำลังสร้างขึ้นใหม่หรือเพิ่งอัปเดต บางครั้งคุณระบุอ็อบเจ็กต์ id ในเวลาสร้าง (ค่าที่สร้างที่ไม่ใช่ฐานข้อมูลที่กำหนดเองเช่นKSUID) และจะทำให้ส่วนคำสั่งนี้ไม่ดำเนินการ ... มีself._state.addingค่าที่ต้องตรวจสอบว่ามีการบันทึกเป็นครั้งแรกหรือเพิ่งอัปเดตซึ่ง ช่วยในกรณีเหล่านั้น
Shahinism

22

นี่เป็นเรื่องเก่ามีคำตอบที่ได้รับการยอมรับว่าใช้ได้ผล (ของ Zach) และสำนวนอื่น ๆ ด้วยเช่นกัน (Michael Bylstra's) แต่เนื่องจากยังคงเป็นผลลัพธ์แรกใน Google ที่คนส่วนใหญ่เห็นฉันคิดว่าเราต้องการแนวทางปฏิบัติที่ดีที่สุดมากกว่า modern-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 วิธี)


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/thought/2007/10/29/ django-signs-vs-custom-save-method /

ในความคิดของฉันการใช้สัญญาณสำหรับงานนี้มีประสิทธิภาพมากกว่าอ่านง่ายกว่า แต่ยาวกว่า


วิธีนี้เป็นวิธีที่แนะนำแทนการล้อเล่นกับ internals วัตถุ แต่ถ้าคุณทำการปรับเปลี่ยนรูปแบบในคำถามและไม่เพียงแค่การสร้างอีกตัวอย่างข้างต้นไม่ลืมที่จะโทร instance.save()ดังนั้นในกรณีนี้ยังมีการลงโทษด้านประสิทธิภาพเนื่องจากจะมีหนึ่ง INSERT และหนึ่งแบบสอบถาม UPDATE ไปยังฐานข้อมูล
Mike Shultz

ลิงก์ไปยังสัญญาณเทียบกับวิธีการบันทึกแบบกำหนดเองเสีย
Sander Vanden Hautte

18

เพื่อตอบคำถามอย่างแท้จริง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เพื่อทำการประมวลผลเพิ่มเติมก่อนที่จะสร้างอินสแตนซ์จริง

หมายเหตุ:รหัสเช่น

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__()อย่าลืมที่จะเรียกผู้ปกครอง


ใช่นี่คือคำตอบ ไม่รู้ว่าฉันมองข้ามเรื่องนี้ไปได้อย่างไร ขอบคุณ Ignacio
ground5hark


1

คำตอบที่ต้องการนั้นถูกต้อง แต่การทดสอบเพื่อบอกว่าวัตถุกำลังสร้างไม่ได้ผลหรือไม่หากโมเดลของคุณมาจาก UUIDModel ฟิลด์ pk จะมีค่าอยู่แล้ว

ในกรณีนี้คุณสามารถทำได้:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.