Bir django model custom save () yönteminde, yeni bir nesneyi nasıl tanımlamanız gerekir?


172

Yeni bir kayıt (var olan bir kaydı güncelleştirmiyorum) kaydederken Django model nesnesinin save () yönteminde özel bir eylemi tetiklemek istiyorum.

Öz kaydın yeni olduğunu ve güncellenmediğini garanti etmek için (self.id! = Yok) kontrolü gerekli ve yeterli mi? Bunun göz ardı edebileceği özel durumlar var mı?


Lütfen doğru cevap olarak stackoverflow.com/a/35647389/8893667 seçin . Cevap pek çok durumda işe yaramıyorUUIDField pk
Kotlinboy

Yanıtlar:


204

Güncel: açıklama ile self._stateözel örnek değişkeni değildir, ancak yolu çatışmaları önlemek için bu ismi, kontrol self._state.addingartık kontrol etmek tercih yoludur.


self.pk is None:

yeni bir Model nesnesi içinde True değerini döndürür, nesne bir UUIDFieldonun gibi değilse primary_key.

Endişelenmeniz gereken köşe vakası, id dışındaki alanlarda benzersizlik kısıtlamaları olup olmadığıdır (örneğin, diğer alanlardaki ikincil benzersiz dizinler). Bu durumda, elinde yeni bir kayıt olabilir, ancak kaydedemezsiniz.


20
Sen kullanmalıdır is notziyade !=zaman ile kimlik denetimi Nonenesne
Ben James

3
Tüm modellerin id özniteliği yoktur, yani diğerini a ile genişleten bir model models.OneToOneField(OtherModel, primary_key=True). Sanırım kullanmalısınself.pk
AJP

4
Bu bazı durumlarda ÇALIŞMAZ. Lütfen bu yanıtı kontrol edin: stackoverflow.com/a/940928/145349
fjsj

5
Bu doğru cevap değil. UUIDFieldA birincil anahtar olarak kullanılıyorsa self.pk, asla None.
Daniel van Flymen

1
Yan not: Bu cevap önceden tarihlendirilmiş UUIDField.
Dave W. Smith

190

Alternatif kontrol yöntemi , modeli self.pkkontrol edebilirizself._state

self._state.adding is True oluşturma

self._state.adding is False güncellenmesi

Bu sayfadan aldım


12
Özel birincil anahtar alanı kullanmanın tek doğru yolu budur.
webtweakers

9
Değil emin nasıl tüm ilgili ayrıntıları self._state.addingçalışır, ancak adil uyarı her zaman eşit görünüyor ki Falsebunu kontrol ediyoruz, eğer sonra arayarak super(TheModel, self).save(*args, **kwargs): github.com/django/django/blob/stable/1.10.x/django/db/models/ …
agilgur5

1
Bu doğru yoldur ve doğru cevap olarak değerlendirilmeli / ayarlanmalıdır.
flungo

7
@guival: _stateözel değil; gibi _meta, bu alan adları ile önlemek karışıklığa bir alt çizgi öneki oluyor. (Bağlantılı belgelerde nasıl kullanıldığına dikkat edin.)
Ry-

2
Bu en iyi yol. Kullandım is_new = self._state.adding, sonra super(MyModel, self).save(*args, **kwargs)ve sonraif is_new: my_custom_logic()
kotrfa

45

Denetleme , model için birincil anahtar self.idolduğunu varsayar id. Daha genel bir yol pk kısayolunu kullanmaktır .

is_new = self.pk is None


15
Profesyonel İpucu: Bu koymak ÖNCEsuper(...).save() .
sbdchd

39

Onay self.pk == Noneolan değil , nesnenin eklendiğinde veya veritabanında güncellenmiş olacak olup olmadığını belirlemek için yeterli.

Django O / RM, PK konumunda bir şey olup olmadığını kontrol etmek için özellikle kötü bir kesmek içerir ve eğer bir UPDATE yaparsanız, aksi takdirde bir INSERT yapın (PK, None ise bu bir INSERT için optimize edilir).

Bunu yapmasının nedeni, bir nesne oluşturulduğunda PK'yi ayarlamanıza izin verilmesidir. Birincil anahtar için bir dizi sütununa sahip olduğunuz yerde yaygın olmasa da, diğer birincil anahtar alanı türleri için bu geçerli değildir.

Eğer gerçekten bilmek istiyorsanız O / RM ne yapmak zorunda ve veritabanına bakın.

Tabii ki kodunuzda belirli bir durum var ve bunun için self.pk == Nonebilmeniz gereken her şeyi size söyleyecek gibi görünüyor , ancak bu genel bir çözüm değil .


