Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a Django model field's default value be defined by a function dependent on a foreign parent model?

I'm trying to have the default value of Report's fee be based on a parent model's attributes. I don't want to do this in save(), because the field needs to be presented to the user if they choose to override the value before saving.

Here are the three methods I've tried, in addition to passing only function pointers (i.e. without ()). The errors are raised when I run python manage.py shell.

#1
from django.db import models

class Job(models.Model):
    veryImportant = models.IntegerField()
    def get_fee(self):
        return 2 * self.veryImportant

class Report(models.Model):
    job = models.ForeignKey(Job)
    overridableFee = models.DecimalField(default=job.get_fee(), max_digits=7, decimal_places=2)

#gives...
#AttributeError: 'ForeignKey' object has no attribute 'get_fee'

#2
from django.db import models

class Job(models.Model):
    veryImportant = models.IntegerField()

class Report(models.Model):
    job = models.ForeignKey(Job)
    overridableFee = models.DecimalField(default=self.set_fee(), max_digits=7, decimal_places=2)
    def set_fee(self):
        overridableFee =  2 * self.job.veryImportant

#gives...
#NameError: name 'self' is not defined

#3
from django.db import models

class Job(models.Model):
    veryImportant = models.IntegerField()
    def get_fee():
        return 2 * veryImportant

class Report(models.Model):
    job = models.ForeignKey(Job)
    overridableFee = models.DecimalField(max_digits=7, decimal_places=2)
    def __init__(self, *args, **kwargs):
        self.overridableFee = self.job.get_fee()
        super(models.Model, self).__init__(*args, **kwargs)

#gives...
#TypeError: object.__init__() takes no parameters

The error with #3 could be that I just have no idea how to override init properly. I copied something out of another answer and gave up after it didn't work.

If none of these will work, I can always revert to just setting the fee after I create each Report in the view, but I'd rather do it automatically when the Report is created, for sanity.

EDIT:

Ended up going with #3, fixed with Yuji's answer and modified to make sure any possible fee set from the shell overrides what job.get_fee() wants.

def __init__(self, *args, **kwargs):
    super(Report, self).__init__(*args, **kwargs)
    if self.overridableFee == None and self.job and not self.pk:
        self.overridableFee = self.job.get_fee()
like image 649
wizpig64 Avatar asked Oct 25 '11 03:10

wizpig64


1 Answers

Your last example could potentially work with some work:

  1. First all, you need to __init__ on your class, not models.Model
  2. You need to set your attribute after the model has been initialized
  3. You need check if the model has been saved, or else your model will revert to the overridable fee every time you load it.

-

class Job(models.Model):
    veryImportant = models.IntegerField()
    def get_fee():
        return 2 * veryImportant

class Report(models.Model):
    job = models.ForeignKey(Job)
    overridableFee = models.DecimalField(max_digits=7, decimal_places=2)
    def __init__(self, *args, **kwargs):
        super(Report, self).__init__(*args, **kwargs)
        if not self.id:
            self.overridableFee = self.job.get_fee() 
like image 121
Yuji 'Tomita' Tomita Avatar answered Dec 01 '22 00:12

Yuji 'Tomita' Tomita