0

I have a legacy database I created a model for it. It looks something like below.

class Account(models.Model):
  balance = models.DecimalField(max_digits=42, decimal_places=1, default=Decimal('0.0'))

When I run the below query I get the balance field as None insted of using the default value on the field.

Account.objects.get(pk=1).balance #returns None instead of Decimal(0.0).

I would like to get the default Decimal(0.0) when legacy balance on SQL database is NULL. Is there a way to tell django orm to give me the default Decimal(0.0) instead of None.

Thanks in advance.

EDIT: I know we can add custom methods or @property and make it a method that returns the balance or the default value but I Also run some other queries like one's below.

  Account.objects.values_list('balance') #returns [(1,),(None,),(None,)]

Which does not use the default value.

Pannu
  • 2,547
  • 2
  • 23
  • 29
  • It might be easier to convert the existing nulls to `0.00`, and change the schema to prevent nulls. – Alasdair Aug 05 '14 at 11:16
  • @Alasdair: I wish but apperantely thats not under my control. :( – Pannu Aug 05 '14 at 11:35
  • You could try writing a [custom model field](https://docs.djangoproject.com/en/1.6/howto/custom-model-fields/#writing-custom-model-fields). – Alasdair Aug 05 '14 at 13:40
  • 1
    @Alasdair: I'll give it a try. Thanks, didn't know about custom model fields. Awesome! ;) – Pannu Aug 06 '14 at 10:21

4 Answers4

1

Python allows you to find many different ways to a problem, so you could make a model method:

class MyAppModel(models.Model):
    ....

    def get_balace(self):
        return self.balance if self.balance else Decimal(0.0)
    ....

Or you could create a Manager (change it to your needs):

class MyAppManager(models.Manager):

    def get_queryset(self):
        return super(MyAppManager, self).get_queryset()

    def get_balances(self):
        results = []
        items = self.get_queryset().all()
        for item in items:
            balance = item.balance if item.balance else Decimal(0,0)
            results.append({'pk':item.pk,"balance":balance})
        return results

Then in your model:

class MyAppModel(models.Model):
    ...
    objects = MyAppManager()
    ...

And you query:

balances = MyAppModel.objects.get_balances()
petkostas
  • 7,250
  • 3
  • 26
  • 29
  • This will work, I have a better solution than this by adding `@property` to the method and making it an attribute rather than a method. But it does not help when you run queries like `Account.objects.values_list('balance')` – Pannu Aug 05 '14 at 11:44
  • The you can create a Manager :) – petkostas Aug 05 '14 at 11:53
  • I know you had posted the solution before, editing the answer, can you please post it again? Thanks :) – Pannu Aug 05 '14 at 13:01
  • 1
    added, refactor to your needs :) – petkostas Aug 05 '14 at 14:06
  • This is an old question (and answer), but for the sake of future interested ones, I feel the `self.balance if self.balance else Decimal(0.0)` would be much better solved with an annotation on the queryset, shoving the hard work onto the database instead of python. This will also avoid re-fetching the queryset and returning a strange custom-made object. – interDist Apr 14 '18 at 00:23
0

Specifying a default value on a column merely means that if you don't pass a value the default will be used. Are you explicitly setting the value of this column to NULL? If so, then NULL will be stored. You haven't provided the query you're using to populate the model.

Consider this table..

CREATE TABLE testtbl 
(
    id NOT NULL PRIMARY KEY,
    balance NUMERIC NULL DEFAULT 0
)

Now consider these two insert statements

INSERT INTO testtbl (id, balance) VALUES (1, NULL);
INSERT INTO testtbl (id) VALUES (2);

In the first case, NULL will be inserted even though you specified a default. In the second case, 0 will be used for balance.

Hope this helped.

  • Yes but its a legacy database which already has data inserted with column as `NULL` more over the inserts are not under my control they are done my an ETL. – Pannu Aug 05 '14 at 11:33
  • So you just want to display `NULL`s as 0.0? –  Aug 05 '14 at 12:17
  • Yes, but using `django models default` – Pannu Aug 05 '14 at 12:57
  • 1
    Can't you just convert the `None`s to 0's while rendering the template (`{{ value|default_if_none:0.0 }}`). Or if in a view, `if a is None: a = 5`? –  Aug 05 '14 at 13:33
  • Yes that is an option but its not an elegant one, I'd have to do it in the view level for every model containing decimal field. petkostas has an answer that might be DRY and might handel all models containing Decimal fields. ;) – Pannu Aug 06 '14 at 10:19
0

May be you can define a custom field class of your own where you override to_python method to handle this particular case of yours. That shall turn something in the lines of following code:

import decimal


class DecimalField(models.DecimalField):

  def to_python(self, value):
    if value is None:
      return decimal.Decimal('0.0')
    super(DecimalField, self).to_python(value)

Hope this is somewhere relevant to your problem. Cheers :)

-2

set editable=False. Look this for further info.

balance = models.DecimalField(max_digits=42, decimal_places=1, default=Decimal('0.0'), editable=False)
S.Ali
  • 664
  • 1
  • 5
  • 12
  • For the record, this did not work and didn't make any sense either. – Pannu Aug 05 '14 at 11:34
  • 1
    For the record, when you write `editable=False` it means that it wont show up in the admin panel, and also it will have the default value assigned every time a new record is entered in the DB. The default value that you mention in the model will be assigned to it. Try hard it will start making sense...:-) – S.Ali Aug 05 '14 at 16:42
  • I have no reference of `django-admin` in the question I'm not using it either. I hope you understand what legacy database means. Read and understand the question before answering. – Pannu Aug 06 '14 at 10:13
  • @Pannu no need to be offended, take it easy. We are here to help each other. Now coming to the comment, i explained that why i wrote editable=False, thats it. If you feel that the answer doesnot address your question you can downvote it. thats how SO works. – S.Ali Aug 06 '14 at 10:37
  • pls have a look at this http://stackoverflow.com/questions/755857/default-value-for-field-in-django-model – S.Ali Aug 06 '14 at 11:06