views:

78

answers:

3

Given a model

class Template(models.Model):
    colour = models.CharField(default="red", blank = True, null=True)

How can I arrange it so that any access to colour either returns the value stored in the field, or if the field is blank/null then it returns red?

The

default=red
will put "red" in the field when it's first created, but if it's then edited to blank I'd like access to it to return "red", not ""

Updated: I tried the properties solution suggested below, but I'm using JSON serialisation to implement a REST API and the properties (e.g. colour) don't get serialised, and serialising the _colour breaks the API

+1  A: 

Use the save method to implement this.

def save( self, *args, **kw ):
    if self.colour is None:
        self.colour= 'red'
    super( ThisModel, self ).save( *args, **kw )
S.Lott
This modifies the stored value, which isn't what I want to happen
Malcolm Box
+3  A: 

You can create a separate method instead:

def get_colour(self):
    if not self.colour:
        return 'red'
    else:
        return self.colour

An alternative is to use property.

http://www.djangoproject.com/documentation/models/properties/

dannyroa
I prefer this solution because it is not modifying the value of the model field. Unless modification of the field is what you want.
jathanism
I can't change my code to access the field through get_colour everywhere - so all read/writes have to stay through colour.Properties look like it could work. Is there any easy way to generate lots of repetitive property functions (I have lots of colour fields in the model, and each will need wrapping)
Malcolm Box
@Malcolm Box: the better way to handle your requirement of lots of colour fields would be to have an abstract class with the appropriate properties that are shared between many models (colour and any other you might have). See: http://docs.djangoproject.com/en/dev/topics/db/models/#id6
celopes
@celopes - don't see how an abstract base class helps here, I would still have to wrap the fields. It turns out it's quite easy to write a generic getter and setter function, then each property line is just field = property(lambda x: x._getter('field'), lambda x,y : x.setter('field', y))
Malcolm Box
@Malcolm Box: I wasn't proposing it as a solution to your problem - that is why it is just a comment and not an answer to your question. I was pointing out that whenever you have the same property in multiple models, it is a good idea to abstract it away to a common ancestor. It eliminates repetition in your code. Instead of having to write the same property in multiple models, you would implement it only in the abstract base class.
celopes
A: 

Here's how I ended up solving this problem.

First some preconditions: I have existing code that accesses these field names (e.g. 'colour') and the field is also serialised (by django-rest-interface) as part of an API. Neither of these things can be changed.

Trying the properties method worked fine, except that colour was no longer a field so didn't get serialised. Result: broken API

Then moved to the save() solution, along with a custom attribute on each field which should behave like this:

class ColourChoices(models.Model):
    colour1 = models.CharField()
    colour1.colour_default = "red"
    colour2 = models.CharField()
    colour2.colour_default = "blue"

    def save(self, *args, **kwargs):
        # force colour fields to default values
        for f in [ x for x in self._meta.fields if hasattr(x, 'colour_default') ]:
            if self.__getattribute__(f.attname) == "":
                self.__setattr__(f.attname, f.colour_default)
       super(ColourChoices, self).save(*args,**kwargs)

Now all works fine and as required.

The only problem with this solution is that if the default values change it's impossible to tell which database fields should have updated defaults, and which are just accidentally the same colour as the old default. However for my application I can live with this.

Malcolm Box