Reputation: 1480
Intro:
I want to override the save() method in my custom ModelForm to create a new row in a model or to update some data if it is exist. I have already found some solutions but it is implemented in the view function like this link Django form INSERTs when I want it to UPDATE, but I want to implement it in the form save() method.
My Models:
I have the default auth.User Model and my Author model as shown below, also I have another model called UserAuthor for the ManyToMany relationship between them as it is mandatory in my case for adding other fields like 'is_follow' and 'review'
class Author(models.Model):
name = models.CharField(max_length=50)
class UserAuthor(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
is_follow = models.BooleanField(default=0)
review = models.TextField(max_length=500, blank=True, null=True)
My Form:
class ReviewFollowAuthorForm(forms.ModelForm):
class Meta:
model = UserAuthor
fields = ("is_follow", "review")
def save(self, commit=True, user=None, author=None):
user_author = super(ReviewFollowAuthorForm, self).save(commit=False)
user_author.user = user
user_author.author = author
if commit:
user_author.save()
return user_author
My View:
class ReviewFollowAuthor(View):
def post(self, request, **kwargs):
user = request.user
author = get_object_or_404(Author, id=kwargs['pk'])
f = ReviewFollowAuthorForm(request.POST)
if(f.is_valid()):
f.save(user=user,userx=userx)
return JsonResponse({'success':1})
else:
return JsonResponse({'success':0})
In that case, it create a new row in the database for each save() call. I don't know how to check in the save method before saving, and just update in case there is a previous row for the requester user with the author, I tried many things but i failed. I know I can use:
user_author = UserAuthor.objects.filter(user=user, author=author)
and update if it returns a result, but how in the save() method itself?
Upvotes: 2
Views: 12261
Reputation: 919
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.
Upvotes: 4