Django - Model.create () yöntemini geçersiz kılıyor musunuz?


89

Django dokümanlar yalnızca geçersiz kılmaya yönelik örnekler listelemek save()ve delete(). Ancak, modellerim için yalnızca oluşturulduklarında bazı ekstra işlemleri tanımlamak istiyorum . Rails'e aşina olan herkes için bu, bir :before_createfiltre oluşturmaya eşdeğer olacaktır . Mümkün mü?

Yanıtlar:


164

Geçersiz kılma __init__(), nesnenin python temsili her oluşturulduğunda kodun çalıştırılmasına neden olur. Rayları bilmiyorum, ancak bir :before_createdfiltre, nesne veritabanında oluşturulduğunda yürütülecek kod gibi geliyor bana. Veritabanında yeni bir nesne oluşturulduğunda kodu yürütmek istiyorsanız save(), nesnenin bir pkniteliğe sahip olup olmadığını kontrol ederek geçersiz kılmalısınız . Kod şuna benzer:

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
Aslında sinyalleri kullanarak bir çözüm buldum: docs.djangoproject.com/en/dev/topics/signals (özellikle pre_save sinyali). Ancak bu çok daha pragmatik bir çözüm gibi görünüyor. Çok teşekkürler.
ground5hark

4
Yönetici yöntemini geçersiz kılmayı mı kastettiğinizi varsayıyorum create? Bu ilginç bir çözüm, ancak nesnenin Object(**kwargs).save()veya bununla ilgili başka bir varyasyon kullanılarak oluşturulduğu durumlarda işe yaramaz .
Zach

4
Bunun bir hack olduğunu sanmıyorum. Resmi çözümlerden biridir.
les

6
Olması gerekmiyor super(MyModel, self).save(*args, **kwargs)mu?
Mark Chackerian

1
Belki de kontrol self.pketmek, nesnenin yeni oluşturulup oluşturulmadığını veya sadece güncellenip güncellenmediğini kontrol etmek için en iyi seçenek değildir. Bazen oluşturma sırasında nesne kimliği sağlarsınız (veritabanı tarafından oluşturulmuş özelleştirilmiş olmayan bir değer gibi KSUID) ve bu cümlenin hiçbir zaman yürütülmemesine neden olur ... self._state.addingİlk kez mi kaydediyor yoksa yalnızca güncelleniyor mu emin olmak için değer var bu durumlarda yardımcı olur.
Shahinism

22

Bu eski, işe yarayan kabul edilmiş bir yanıtı var (Zach'in) ve daha deyimsel bir yanıtı da var (Michael Bylstra's), ancak yine de çoğu kişinin Google'da gördüğü ilk sonuç olduğu için, daha iyi uygulamalara ihtiyacımız olduğunu düşünüyorum modern django stil cevabı burada :

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)

Önemli olan şudur:

  1. sinyalleri kullanın ( resmi belgelerde buradan daha fazlasını okuyun )
  2. (mantıklı varsa) güzel için ad için bir yöntem kullanmak ... ve bunu olarak işaretlenmiş @classmethodyerine @staticmethodbüyük olasılıkla kodda statik sınıf üyelerini başvurmak gerek bitireceğiz çünkü

Çekirdek Django'nun gerçek bir post_createsinyali olsaydı, daha da temiz olurdu . (Bir yöntemin davranışını değiştirmek için bir mantıksal argüman iletmeniz gerekiyorsa, bu 2 yöntem olmalıdır.)


22

post_save sinyalinin nasıl oluşturulacağına dair bir örnek ( http://djangosnippets.org/snippets/500/ adresinden )

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)

burada sinyalleri mi yoksa özel kaydetme yöntemlerini mi kullanmanın en iyisi olduğuna dair düşünceli bir tartışma var https://web.archive.org/web/20120815022107/http://www.martin-geber.com/thought/2007/10/29/ django-sinyaller-vs-özel-kaydetme yöntemi /

Kanımca bu görev için sinyal kullanmak daha sağlam, okunması daha kolay ancak daha uzun.


Bu, nesne içi öğelerle uğraşmak yerine tercih edilen yoldur, ancak, söz konusu modelde değişiklikler yaparsanız ve yukarıdaki örnekte yalnızca bir tane oluşturmakla kalmazsanız, aramayı unutmayıninstance.save() . Yani bu durumda, veri tabanına bir INSERT ve bir UPDATE sorgusu olacağı için bir performans cezası da vardır.
Mike Shultz

Sinyallere ve özel kaydetme yöntemlerine olan bağlantı koptu.
Sander Vanden Hautte

18

Soruyu tam anlamıyla yanıtlamak için, createbir modelin yöneticisindeki yöntem Django'da yeni nesneler oluşturmanın standart bir yoludur. Geçersiz kılmak için şunun gibi bir şey yapın:

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

Bu örnekte, createörnek gerçekten yaratılmadan önce bazı ekstra işlemler yapmak için Yöneticinin yöntem yöntemini geçersiz kılıyorum .

NOT: Kod gibi

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

bu değiştirilmiş createyöntemi çalıştıracak , ancak kod gibi

my_new_unsaved_instance = MyModel(my_field='my_field value')

olmayacak.


3

Geçersiz kılma __init__(), model başlatıldığında kodu çalıştırmanıza izin verir. Ebeveynleri aramayı unutma __init__().


Ah evet bu cevaptı. Bunu nasıl gözden kaçırdım bilmiyorum. Teşekkürler Ignacio.
ground5hark


1

Tercih edilen yanıt doğrudur ancak modeliniz UUIDModel'den türetilmişse nesnenin oluşturulup oluşturulmadığını söyleyen test çalışmaz. Pk alanı zaten bir değere sahip olacaktır.

Bu durumda şunu yapabilirsiniz:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.