Поле модели Django по умолчанию отключено от другого поля в той же модели - PullRequest
75 голосов
/ 07 декабря 2010

У меня есть модель, в которой я хотел бы указать имя субъекта и его инициалы.(Данные несколько анонимны и отслеживаются по инициалам.)

Прямо сейчас я написал

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)

Как указано в последней строке, я бы предпочел иметь возможность иметь инициалы на самом делехраниться в базе данных как поле (независимо от имени), но оно инициализируется значением по умолчанию на основе поля имени.Тем не менее, у меня возникают проблемы, поскольку у моделей django, похоже, нет «я».

Если я изменю строку на subject_init = models.CharField("Subject initials", max_length=2, default=subject_initials), я могу выполнить syncdb, но не могу создавать новые темы.

Возможно ли это в django, если для вызываемой функции по умолчанию задано значениекакое-то поле, основанное на значении другого поля?

(Для любопытных причина, по которой я хочу отделить инициалы своего магазина отдельно, в редких случаях, когда странные фамилии могут отличаться от тех, которые я отслеживаю. Например,, кто-то еще решил, что субъект 1 Инициалы «John O'Mallory» являются «JM», а не «JO», и хочет исправить его как администратор.)

Ответы [ 4 ]

74 голосов
/ 07 декабря 2010

Модели, безусловно, имеют «я»!Просто вы пытаетесь определить атрибут класса модели как зависимый от экземпляра модели;это невозможно, так как экземпляр не существует (и не может существовать) до того, как вы определите класс и его атрибуты.

Чтобы получить желаемый эффект, переопределите метод save () класса модели.Внесите любые необходимые изменения в экземпляр, затем вызовите метод суперкласса для фактического сохранения.Вот краткий пример.

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

Это описано в Переопределение методов модели в документации.

14 голосов
/ 07 декабря 2010

Я не знаю, есть ли лучший способ сделать это, но вы можете использовать обработчик сигнала для 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)
4 голосов
/ 09 января 2018

Используя сигналы Джанго , это можно сделать довольно рано, получив сигнал post_init от модели.

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_init отправляется классом после инициализации экземпляра. Таким образом, экземпляр получает значение для name перед проверкой, установлены ли его необнуляемые поля.

1 голос
/ 28 июня 2018

В качестве альтернативного варианта ответа Gabi Purcaru вы также можете подключиться к сигналу pre_save с помощью декоратора receiver :

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

Эта функция приемника также принимает аргументы с подстановочными словами **kwargs, которые должны принимать все обработчики сигналов в соответствии с https://docs.djangoproject.com/en/2.0/topics/signals/#receiver-functions.

...