Django - Ghi đè phương thức Model.create ()?


88

Tài liệu Django chỉ liệt kê các ví dụ để ghi đè save()delete(). Tuy nhiên, tôi chỉ muốn xác định một số xử lý bổ sung cho các mô hình của mình khi chúng được tạo . Đối với bất kỳ ai quen thuộc với Rails, nó sẽ tương đương với việc tạo một :before_createbộ lọc. Điều này có khả thi không?

Câu trả lời:


161

Ghi đè __init__()sẽ khiến mã được thực thi bất cứ khi nào biểu diễn python của đối tượng được khởi tạo. Tôi không biết đường ray, nhưng một :before_createdbộ lọc nghe có vẻ giống như mã được thực thi khi đối tượng được tạo trong cơ sở dữ liệu. Nếu bạn muốn thực thi mã khi một đối tượng mới được tạo trong cơ sở dữ liệu, bạn nên ghi đè save(), kiểm tra xem đối tượng có pkthuộc tính hay không. Mã sẽ trông giống như sau:

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
Tôi thực sự đã tìm ra giải pháp bằng cách sử dụng signal: docs.djangoproject.com/en/dev/topics/signals (cụ thể là tín hiệu pre_save). Tuy nhiên, đây có vẻ là một giải pháp thực dụng hơn nhiều. Cảm ơn nhiều.
ground5hark

4
Tôi cho rằng bạn có nghĩa là ghi đè phương pháp quản lý create? Đó là một giải pháp thú vị, nhưng nó sẽ không hoạt động trong trường hợp đối tượng đang được tạo bằng cách sử dụng Object(**kwargs).save()hoặc bất kỳ biến thể nào khác trên đó.
Zach

4
Tôi không nghĩ đó là một vụ hack. Đó là một trong những giải pháp chính thức.
les

6
Có nên không super(MyModel, self).save(*args, **kwargs)?
Mark Chackerian

1
Có thể kiểm tra self.pkkhông phải là tùy chọn tốt nhất để kiểm tra xem đối tượng có phải là đối tượng mới được tạo hay chỉ đang cập nhật. Đôi khi bạn cung cấp id đối tượng trong thời gian tạo (một giá trị được tạo không phải cơ sở dữ liệu tùy chỉnh như KSUID), và nó sẽ khiến mệnh đề này không bao giờ thực thi ... Có self._state.addinggiá trị để đảm bảo nếu nó được lưu lần đầu tiên hay chỉ cập nhật, điều này giúp trong những trường hợp đó.
Shahinism

22

ví dụ về cách tạo tín hiệu post_save (từ 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)

đây là một cuộc thảo luận sâu sắc về việc liệu tốt nhất nên sử dụng tín hiệu hay phương pháp lưu tùy chỉnh https://web.archive.org/web/20120815022107/http://www.martin-geber.com/thought/2007/10/29/ django-signal-vs-custom-save-method /

Theo ý kiến ​​của tôi, sử dụng các tín hiệu cho nhiệm vụ này mạnh mẽ hơn, dễ đọc hơn nhưng dài hơn.


Đây là cách ưa thích thay vì gây rối với nội bộ đối tượng, tuy nhiên, nếu bạn thực hiện sửa đổi đối với mô hình được đề cập và không chỉ tạo một mô hình khác trong ví dụ trên, đừng quên gọiinstance.save() . Vì vậy, trong trường hợp này, cũng có một hình phạt về hiệu suất vì sẽ có một CHÈN và một truy vấn CẬP NHẬT cho cơ sở dữ liệu.
Mike Shultz,

Liên kết đến các tín hiệu so với các phương pháp lưu tùy chỉnh bị hỏng.
Sander Vanden Hautte

21

Câu này đã cũ, có một câu trả lời được chấp nhận có hiệu quả (của Zach) và một câu thành ngữ hơn (của Michael Bylstra), nhưng vì nó vẫn là kết quả đầu tiên trên Google mà hầu hết mọi người đều thấy, tôi nghĩ chúng ta cần một phương pháp hay nhất hiện đại hơn câu trả lời kiểu ở đây :

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)

Vấn đề là đây:

  1. sử dụng tín hiệu (đọc thêm tại đây trong tài liệu chính thức )
  2. sử dụng một phương thức để tạo không gian tên đẹp (nếu nó có ý nghĩa) ... và tôi đã đánh dấu nó là @classmethodthay @staticmethodvì vì rất có thể bạn sẽ cần tham chiếu đến các thành viên lớp tĩnh trong mã

Thậm chí còn rõ ràng hơn nếu Django lõi có một post_createtín hiệu thực tế . (Imho nếu bạn cần truyền một đối số boolean để thay đổi hành vi của một phương thức, đó phải là 2 phương thức.)


15

Để trả lời câu hỏi theo nghĩa đen, createphương thức trong trình quản lý mô hình là một cách tiêu chuẩn để tạo các đối tượng mới trong Django. Để ghi đè, hãy làm điều gì đó như

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

Trong ví dụ này, tôi đang ghi đè phương thức của Trình quản lý createđể thực hiện một số xử lý bổ sung trước khi phiên bản thực sự được tạo.

LƯU Ý: Mã như

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

sẽ thực thi createphương thức đã sửa đổi này, nhưng mã như

my_new_unsaved_instance = MyModel(my_field='my_field value')

sẽ không.


3

Ghi đè __init__()sẽ cho phép bạn thực thi mã khi mô hình được khởi tạo. Đừng quên gọi cho phụ huynh __init__().


À vâng, đây là câu trả lời. Không biết làm thế nào tôi bỏ qua điều này. Cảm ơn Ignacio.
ground5hark


1

Câu trả lời ưa thích là đúng nhưng kiểm tra để biết đối tượng đang được tạo không hoạt động nếu mô hình của bạn bắt nguồn từ UUIDModel. Trường pk sẽ có một giá trị.

Trong trường hợp này, bạn có thể làm như sau:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.