1

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"

Community
  • 1
  • 1
Rueen1963
  • 41
  • 2
  • 13
  • So what is wrong with that code? You can simplify it changing the condition to `if input_name != "MyKeyword": super(MyCustomForm, self).save(commit=commit)`, but other than that it should work as you wrote it. – xbello Jul 15 '16 at 15:02
  • I tried that but the error is: AttributeError 'NoneType' object has no attribute 'save' – Rueen1963 Jul 15 '16 at 15:18
  • Then maybe your ModelForm isn't returning the object being saved: it's returning "None". Read this answer http://stackoverflow.com/a/817364/1688590. – xbello Jul 15 '16 at 15:26

1 Answers1

1

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")
Antoine Pinsard
  • 33,148
  • 8
  • 67
  • 87
  • 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"? – Rueen1963 Jul 16 '16 at 02:38
  • Sure you can. But it really looks like this is not actually what you want to do. Why didn't you say from the start that you wanted to control form validation? Please see [what is the XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) and the update in my answer. Also, do you really want to raise an error when `input_name` is set to **a keyword**? Or is **variable?** Maybe a **list of keywords?** Maybe a data **retrieved from the database?** Maybe you just want this field to be **unique?** If so, why are you putting efforts in hiding key information of your issue? – Antoine Pinsard Jul 16 '16 at 05:18