Aynı Modeldeki Başka Bir Alana Dayalı Django Modeli Alan Varsayılanı


92

Bir denek adı ve baş harflerini içermesini istediğim bir modelim var (veriler bir şekilde anonimleştirilir ve baş harflerle izlenir).

Şimdi yazdım

class Subject(models.Model):

    name = models.CharField("Name", max_length=30)
    def subject_initials(self):
        return ''.join(map(lambda x: '' if len(x)==0 else x[0],
                           self.name.split(' ')))
    # Next line is what I want to do (or something equivalent), but doesn't work with
    # NameError: name 'self' is not defined
    subject_init = models.CharField("Subject Initials", max_length=5, default=self.subject_initials)

Son satırda belirtildiği gibi, baş harflerin veritabanında bir alan olarak (addan bağımsız olarak) depolanmasını tercih ederim, ancak bu ad alanına dayalı bir varsayılan değerle başlatılır. Ancak, django modellerinin bir 'benliği' olmadığı için sorunlar yaşıyorum.

Satırı olarak değiştirirsem, subject_init = models.CharField("Subject initials", max_length=2, default=subject_initials)syncdb yapabilirim, ancak yeni konular yaratamam.

Bu Django'da mümkün müdür, çağrılabilir bir işleve sahip olmak, başka bir alanın değerine bağlı olarak bazı alanlara bir varsayılan değer verir mi?

(Merak edenler için, mağazamın baş harflerini ayrı ayrı ayırmak istememin nedeni, tuhaf soyadlarının izlediklerimden farklı olabileceği ender durumlarda. Örneğin, başka biri Konu 1 adlı Konu 1'in "John O'Mallory" "JO" yerine "JM" ve yönetici olarak düzenlemeyi düzeltmek istiyor.)

Yanıtlar:


89

Modellerin kesinlikle bir "özü" vardır! Sadece bir model sınıfının bir özniteliğini bir model örneğine bağlı olarak tanımlamaya çalışıyorsunuz; Bu mümkün değildir, çünkü siz sınıfı ve özniteliklerini tanımlamadan önce örnek mevcut değildir (ve olamaz).

İstediğiniz efekti elde etmek için model sınıfının save () yöntemini geçersiz kılın. Gerekli örnekte istediğiniz değişiklikleri yapın, ardından gerçek kaydetmeyi yapmak için süper sınıfın yöntemini çağırın. İşte hızlı bir örnek.

def save(self, *args, **kwargs):
    if not self.subject_init:
        self.subject_init = self.subject_initials()
    super(Subject, self).save(*args, **kwargs)

Bu, dokümantasyondaki Model Yöntemlerini Geçersiz Kılma bölümünde ele alınmıştır .


5
Python 3'te , referans verilen dokümantasyondaki örnekte olduğu gibi super().save(*args, **kwargs)( Subject, selfargümanlar olmadan ) basitçe çağırabileceğinizi unutmayın .
Kurt Peek

1
Çok kötü, bu, onu kaydederken işlediği için Yönetici tarafında (örneğin, otomatik artırılmış bir alan için) özellikle yararlı olan, başka bir modele dayalı bir varsayılan model öznitelik değeri tanımlamaya izin vermez. Yoksa yanlış mı anlıyorum?
Vadorequest

18

Bunu yapmanın daha iyi bir yolu olup olmadığını bilmiyorum, ama sen yapabilirsiniz bir sinyal işleyicisi kullanmak için sinyal :pre_save

from django.db.models.signals import pre_save

def default_subject(sender, instance, using):
    if not instance.subject_init:
        instance.subject_init = instance.subject_initials()

pre_save.connect(default_subject, sender=Subject)

1
Sen ithal post_saveyerine pre_save.
Ali Rasim Koçal

1
@arkocal: Uyarılar için teşekkürler, sadece düzeltti. Bu gibi durumlarda kendiniz düzenlemeler önerebilirsiniz, bu gibi şeylerin düzeltilmesine yardımcı olur :)
Gabi Purcaru

1
Bu uygulama eksik gibi görünüyor **kwargstüm alıcı fonksiyonları olmalıdır argüman göre docs.djangoproject.com/en/2.0/topics/signals/... ?
Kurt Peek

Belgeler, kontrol ettiğiniz kod yolları için sinyallere karşı tavsiyelerde bulunur . Geçersiz kılmanın kabul edilen yanıtı save(), belki de geçersiz kılmaya değiştirildiğinde __init__, daha açık olduğu için daha iyidir.
Adam Johnson

7

Django sinyallerini kullanarak , modelden sinyal alarak bupost_init oldukça erken yapılabilir .

from django.db import models
import django.dispatch

class LoremIpsum(models.Model):
    name = models.CharField(
        "Name",
        max_length=30,
    )
    subject_initials = models.CharField(
        "Subject Initials",
        max_length=5,
    )

@django.dispatch.receiver(models.signals.post_init, sender=LoremIpsum)
def set_default_loremipsum_initials(sender, instance, *args, **kwargs):
    """
    Set the default value for `subject_initials` on the `instance`.

    :param sender: The `LoremIpsum` class that sent the signal.
    :param instance: The `LoremIpsum` instance that is being
        initialised.
    :return: None.
    """
    if not instance.subject_initials:
        instance.subject_initials = "".join(map(
                (lambda x: x[0] if x else ""),
                instance.name.split(" ")))

post_initO örneğinde başlatılması çıkarmış kez sinyal sınıfı tarafından gönderilir. Bu şekilde, örnek, namenull yapılamayan alanlarının ayarlanıp ayarlanmadığını test etmeden önce bir değer alır .


Belgeler, kontrol ettiğiniz kod yolları için sinyallere karşı tavsiyelerde bulunur . Geçersiz kılmanın kabul edilen yanıtı save(), belki de geçersiz kılmaya değiştirildiğinde __init__, daha açık olduğu için daha iyidir.
Adam Johnson

2

Gabi Purcaru'nun cevabının alternatif bir uygulaması olarak pre_save, receiverdekoratörü kullanarak da sinyale bağlanabilirsiniz :

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


@receiver(pre_save, sender=Subject)
def default_subject(sender, instance, **kwargs):
    if not instance.subject_init:
        instance.subject_init = instance.subject_initials()

Bu alıcı işlevi, **kwargstüm sinyal işleyicilerinin https://docs.djangoproject.com/en/2.0/topics/signals/#receiver-functions'a göre alması gereken joker karakter anahtar sözcük bağımsız değişkenlerini de alır .

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.