Django form.save step by step - python

Let's say I have a form for adding/editing products (with field 'user' being a foreign key to my User) triggered from two separate view functions - add/edit :
def product_add(request):
userprofile = UserProfile.objects.get(user=request.user)
if request.method == 'POST':
form = ProductAddForm(request.POST, request.FILES,)
if form.is_valid():
form.save(user=request.user)
else:
form = ProductAddForm()
return render_to_response('products/product_add.html', {
'form':form, 'user':request.user,
}, context_instance=RequestContext(request))
def product_edit(request, id):
product = get_object_or_404(Product, id=id, user=request.user)
if product.user.id!=request.user.id:
raise Http404
if request.method == 'POST':
form = ProductAddForm(request.POST, request.FILES, instance=product)
if form.is_valid():
form.save(user=request.user)
else:
form = ProductAddForm(instance=product)
return render_to_response('products/product_edit.html', {
'form':form, 'user':request.user,
}, context_instance=RequestContext(request))
The form's save method looks as follows :
def save(self, user, *args, **kwargs):
self.instance.user = user
post = super(ProductAddForm, self).save(*args, **kwargs)
post.save()
Can somebody tell me what's happening in this save method step by step ?Why do we call super on this form and what is the difference in whole processing when we edit and save new product if function call is the same ?

self.instance.user = user
Save the user argument into the self.instance object's user attribute
post = super(ProductAddForm, self).save(*args, **kwargs)
Invoke the superclass save method to get a resulting object.
post.save()
Save the resulting object. This may be needless, or it may be essential, depending on the arguments to save.
See http://docs.djangoproject.com/en/1.2/topics/forms/modelforms/#the-save-method. If commit is False, then the post object has not been saved to the database.

Related

How to update a record in django?

I try to update my record but in place of updating it displays a message saying the record already exists.
here is the logic I m executing :
#login_required
def cylinderUpdateView(request,pk):
if not request.user.is_superuser:
return redirect('index')
obj=CylinderEntry.objects.get(id=pk)
form=CylinderEntryForm(instance=obj)
if request.method=='POST':
form=CylinderEntryForm(data=request.POST)
if form.is_valid():
obj=form.save(commit=False)
obj=form.save()
return redirect(cylinderListView)
return render(request,'entry/cylinderentry_form.html',locals())
In case of a POST request you have initialized the form as:
if request.method=='POST':
form=CylinderEntryForm(data=request.POST)
Here you haven't specified the instance like you did in case of a GET request so only the data submitted from the form is considered you need to specify the instance in this case too:
if request.method=='POST':
form=CylinderEntryForm(data=request.POST, instance=obj)
If you want to update an object with a ModelForm, you should pass that instance with the instance=… parameter:
#login_required
def cylinderUpdateView(request,pk):
if not request.user.is_superuser:
return redirect('index')
obj = CylinderEntry.objects.get(id=pk)
if request.method=='POST':
# pass the instance ↓
form = CylinderEntryForm(data=request.POST, instance=obj)
if form.is_valid():
form.save()
return redirect(cylinderListView)
else:
form = CylinderEntryForm(instance=obj)
return render(request,'entry/cylinderentry_form.html',locals())

Is this the right way to open object from DB for editing in the same form, and then saving the updated value for the same object using django?

