I have a status field which has 3 values: pending, activated
and rejected. If I am changing the value of status I want to
have a check that activated cannot be changed to pending. I
do not want to write stored-procs for this. Can I have the
previous value in Django before saving?
Means new and old value.
def clean_status(self):
status = self.cleaned_data.get('status')
if status == 'pending':
if self.instance and self.instance.status == 'activated':
raise forms.ValidationError('You cannot change activated to pending')
return status
This method is to be added in a Form
subclass. Its name is clean_FIELD_NAME
.
cleaned_data
contains previous values. New value is stored in self.instance
.
Alternatively, validate()
method can be added to a forms.Field
subclass. See Django documentation.
You can do this in an overridden save
method. The thing to remember is that Django model instances aren't the actual database objects, they just get their values from there on load. So you can easily go back to the database before saving your current object to get the existing values.
def save(self, *args, **kwargs):
if self.status == 'pending':
old_instance = MyClass.objects.get(pk=self.pk)
if old_instance.status == 'activated':
raise SomeError
super(MyModel, self).save(*args, **kwargs)
There is currently no good way of returning an error message to the user other than raising an exception. There is a Google Summer of Code project currently under way to enable 'model validation', but this will not be ready for a few months.
If you want to do something similar in the admin, the best way is to define a custom ModelForm with an overridden clean()
method. However, this time since this is a form you already have access to the old values without hitting the db again. Another benefit is that you can return a form validation error to the user.
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
def clean_status(self):
status = self.cleaned_data.get('status', '')
if status == 'pending':
if self.instance and self.instance.status == 'activated':
raise forms.ValidationError(
'You cannot change activated to pending'
)
return status
class MyModelAdmin(forms.ModelAdmin):
form = MyModelForm
model = MyModel
This has been answered elsewhere on Stack Overflow, but the correct way is to use something like this to track whether fields are dirty. Then you could use a signal to denote that something has changed that's of importance. (i.e. your field)
Instead of overriding the save method, wouldn't this be a good place to use signals? Intercept the save before commit, check the current value in the database, and either forward the save on, or reject it?
Now I'm not sure if the signal blocks the save request or if it happens asynch, so feel free to downvote this answer if a signal can not be used to prevent the save happening upon validation.
I'm against overriding inbuilt methods if there is another inbuilt tool that works just as well.
Found this thread while searching for an answer to the same question. Why not do something like this? This way you can avoid touching database. And inbuilt __init__
just a little extended. I think it's much more simple way than using signals.
class MyModel(models.Model):
my_fair_field = ....
def __init__(self, *args, **kwargs):
super(MyModel, self).__init__(*args, **kwargs)
self.__clean_fair_field = self.my_fair_field
def save(self, *args, **kwargs):
# check if field value changed
if self.__clean_fair_field != self.my_fair_field
# ...do some work...
super(MyModel, self).save(*args, **kwargs)