İyi bir nokta! Ben asla yeni nesneler için pk ayarlamak çünkü benim uygulama (Bu hiçbiri birincil anahtar kontrol) ile kurtulmak. Ancak bu kesinlikle yeniden kullanılabilir bir eklenti veya çerçevenin bir parçası için iyi bir kontrol olmaz.
MikeN

1
Bu, birincil anahtarı kendiniz ve veritabanı aracılığıyla atadığınızda özellikle geçerlidir. Bu durumda yapılacak en kesin şey db'ye bir gezi yapmaktır.
Constantine M

1
Uygulama kodunuz açıkça pks belirtmemiş olsa bile test durumlarınız için fikstürler olabilir. Bununla birlikte, testlerden önce yaygın olarak yüklendikleri için sorun olmayabilir.
Risadinha

1
Bu özellikle UUIDFielda Birincil Anahtar olarak kullanıldığında geçerlidir : anahtar DB düzeyinde doldurulmaz, bu yüzden self.pkher zaman geçerlidir True.
Daniel van Flymen

10

Sadece "oluşturulmuş" kwargs gönderen post_save sinyaline bağlanabilirsiniz, doğruysa nesneniz eklenmiştir.

http://docs.djangoproject.com/en/stable/ref/signals/#post-save


8
Çok fazla yük varsa, bu durum yarış koşullarına neden olabilir. Bunun nedeni post_save sinyalinin kaydetme sırasında, ancak işlem yapılmadan önce gönderilmesidir. Bu sorunlu olabilir ve hata ayıklamak çok zor olabilir.
Abel Mohler

Bir şeylerin değişip değişmediğinden emin değilim (eski sürümlerden), ancak sinyal işlemcileri aynı işlem içinde çağrıldığından, her yerde bir hata tüm işlemi geri alır. Kullanıyorum ATOMIC_REQUESTS, bu yüzden varsayılandan gerçekten emin değilim.
Tim Tisdall

7

self.idVe force_insertbayrağı kontrol edin .

if not self.pk or kwargs.get('force_insert', False):
    self.created = True

# call save method.
super(self.__class__, self).save(*args, **kwargs)

#Do all your post save actions in the if block.
if getattr(self, 'created', False):
    # So something
    # Do something else

Yeni oluşturulan nesne (öz) sahip olduğu, çünkü bu kullanışlı pkdeğeri


5

Bu görüşmeye çok geç kaldım, ancak kendisiyle ilişkilendirilen varsayılan bir değeri olduğunda self.pk ile ilgili bir sorunla karşılaştım.

Bunu aşmanın yolu modele bir date_created alanı eklemektir

date_created = models.DateTimeField(auto_now_add=True)

Buradan gidebilirsin

created = self.date_created is None


4

UUIDFieldBir birincil anahtarınız olsa bile çalışan bir çözüm için (diğerlerinin belirttiği gibi Nonesadece geçersiz kıldığınızda değil save), Django'nun kaydetme sonrası sinyalini takabilirsiniz . Bunu models.py'nize ekleyin :

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=MyModel)
def mymodel_saved(sender, instance, created, **kwargs):
    if created:
        # do extra work on your instance, e.g.
        # instance.generate_avatar()
        # instance.send_email_notification()
        pass

Bu geri arama saveyöntemi engeller , böylece AJAX çağrıları için formlar veya Django REST çerçevesi kullanıyor olsanız da yanıtınız tel üzerinden geri gönderilmeden önce tetikleyici bildirimleri gibi işlemleri yapabilir veya modeli daha da güncelleyebilirsiniz. Tabii ki, sorumlu bir şekilde kullanın ve kullanıcılarınızı bekletmek yerine ağır görevleri bir iş kuyruğuna boşaltın :)



1

Bunu yapmanın yaygın yolu budur.

db'ye ilk kez kaydedilirken kimlik verilecektir


0

Bu yukarıdaki senaryoların tümü için işe yarar mı?

if self.pk is not None and <ModelName>.objects.filter(pk=self.pk).exists():
...

bu ek bir veritabanı isabet neden olur.
David Schumann

0
> def save_model(self, request, obj, form, change):
>         if form.instance._state.adding:
>             form.instance.author = request.user
>             super().save_model(request, obj, form, change)
>         else:
>             obj.updated_by = request.user.username
> 
>             super().save_model(request, obj, form, change)

Cleaner_data.get () kullanarak, ben bir örnek olup olmadığını belirlemek mümkün, ayrıca null ve boş nerede bir CharField vardı nerede doğru. Bu, oturum
açan

-3

Nesneyi (veri) güncellediğiniz veya eklediğinizi bilmek self.instance.fieldnameiçin formunuzda kullanın . Formunuzda temiz bir işlev tanımlayın ve geçerli değer girişinin öncekiyle aynı olup olmadığını kontrol edin, değilse güncelliyorsunuz demektir.

self.instanceve self.instance.fieldnameyeni değerle karşılaştırın

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.