Rueen1963 Rueen1963 - 4 months ago 66
Python Question

Django admin site modify save

In the admin site, I have a custom form. However, I want to override the save method so that if a certain keyword is entered, I do not save it into the database. Is this possible?

class MyCustomForm (forms.ModelForm):

def save(self, commit=True):
input_name = self.cleaned_data.get('input_name', None)
if input_name == "MyKeyword":
//Do not save
else:
return super(MyCustomForm, self).save(commit=commit)


but this returns the error:

AttributeError 'NoneType' object has no attribute 'save'


Edit
Following this: Overriding the save method in Django ModelForm

I tried:

def save(self, force_insert=False, force_update=False, commit=True):
m = super(MyCustomCategoryForm, self).save(commit=False)
input_name = self.cleaned_data.get('input_name', None)
if input_name != "MyKeyword":
m.save()
return m


But the admin site still creates a new entry if the input_name is "MyKeyword"

Answer

The save() method is supposed to return the object, whether saved or not. It should rather look like:

def save(self, commit=True):
    input_name = self.cleaned_data.get('input_name')
    if input_name == "MyKeyword":
        commit = False
    return super().save(commit=commit)

However, as you can see here and here, the form is already called with commit=False and the object is saved later by the save_model() method of your ModelAdmin. This is the reason why you got AttributeError 'NoneType' object has no attribute 'save'. If you check the traceback of the exception, I'm pretty sure the error comes from this line.

Thus you should, in addition, override your ModelAdmin's save_model() method:

def save_model(self, request, obj, form, change):
    if form.cleaned_data.get('input_name') == 'MyKeyword':
        return  # Don't save in this case
    super().save_model(request, obj, form, change)

# And you'll also have to override the `ModelAdmin`'s `save_related()` method
# in the same flavor:

def save_related(self, request, form, formsets, change):
    if form.cleaned_data.get('input_name') == 'MyKeyword':
        return  # Don't save in this case
    super().save_related(request, form, formsets, change)

Given your comment in this answer

It is working now but I just have one related question. The admin site will still display a green banner at the top saying "The model was added successfully". Can I override some method so that it is red and says "The model already exists"?

It looks that what you want to do is not overriding the save method but controlling form validation. Which is completely different. You just have to override your form's clean method.

from django import forms

def clean(self):
    """
    super().clean()
    if self.cleaned_data.get('input_name') == 'MyKeyword':
        raise forms.ValidationError("The model already exists")

Or, even better since it looks like you want to clean a single field:

from django import form

def clean_input_name(self):
    data = self.cleaned_data['input_name']
    if data == 'MyKeyWord':
        raise forms.ValidationError("The model already exists")

However, I guess input_name is also a field of your model and you don't want to raise this error only on one form but across all your project. In which case, what you are looking for are Model validators:

from django.core.exceptions import ValidationError

def validate_input_name(value):
    if value == 'MyKeyword':
        raise ValidationError("The model already exists")