Using Django 1.7.
In a Function Based View, I'm trying to get the data of the current (i.e. not yet updated) instance to do a comparison which action I need to take.
When I try to get the data using smt.field1, I'd expect to get what I have in the database for this instance, but instead I get the new, unsaved data that is in the form.
def edit_something(request, pk):
smt = get_object_or_404(Something, pk=pk)
if request.method == 'POST':
form = MyForm(instance=smt, data=request.POST)
context_dict['form'] = form
if form.is_valid():
if smt.field1 != form.cleaned_data.get('field1'):
print "field1 has changed, so I need to do Action1"
else:
print "field1 has not changed, so I need to do Action2"
form.instance.modified_by = request.user
form.save()
return HttpResponseRedirect(reverse('smt_detail', args=(smt.id,)))
else:
...
In other words, the smt.field1 == form.cleaned_data.get('field1') will always yield True and Action1 will always be taken. I don't want this.
Any idea how to elegantly solve this?
You need to use form.changed_data to get at which fields have been updated.
The instance of smt gets changed when if form.is_valid(): is called.
One solution is to get the smt.field1 variable before calling form.is_valid(), e.g.
...
form = MyForm(instance=smt, data=request.POST)
context_dict['form'] = form
orig_field1 = smt.field1
if form.is_valid():
if orig_field1 != form.cleaned_data.get('field1'):
print "field1 has changed, so I need to do Action1"
else:
print "field1 has not changed, so I need to do Action2"
...
Related
I'm trying to learn Django and have come up with a situation I can't figure out. I have the following code:
def contact_add(request):
if request.method == 'POST':
form = ContactManageForm(request.POST)
if form.is_valid():
if form.has_changed(): # <-- ALWAYS RETURNS TRUE!
form.clean()
...
elif 'id' in request.GET: # Request to show an existing contact
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(person_static = request.GET['id'],
current_record_fg = True)
form = ContactManageForm(new_contact_dynamic.__dict__, initial=new_contact_dynamic.__dict__)
else: # This must be to add a new contact
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
So, if I'm sent an ID number, I read a record and display it on the screen. My template gives the user a 'submit changes' button. My problem, as noted above, is that Django always shows that the form has changed, even if the user hasn't changed any data on the screen (i.e. he just hit the submit changes button without changing anything).
So, am I doing something obviously wrong in my code that's creating this situation? Am I misinterpreting how the form.has_changed() method works?
It's my assumption that when I use the initial=parameter after a GET request, Django is storing that data somewhere and knows the context when the user then hits the 'submit data' button, is this wrong?
Yes you need to initialize your Form with initial data.
In your view the GET and POST requests have no common context. You may want to use sessions for that.
But in this case, it is not necessary. You can retrieve the instance on each request:
def contact_add(request):
if 'id' in request.GET:
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(
person_static = request.GET['id'],
current_record_fg = True
)
if request.method == 'POST':
form = ContactManageForm(request.POST, initial=new_contact_dynamic.__dict__)
...
else: # Show an existing contact
form = ContactManageForm(initial=new_contact_dynamic.__dict__)
else:
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
I'm creating a form in one page, then in another page I'm trying to pull out the form (populated with the data saved in it already) and would like to make changes to it so that when I save it it overwrites the instance instead of creating another one.
def edit(request):
a = request.session.get('a', None)
if a is None:
raise Http404('a was not found')
if request.method == 'POST':
form = Name_Form(request.POST, instance=a)
if form.is_valid():
j = form.save( commit=False )
j.save()
else:
form = Name_Form( instance = a )
This is the code I have for the "editting form" view.. When I open this page the form is successfully prepopulated with all the data. However, when I make changes and save, it does not overwrite the existing instance, instead it creates a new one.
Any ideas?
Have a look here: https://docs.djangoproject.com/en/dev/ref/models/instances/#how-django-knows-to-update-vs-insert
I think this may help you.
Update:
What about trying a more "explicit" way.
Assume, id_of_Name stores only the id or pk of your model which you want to edit (I assume the model is called "Name"). Then just retrieve the id/pk from session to query your db for the model instance. Also try to directly call the save method on the form.
def edit(request):
id_of_Name = request.session.get('a', None)
if id_of_Name is None:
raise Http404('id_of_Name was not found')
instance = Name.objects.get(pk=id_of_Name)
if request.method == 'POST':
form = Name_Form(request.POST, instance=instance)
if form.is_valid():
form.save()
else:
form = Name_Form( instance = instance )
I'm misunderstanding something! If my model is not saved, it does not have an id associated with it. So if I have something like this:
views.py (inserting or editing existing info uses the same modelform)
def insert_or_modify(request, id=None):
if id is not None:
book = BookModel.objects.get(pk=id)
else:
book = BookModel()
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES, instance=book)
if form.is_valid():
form.save()
....
return render_to_response(...)
I also have an image and use upload_to for the imagefield. There are two problems: id is None and I'm not sure how to manipulate/save the instance=book so that I would actually get an id. The second problem is the location I save my data to is way off. Even though the template tag book.img.url has the desired location of the book at http:127.0.0.1:8000/folder1/media/id/, the actual location is somewhere else:
Where I want to save my image to:
/project/folder1/media/id/
where id is the book id.
What I actually get:
/project/id/
(But 'id' becomes 'None' since it doesn't exist!)
My previous code worked. It would save to the correct location, but with this current code, it doesn't work. So the saving issue doesn't seem like it's due to settings.py since it worked previously.
EDIT: removed non-code from code formatting area
EDIT: I found out why I wasn't saving to the correct location. As it turned out, I forgot to uncomment something when I last modified settings.py. Saving to the location works now! Sorry guys!
EDIT: I think the id=None problem is caused by form.save(). If I avoid doing that and just save to the model directly, I don't have this problem.
Id assigns only on saving objects when you use autoincrement id field (default).
You can save item before handling image, and then save image.
May be you can not worry about image name - becouse django file storages dont have troubles with same image names. So if you just save file "image.png", and then save another file with name "image.png" - then it will be saved as "image_1.png"
def add_or_create(request, item_id=None):
item = get_object_or_404(BookModel, id=item_id) if item_id else None
form = BookInfoForm(request.POST or None, request.FILES or None, instance=book) # assume it is ModelForm
if form.is_valid():
book = form.save()
For the first part:
def insert_or_modify(request, id=None):
if id:
book = BookModel.objects.get(pk=id)
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES, instance=book)
if form.is_valid():
save_book = form.save()
# use save_book as your instance of BookModel
....
else:
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES)
if form.is_valid():
save_book = form.save()
# use save_book as your instance of BookModel
....
save_book = form.save() allows you to then use save_book as your saved instance of BookModel, and save_book.id is its id.
def create_id(instance,some_id=None):
if some_id is None:
obj=Post.objects.first()
new_id=obj.id
new_id+=1
return new_id
else:
return some_id
def pre_save_post_receiver(sender, instance, *args, **kwargs):
if not instance.id:
instance.id = create_id(instance)
pre_save.connect(pre_save_post_receiver, sender=Post)
I thought it looks trivial, and was surprised.
What I have
I have a Django model + form (ModelForm).
My user fills in the form, and on my view I have the usual:
if request.POST:
form = myForm(request.POST)
if request.method == "POST" and form.is_valid():
result = form.save(commit=False)
Now I need to heavily manipulate some fields of the form (in the "result" object) and I want to check the forms is_valid() again before saving.
The Problem
I tried to create a new form using the "result" object (that is a ModelForm object) using a dictionary (as suggested here)
result_dictionary = dict((x.name, getattr(result, x.name)) for x in result._meta.fields)
or
result_dictionary = model_to_dict(result, fields=[field.name for field in result._meta.fields])
plus
testForm = myForm(initial=result_dictionary)
but it doesn't pass is_valid() and does'nt give any errors!
The fields are passed OK to the new form...
Any ideas?
Sometimes, looking in the Django source can be really helpful, here's BaseForm.is_valid():
def is_valid(self):
"""
Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False.
"""
return self.is_bound and not bool(self.errors)
So if there are no errors, is_valid() returns false because you haven't bound the form, you haven't given the form anywhere to look for data. Try using the form.data dictionary instead, something like this:
if request.POST:
form = myModel(request.POST)
if request.method == "POST" and form.is_valid():
form.data['field1'] = 'Changing this'
form.data['field2'] = 34
testform = myModel(data=form.data)
I am using django forms to add a new objects to the db. The code I currently have is:
if request.method == 'POST':
form = MyForm(data=request.POST)
if form.is_valid():
obj = form.save()
else:
form = MyForm()
return render_to_response('reflections/add_reflection.html', {'form':form},context_instance=RequestContext(request))
The code above currently adds a new object each time the form is submitted. What I want to happen is that the object is edited the next time the save button is pressed rather than adding a new record.
How would I do this?
Use
instance_id = None
if request.method == 'POST':
try:
instance = MyType.objects.get(id=request.POST.get('instance_id'))
except MyType.DoesNotExist:
instance = None
form = MyForm(data=request.POST, instance=instance)
if form.is_valid():
obj = form.save()
instance_id = obj.id
else:
form = MyForm(instance=None)
return render_to_response('reflections/add_reflection.html', {'form':form, 'instance_id': instance_id or ''},context_instance=RequestContext(request))
Once the object is saved, pass it's id in context to page
and add it to a hidden input field inside the form as name='instance_id'.
Happy Coding.
You either need to add a separate view for editing an existing object, or - better - to add the functionality to this view. To do the latter, you could pass in an instance of the object you want to edit with your modelform to else part of your clause:
else:
if existing_obj:
form = MyForm(instance=existing_obj) #this is editing your 'existing_obj'
else:
form = MyForm() # this is creating a brand new, empty form
You'll also need to update the POST-handling bit of code too. See the example here