Reputation: 27107
According to the docs, if you give a model field a callable as default, then this default method gets no parameters:
https://docs.djangoproject.com/en/1.8/ref/models/fields/#default
def contact_default():
return {"email": "[email protected]"}
contact_info = JSONField("ContactInfo", default=contact_default)
I am missing the access to the other attributes of the instance. At best I would like to access self
.
Use case:
class Face(models.Model):
male=models.BooleanField()
beard=models.NullBooleanField()
If the face is a not male, then beard
should be set to False.
The default value should be applied only for new instances.
Upvotes: 4
Views: 1007
Reputation: 10618
To answer your question: it's not possible to read the attributes of the model instance being created in the default function. pre_save
signals will be run every time an instance is saved, so you don't want to do that either. There are ways around that, but signals make the code harder to read (IMHO).
What you might want to do is add a convenient method to the manager of the model manager.
class FaceManager(models.Manager):
def create_person(male, beard=None):
if not male and beard is None:
return self.create(male=False, beard=False)
else:
return self.create(male=male, beard=beard)
class Face(models.Model):
objects = FaceManager()
You can then create a non-bearded, non-female face:
Face.objects.create_person(male=False)
Upvotes: 1
Reputation: 119
I would override save.
def save(self):
if self.male:
self.beard = True
super(Face, self).save()
Upvotes: 0
Reputation: 404
You can't use self in the function for defining default values. You would have to use pre_save signal for this.
from django.db.models.signals import post_save, pre_save
@receiver(pre_save, sender=Face)
def check_beard(sender, instance=None, created=False, **kwargs):
if not instance.id and not instance.male: # Check instance id if it saving for first time, and set default
instance.beard = False
Upvotes: 2
Reputation: 9767
You can use django-smartfields for that:
from django.db import models
from smartfields import fields
from smartfields.dependencies import Dependency
def has_beard(is_male, instance=instance, **kwargs):
if not is_male:
return False
class Face(models.Model):
male=fields.BooleanField(dependencies=[
Dependency(attname='beard', default=has_beard
])
beard=models.NullBooleanField()
This will basically act as a default
on the beard
field, except you get access to male
field value and a model instance. If you would like to have this sort of check every time male
field is changed than you can extend smartfields.processors.BaseProcessor
and pass it as a processor
keyword argument to a Dependency
.
In case your actual problem, you can do it this way:
def contact_default(value, instance=instance, **kwargs):
return {"email": "[email protected]"}
class MyModel(models.Model):
# ... other fields
contact_info = fields.JSONField("ContactInfo", dependencies=[
Dependency(default=contact_default)
])
So, in this example it's a self dependency, so whenever contact_info is null
it will call the default
function.
Upvotes: 0
Reputation: 33690
There are several approaches you can choose from to interject and set the value of a model field that depends on another.
The suggested approach is to actually use Form.clean()
. This would allow you to encapsulate business logic at it's the source.
Another approach would be to override Model.save()
or register a handler for django.db.models.signals.pre_save()
, though these are usually reserved for overarching relational constraints and application requirements.
Unless your creating the models programmatically, without user input which would otherwise be drained through a ModelForm
, you'll have to resort to one of the latter two. In most cases, the obvious approach is to satisfy such requirements through Form.clean()
.
Upvotes: 0
Reputation: 20569
You can't access self in default callback, because it is called on creating object and it can't be provided.
But you can override __init__
or save
method and check here values of your fields to make some changes. Make sure that in __init__
you will make changes after calling __init__
from parent class.
Upvotes: 0