Overriding the Save Method in Django Modelform

Overriding the save method in Django ModelForm

In your save you have to have the argument commit. If anything overrides your form, or wants to modify what it's saving, it will do save(commit=False), modify the output, and then save it itself.

Also, your ModelForm should return the model it's saving. Usually a ModelForm's save will look something like:

def save(self, commit=True):
m = super(CallResultTypeForm, self).save(commit=False)
# do custom stuff
if commit:
m.save()
return m

Read up on the save method.

Finally, a lot of this ModelForm won't work just because of the way you are accessing things. Instead of self.callResult, you need to use self.fields['callResult'].

UPDATE: In response to your answer:

Aside: Why not just use ManyToManyFields in the Model so you don't have to do this? Seems like you're storing redundant data and making more work for yourself (and me :P).

from django.db.models import AutoField  
def copy_model_instance(obj):
"""
Create a copy of a model instance.
M2M relationships are currently not handled, i.e. they are not copied. (Fortunately, you don't have any in this case)
See also Django #4027. From http://blog.elsdoerfer.name/2008/09/09/making-a-copy-of-a-model-instance/
"""
initial = dict([(f.name, getattr(obj, f.name)) for f in obj._meta.fields if not isinstance(f, AutoField) and not f in obj._meta.parents.values()])
return obj.__class__(**initial)

class CallResultTypeForm(ModelForm):
callResult = ModelMultipleChoiceField(queryset=CallResult.objects.all())
campaign = ModelMultipleChoiceField(queryset=Campaign.objects.all())
callType = ModelMultipleChoiceField(queryset=CallType.objects.all())

def save(self, commit=True, *args, **kwargs):
m = super(CallResultTypeForm, self).save(commit=False, *args, **kwargs)
results = []
for cr in self.callResult:
for c in self.campain:
for ct in self.callType:
m_new = copy_model_instance(m)
m_new.callResult = cr
m_new.campaign = c
m_new.calltype = ct
if commit:
m_new.save()
results.append(m_new)
return results

This allows for inheritance of CallResultTypeForm, just in case that's ever necessary.

Override the save() method in Django ModelForm to create or update

Use django queryset get_or_create() method.

From the docs: This method is used for looking up an object with the given kwargs (may be empty if your model has defaults for all fields), creating one if necessary.

Read more about it here in the docs.

Now, inside your view:

class ReviewFollowAuthor(View):
def post(self, request, **kwargs):
user = request.user
author = get_object_or_404(Author, id=kwargs['pk'])
#MAKE certain that user and author always have an instance within them.
#send the user, author to the form.
f = ReviewFollowAuthorForm(request.POST , user = user , author = author)
if(f.is_valid()):
f.save(user=user,userx=userx)
return JsonResponse({'success':1})
else:
return JsonResponse({'success':0})

Now, inside your ModelForm:

class ReviewFollowAuthorForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
#override __init__ to make your "self" object have the instances sent by the your views.(author and user)
self.user = kwargs.pop('user' , None)
self.author = kwargs.pop('author' , None)
#Now your model-form instance has the user and the author attribute within itself.
super(ReviewFollowAuthorForm, self).__init__(*args, **kwargs)
class Meta:
model = UserAuthor
fields = ("is_follow", "review")

def save(self, commit=True):
user_author, created = UserAuthor.objects.update_or_create( user=self.user, author=self.author, defaults={'is_follow': self.cleaned_data.get('is_follow'), 'review': self.cleaned_data.get('review')} )
#rest of your logic
return user_author

I Hope this guides you in some way.
Thanks.

Overriding Django ModelForm save method and deleting old values(files)

Try using this, it works just fine in terms of auto deleting old files and pictures.
https://pypi.org/project/django-cleanup/

Django save() overriding, best practice: on model, form or view?

Upon digging in the source code I found the ModelForm's save() method to be calling the Model's(Model Instance) save() method. Check it here.

Now it is clear that first ModelForm's save() is called and within it(depending upon the commit value) Model's save() is called.

Also it's worth noting that in the code:

def save(self, commit = True): 
user = super(RegisterForm, self).save(commit = False)
user.set_password(self.cleaned_data["password1"])
#When you're hashing and setting the password to the user variable,
#this user variable is a Model Object but is not saved in the database yet.
if commit:
user.save()
#Now in the above line you're ultimately calling the save method of Model instance.
return user

So the question comes down to you.

Do you want to go one more step above in the abstraction layers(by overriding Model instance's save method) of django models?

Do you actually have a need to do it?

Abstraction is one of the building block of OOPS. So until a need arises which actually requires you to go above in the abstraction layers, why do it at all? Clearly the password can be hashed in the ModelForm's save() method.

Also, if you go a layer above in abstraction,
what if an unexpected behavior occurs in the future?

In my opinion, why not just leave it inside the ModelForm? We start from the highest layer of Abstraction when we start using django(we just call the methods and usually don't know what's happening in the classes we're inheriting; that's what oops is for) and we go above only when a specific need arises.

Hope this guides you in some way. Thanks.

override the save method in django

You can try a signal like this:

from django.dispatch import receiver
from django.db.models.signals import pre_save
from django.utils import timezone

@receiver(pre_save, sender=Product)
def update_create_field(sender, instance, **kwargs):
try:
_pre_save_instance = Product.objects.get(pk=instance.pk)
if _pre_save_instance.price != instance.price:
instance.create = timezone.now() #here you can specify any value you want
except Exception as e:
print('Error updating create field!')
print(e)


Related Topics



Leave a reply



Submit