Django: стойността по подразбиране на полето от екземплярите на собствения модел

Как мога да направя стойност по подразбиране за поле, което да бъде взето от съществуващи обекти на модел?

Пробвах тези и не се получи:

1)

class ModelA(models.Model):
    fieldA = models.CharField(default=self.get_previous())

    def get_previous(self):
        return ModelA.objects.all()[0].fieldA

NameError: name 'self' is not defined

2)

class ModelA(models.Model):
    fieldA = models.CharField(default=ModelA.get_previous())

    @staticmethod
    def get_previous():
        return ModelA.objects.all()[0].fieldA

NameError: name 'ModelA' is not defined

3)

class ModelA(models.Model):
    fieldA = models.CharField(default=get_previous())

def get_previous():
    return ModelA.objects.all()[0].fieldA

NameError: global name 'get_previous' is not defined

4)

def get_previous():
    return ModelA.objects.all()[0].fieldA

class ModelA(models.Model):
    fieldA = models.CharField(default=get_previous())

NameError: global name 'ModelA' is not defined

Ясно ми е защо 3) и 4) няма да работят. Мога да си представя защо 1) няма да работи - изглежда, че свойствата на класа не могат да се отнасят до екземпляра (т.е. себе си). Мога да си представя защо 2) няма да работи - очевидно няма препратка към ModelA, докато интерпретаторът не премине през целия клас.

И така, как да подходя към това?


person grucha    schedule 26.08.2010    source източник


Отговори (3)


Във вашите примери трябва да премахнете оператора за повикване ().

Понастоящем операторът се изпълнява незабавно при първия цикъл на четене-разбор. Като вместо това посочи името на символа, класът Django получава указател на функция, който ще изпълни, когато действително се нуждае от стойността по подразбиране.

Примерът става:

def get_previous():
    return ModelA.objects.all()[0].fieldA

class ModelA(models.Model):
    fieldA = models.CharField(default=get_previous)

Ако ще направите това за много полета, помислете за замяна на функцията save, така че да трябва да извлечете предишния обект от базата данни само веднъж.

person vdboor    schedule 26.08.2010
comment
Добра точка! В моя случай обаче имам много полета, за които да получа стойност по подразбиране, така че ще ми трябва отделна функция за всяко едно. (Но ще обмисля използването на ламбда, за да обобщя това, нещо като lambda: get_default('field_name') ) - person grucha; 26.08.2010
comment
По същия начин трябва да можете да направите default=lambda: ModelA.objects.all()[0].fieldA. - person Michael Mior; 10.08.2012
comment
Имайте предвид, че използването на ламбда функция по подразбиране ще затрудни миграциите, тъй като ламбда функциите не могат да бъдат сериализирани. - person Ironbeard; 07.09.2017

Ако искате просто да изведете стойност по подразбиране, заменете метода __getattr__() по този начин:

class ModelA(models.Model):
  # your fields
  def __getattr__(self, name):
    if(name == 'fieldA' and !self.fieldA):
      return self.get_previous()
    else:
      return super(ModelA, self).__getattr__(name)

Запазването на стойността по подразбиране от обекта ще бъде малко по-трудно. Първото решение, което ми идва на ум, замяна на save() метод (вярвам, че има по-просто решение)

person Anpher    schedule 26.08.2010

Вариант 1: Използвайте моделни формуляри. Това са формуляри, базирани на модели, чиито стойности можете лесно да попълните със стойности от конкретен екземпляр. Тази опция е добра, ако искате стойността по подразбиране като нещо, което да се показва на потребителя във формуляри, очевидно.

Опция 2: Замяна на метода за запазване на модела и кодирайте проверката там - ако fieldA е None, тогава поставете някаква стойност в него. Тази опция е по-добра, ако искате стойността по подразбиране да работи в слоя данни, а не само като стойности по подразбиране на формуляр (т.е. също и за екземпляри, които създавате по други начини).

person Ofri Raviv    schedule 26.08.2010
comment
Искам да покажа стойността по подразбиране на потребителя, а не да я запазя, ако няма стойност, така че Option_1 от вашия отговор да изглежда добре. Въпреки че харесвам отговора на vdboor повече, тъй като е ниво на python и не е съвсем специфично за django. Djangonauts, поправете ме, ако греша ;-) - person grucha; 26.08.2010