This is my code:
To create the new issue object, use form
def raise_issue_view(request, *args, **kwargs):
form = RaiseIssueForm(request.POST or None)
if form.is_valid():
form.save()
obj = RaiseIssueModel.objects.latest('id')
return redirect("/raise_issue/" + str(obj.id))
context = {
"form" : form
}
return render(request,"raise_issue.html", context)
To edit the previously created issue, by link 127...8000/edit_issue/<issue_id>
def edit_issue_view(request, id):
obj = RaiseIssueModel.objects.get(id=id)
form = RaiseIssueForm(instance=obj)
new_form = RaiseIssueForm(request.POST, instance=obj)
if new_form.is_valid():
new_form.save()
return redirect("/raise_issue/" + str(obj.id))
context = {
"form" : form
}
return render(request,"raise_issue.html", context)
Here, in Edit issue view, first i'm loading the DB data into 'form', then I'm creating a new form (new_form) to save the updated data.
Is this OK, or is there a better way to do this?
Thanks,
This works but is not correct...
The validated form is the new_form. If it is invalid, you will display the errors from form (which does not have any error since it hasn't been submitted).
You also failed to check if the object exists.
def edit_issue_view(request, id):
try:
obj = RaiseIssueModel.objects.get(id=id)
except RaiseIssueModel.DoesNotExist:
raise Http404()
form = RaiseIssueForm(request.POST or None, instance=obj)
if form.is_valid():
form.save()
return redirect("/raise_issue/" + str(obj.id))
context = {
"form" : form
}
return render(request,"raise_issue.html", context)
Then, for your first form. The save() method of your form should return the created object (assuming you used a django.forms.ModelForm). So you can write it this way:
def raise_issue_view(request, *args, **kwargs):
form = RaiseIssueForm(request.POST or None)
if form.is_valid():
obj = form.save()
return redirect("/raise_issue/" + str(obj.id))
context = {
"form" : form
}
return render(request,"raise_issue.html", context)
You can use GET and POST requests to do different functions. GET for fetching the object from DB and POST to make the changes and saving it to DB. For example:
from django.shortcuts import get_object_or_404
def edit_issue_view(request, id):
obj = get_object_or_404(RaiseIssueModel, pk=id)
new_form = RaiseIssueForm(request.POST or None, instance=obj)
if request.method == 'POST':
if new_form.is_valid():
new_form.save()
return redirect("/raise_issue/" + str(obj.id))
context = {
"form" : new_form
}
return render(request,"raise_issue.html", context)
Also add a minor change in raise_issue_view method regarding the redirecting to edit view. As you can see, form.save() returns the object you just created/updated. You can use that for redirecting:
if form.is_valid():
obj = form.save()
return redirect("/raise_issue/" + str(obj.id))
Finally, for redirecting, it is better to use named urls. For example, if your url looks like this:
path('raise_issue/<int:pk>/', name='raise_issue')
Then you can use the following code to redirect:
return redirect('raise_issue', pk=obj.pk)
For more information, please see the documentation.

Django model form, adding a user id when creating new note

I'm pretty new to Django, I've been stuck on this view for a little while. My goal with this form is to be able to create a small note on a "Property" about maintenance or other information. The note would log the time, date, note and the user that recorded the note. Any help would be appreciated.
View:
#login_required(login_url="login")
def createNote(request, pk):
PropertyNoteFormSet = inlineformset_factory(
Property, PropertyNote, fields=('note', 'user',))
property_note = Property.objects.get(id=pk)
form = PropertyNoteFormSet(instance=property_note)
# form = OrderForm(initial={'customer': customer})
if request.method == "POST":
print(request.POST)
form = PropertyNoteFormSet(
request.POST, instance=property_note)
if form.is_valid():
form.save()
return redirect("/")
context = {"form": form}
return render(request, "dashboard/create_note.html", context)
Here is the ModelForm:
class PropertyNoteForm(ModelForm):
class Meta:
model = PropertyNote
fields = ['note']
exclude = ['user']
Here is the Model:
class PropertyNote(models.Model):
airbnb_name = models.ForeignKey(Property, blank=True,
null=True,on_delete=models.CASCADE)
note = models.TextField(blank=True, null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
created_on = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.note
The form comes out with around 4 boxes to fill in. Currently it works, but you have to actually select the user that is posting the note, I would like this part to be handled automatically and use the current logged in user. I think I still have a whole lot of holes in my knowledge around this stuff, I just can't seem to work it out.
Thanks in advance.
Edit:
I've tried this:
def createNote(request, pk):
PropertyNoteFormSet = inlineformset_factory(
Property, PropertyNote, fields=('note',), extra=1)
property_note = Property.objects.get(id=pk)
form = PropertyNoteFormSet(
queryset=PropertyNote.objects.none(), instance=property_note)
# form = OrderForm(initial={'customer': customer})
if request.method == "POST":
print(request.POST)
form = PropertyNoteFormSet(
request.POST, instance=property_note)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
print(instance.user)
instance.save()
return redirect("/")
context = {
"form": form,
'pk': pk,
}
return render(request, "dashboard/create_note.html", context)
But I get this:
AttributeError at /create_note/75/
'list' object has no attribute 'user'
Request Method: POST
Request URL: http://127.0.0.1:8000/create_note/75/
Django Version: 3.0.4
Exception Type: AttributeError
Exception Value:
'list' object has no attribute 'user'
you can use request.user.id to get the logged user id in your view.
See Documentation in Django
#login_required(login_url="login")
def createNote(request, pk, **kwargs):
note_form = PropertyNoteForm()
if request.method == "POST":
note_form = PropertyNoteForm(request.POST)
if note_form.is_valid():
add_note = note_form.save(commit=False)
add_note.user = request.user
add_note.airbnb_name =
Property.objects.get(id=pk)
add_note.save()
return redirect('/property/' + pk + '/')
context = {
"form": note_form,
'pk': pk,
}
return render(request, "dashboard/create_note.html", context)
I solved it with the above code. Using instance was the incorrect thing to do here. I didn't need to create an instance and I didn't need the inline form. I simply needed a new form:
note_form = PropertyNoteForm()
The user input information, I need to send that information to check if it's valid:
if request.method == "POST":
note_form = PropertyNoteForm(request.POST)
if note_form.is_valid():
Then I needed to populate the form with information that was not already in the form from the user:
add_note = note_form.save(commit=False)
add_note.user = request.user
add_note.airbnb_name = Property.objects.get(id=pk)
add_note.save()
return redirect('/property/' + pk + '/')

How to prepopulate an Django Update form and write it back to the database

I'm new at Python & Django and currently struggling right now.
I created an update/edit form with Django Model forms, but it just doesn't prepopulate the form fields and post it to the database at the same time.
I think the problem lies in form = AdvertForm(request.POST or None, request.FILES or None, instance=form).
Without request.POST or None, request.FILES or None, it does prepopulate the fields but won't update the data to database.
Here's my views.py:
def update_advert(request, id):
if not request.user.is_authenticated():
return render(request, 'forum/login.html')
else:
form = get_object_or_404(Advert, pk=id)
if request.method == 'POST':
form = AdvertForm(request.POST or None, request.FILES or None, instance=form)
if form.is_valid():
form = form.save(commit=False)
form.user = request.user
form.save()
return redirect('forum:user_account')
else:
form = AdvertForm(instance=form)
context = {'form': form}
return render(request, 'forum/update_advert.html', context)
In the moment it looks like this, when I try to open the update form:
opening the form --> not prepopulated :(
According to https://docs.djangoproject.com/en/dev/ref/forms/api/#dynamic-initial-values, you can use the initial attribute when instantiating forms in order to prepropulate your forms.
def update_advert(request, id):
if not request.user.is_authenticated():
return render(request, 'forum/login.html')
else:
advert_obj = get_object_or_404(Advert, pk=id)
if request.method == 'POST':
form = AdvertForm(request.POST or None, request.FILES or None, instance=advert_obj)
if form.is_valid():
form.save()
return redirect('forum:user_account')
else:
# Prepopulation happens here:
data = {"some_field": advert_obj.some_val} # Insert all the values of advert_obj here and their field names as keys.
form = AdvertForm(initial=data)
context = {'form': form}
return render(request, 'forum/update_advert.html', context)
In your AdvertForm, put this code:
class AdvertForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(AdvertForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
instance = super(AdvertForm, self).save(commit=False)
instance.user = self.request.user
if commit:
instance.save()
return instance
The overriding save method simply does what you were doing in the views to link up the request.user to the instance, however I have placed that code in the form class to keep your views simple.
Also, I can see one confusing (but not vital) issue - you have mixed up the variable names.
When calling form = get_object_or_404(Advert, pk=id), this should return to a variable name such as advert or something similar. Not form as that can be confusing as we are returning a model object not a form. Similarly, form.save(commit=False) returns an "instance" not a model form. This won't solve your problem but should be pointed out for more clarification on what exactly is being returned and how you should then name your variables.

How to update a form attribute from view code in django

I have a page for updating a user profile in my django project. The view code looks like this:
#login_required
def updateProfile(request, user_id):
if request.method == 'POST':
form = UserProfileForm(request.POST)
if form.is_valid():
form.user_id = user_id
form.save(commit=True)
return index(request)
else:
profile, created = UserProfile.objects.get_or_create(user_id = self.user_id) # don't know if this will actually work.
profile_form = UserProfileForm(profile)
context = {
'user' : request.user,
'form' : profile_form
}
return render(request, 'myapp/profile.html', context)
My form looks like this:
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['age', 'skill_level']
My user profile looks like this:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
age = models.IntegerField(default=18)
skill_level = models.ForeignKey(SkillLevel)
When this gets posted to, we receive what appears to be a valid user_id along with a valid form. In the UserProfileForm form we do not include user_id so that when it renders the user cannot decide to swap that out. Instead, the user_id gets posted back as a separate parameter (as I type this out, I realize it's kind of weird..). I want to save the UserProfile encapsulated by UserProfileForm to the database on post, so I give it a user_id and try to call .save(commit=True) on it, which returns "Column 'user_id' cannot be null".
My question is simple, how can I get that underlying UserProfile object saved from the form data with the information at hand?
Standard Django form handling idiom in case like that is
#login_required
def updateProfile(request, user_id):
if request.method == 'POST':
form = UserProfileForm(request.POST)
if form.is_valid():
obj = form.save(commit=False) # Get just object, but don't save yet
obj.user = request.user # set user (or user_id)
obj.save() # Save object
return index(request)
else:
profile, created = UserProfile.objects.get_or_create(user_id = self.user_id) # don't know if this will actually work.
profile_form = UserProfileForm(profile)
context = {
'user' : request.user,
'form' : profile_form
}
return render(request, 'myapp/profile.html', context)
Note that form data is not in fields, so form.my_field = 123 won't work - form data is parsed to form.cleaned_data dictionary where form.save() reads it.
use the request.user for userProfile user, do this way
#login_required
def updateProfile(request, user_id):
if request.method == 'POST':
form = UserProfileForm(request.POST)
form.user = request.user
if form.is_valid():
form.save(commit=True)
return index(request)
else:
profile, created = UserProfile.objects.get_or_create(user = request.user) # don't know if this will actually work.
profile_form = UserProfileForm(instance=profile)
context = {
'user' : request.user,
'form' : profile_form
}
return render(request, 'myapp/profile.html', context)

Categories