django update model while saving another form - django

I am saving a from and updating another model in the form, but the update is not saved to database.
if request.method == 'POST':
form = InventoryTransactionForm(request.POST, instance=InventoryTransaction())
if form.is_valid():
quantity = request.POST['quantity']
part_id = request.POST['part_id']
item_template_id = request.POST['supply']
try:
item_object = Item.objects.get(pk=part_id)
masterQty = item_object.masterQty - int(quantity)
item_object.save(force_update=True)
except Exception, e:
messages.error(request, e.message)
can anybody please help what is wrong in above code, thanks.
EDIT: updated code as below
#transaction.commit_manually
def post(self, request, *args, **kwargs):
if request.method == 'POST':
form = InventoryTransactionForm(request.POST, instance=InventoryTransaction())
if form.is_valid():
quantity = request.POST['quantity']
part_id = request.POST['part_id']
item_template_id = request.POST['supply']
try:
item_object = Item.objects.get(pk=part_id)
masterQty = item_object.masterQty - int(quantity)
item_object.save(force_update=True)
transaction.commit()
except Exception, e:
transaction.rollback()
messages.error(request, e.message)
form.save(True)

You are using force_update=True in the save method. From the docs:
In some rare circumstances, it’s necessary to be able to force the save() method to perform an SQL INSERT and not fall back to doing an UPDATE. Or vice-versa: update, if possible, but not insert a new row. In these cases you can pass the force_insert=True or force_update=True parameters to the save() method. Obviously, passing both parameters is an error: you cannot both insert and update at the same time!
You are saving the object to the database for the first time so it doesn't make sense to call force_update=True as it will prevent the object from being created in the first place. So change
item_object.save(force_update=True)
to
item_object.save()

I think your problem is about working with django ORM here.
You get your object from the database here : item_object = Item.objects.get(pk=part_id) but you lose the instance of this object when doing item_object = Item(masterQty=masterQty, item_template_id=item_template_id, id=part_id).
That's why you think you need a force update but you actually just need to keep the right instance of your object.
Why don't you just update the fields on the object you got from the db and then save it ?
Like so :
item_object = Item.objects.get(pk=part_id)
item_object.masterQty -= int(quantity)
item_object.item_template_id = item_template_id
item_object.save()
You don't even need a force update as you'll be working on an instance you got from the db.

Related

Form validation syntax - calling form.save() versus foo.save()

I am processing a ModelForm, and both of these seem to return identical results:
if request.method == 'POST':
form = FooForm(request.POST)
if form.is_valid():
bar = form.save(commit = False)
bar.user = request.user
form.save()
if request.method == 'POST':
form = FooForm(request.POST)
if form.is_valid():
bar = form.save(commit = False)
bar.user = request.user
bar.save() # calling save on bar instead of on the form
In terms of how these save data and relations between data, is there any difference in these two? The second approach doesn't seem to work when I need to use bar.save_m2m(), which makes me think that the first approach is the right way to go. However, the logic of the second approach makes more sense to me. Can anyone clarify for me which one of these is more correct and why? I am worried that I am unintentionally processing data incorrectly and setting my app up for problems down the line.
From Django's website, probably this will clear up what you should do:
# Create a form instance with POST data.
>>> f = AuthorForm(request.POST)
# Create, but don't save the new author instance.
>>> new_author = f.save(commit=False)
# Modify the author in some way.
>>> new_author.some_field = 'some_value'
# Save the new instance.
>>> new_author.save()
# Now, save the many-to-many data for the form.
>>> f.save_m2m()
From https://docs.djangoproject.com/en/1.6/topics/forms/modelforms/
This is extracted from the source code of Django 1.6.2:
if commit:
# If we are committing, save the instance and the m2m data immediately.
instance.save()
save_m2m()
else:
# We're not committing. Add a method to the form to allow deferred
# saving of m2m data.
form.save_m2m = save_m2m
return instance
This is the reason why if after Ex1:form.save() you call bar.save_m2m() everything will be fine, you will be executing a normal save() step by step. The Base class is returning its instance each time so if you call save() it will save the same instance you are pointing to from outside and that you just modified (Warning: are you using it as a global variable?)...and anyway you can not be sure for how long this method will work as you expect since nowhere in the documentation says the instance returned when calling form.save(commit=False) is the same instance used when you call form.save() few lines below.
As far as I know you wanted to know which way is better to avoid future problems...my recommendation is: go with the second one!.

assign unique id avoiding race conditions

I have a form called Vehicles and i'm trying to assign a unique id to each one, each time a user completes one.
class Vehicles(models.Model):
id = models.DecimalField(primary_key=True, unique=True)
Trying to avoid race conditions(when two forms are being submitted in the same time) after the initial value that I assign to the id field according to the last vehicle-db-record, before saving the form I query again the db for the id of the last record. More or less I do it this way:
def vehicle(request):
vehicles= Vehicles.objects.all().order_by("-id")[0]
id = vehicles.id+1
if request.method == 'POST':
form = VehiclesForm(data=request.POST)
if form.is_valid():
vehicles= Vehicles.objects.all().order_by("-id")[0]
id = vehicles.id+1
temp = form.save(new_veh_id=id)
return render_to_response('success.html', locals(), context_instance= RequestContext(request))
else:
form = VehiclesForm(initial={'id': id})
return render_to_response('insertVehicle.html', locals(), context_instance= RequestContext(request))
and in forms I override the save method:
def save(self, *args, **kwargs):
commit = kwargs.pop('commit', True)
new_veh_id = kwargs.pop('new_veh_id', None)
instance = super(VehiclesForm, self).save(*args, commit = False, **kwargs)
if id is not None: instance.id = new_veh_id
if commit:
instance.save()
return instance
but is_valid returns false with form error:vehicles with this id already exists.
I use exactly this practice with another model and form (identical fields to these) and it works like a charm, forms pass validation despite the same id and it changes it in the last submitted form. Can anyone help me on this or suggest a better solution to achieve this functionality? What I do is maybe somehow 'custom'.
EDIT: I also tried this, but is_valid fails again
def clean(self):
cleaned_data = super(VehiclesForm, self).clean()
id = unicode(self.cleaned_data.get('id'))
vehicles= Vehicles.objects.all().order_by("-id")[0]
id = vehicles.id+1
cleaned_data[id] = id
return cleaned_data
I think you need select_for_update which locks the rows until the end
of transaction. Mind you that although it is designated to work with many
rows, you can still lock just one row, by making sure your filter query
will return only one object.

Django Overwrite existing instance in table

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 )

django forms instance

i have a method where i am saving the data from users, but each user has to have a single profile, so each time he saves, the data should be overwritten.
first i verify if he already has a profile data, and in this case i add an instance to the form. if not, (this is his first registration), i simply add data into DB
my code is:
but i get an error:'QuerySet' object has no attribute '_meta'
is my method right? thanks!
def save_userprofile(request):
if request.method == 'POST':
u = UserProfile.objects.filter(created_by = request.user)
if u:
form = UserProfileForm(request.POST, request.FILES,instance=u )
else:
form = UserProfileForm(request.POST, request.FILES)
If you're expecting just one object back from a query, you should use the get() method.
from django.core.exceptions import ObjectDoesNotExist
try:
u = UserProfile.objects.get(created_by = request.user)
# can update here
except ObjectDoesNotExist:
# create object
The queryset reference should explain all. You may also find get_or_create() is useful.
The instance argument of UserProfileForm doesn't expect to receive a QuerySet, which is what you're giving it. To retrive the profile, you should use this, which returns a single UserProfile object:
u = UserProfile.objects.get(created_by = request.user)

Django Forms with get_or_create

I am using Django ModelForms to create a form. I have my form set up and it is working ok.
form = MyForm(data=request.POST)
if form.is_valid():
form.save()
What I now want though is for the form to check first to see if an identical record exists. If it does I want it to get the id of that object and if not I want it to insert it into the database and then give me the id of that object. Is this possible using something like:
form.get_or_create(data=request.POST)
I know I could do
form = MyForm(instance=object)
when creating the form but this would not work as I still want to have the case where there is no instance of an object
edit:
Say my model is
class Book(models.Model):
name = models.CharField(max_length=50)
author = models.CharField(max_length=50)
price = models.CharField(max_length=50)
I want a form which someone can fill in to store books. However if there is already a book in the db which has the same name, author and price I obviously don't want this record adding again so just want to find out its id and not add it.
I know there is a function in Django; get_or_create which does this but is there something similar for forms? or would I have to do something like
if form.is_valid():
f = form.save(commit=false)
id = get_or_create(name=f.name, author=f.author, price=f.price)
Thanks
I like this approach:
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
book, created = Book.objects.get_or_create(**form.cleaned_data)
That way you get to take advantage of all the functionality of model forms (except .save()) and the get_or_create shortcut.
You just need two cases in the view before the postback has occurred, something like
if id:
form = MyForm(instance=obj)
else
form = MyForm()
then you can call form.save() in the postback and Django will take care of the rest.
What do you mean by "if an identical record exists"? If this is a simple ID check, then your view code would look something like this:
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
form.save()
else:
if get_id:
obj = MyModel.objects.get(id=get_id)
form = MyForm(instance=obj)
else:
form = MyForm()
The concept here is the check occurs on the GET request, such that on the POST to save, Django will already have determined if this is a new or existing record.
If your check for an identical record is more complex, it might require shifting the logic around a bit.
I would do this -
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
author = form.cleaned_data['author']
price = form.cleaned_data['prince']
if name and author and price:
book, created = Book.objects.get_or_create(name=name, \
author=author, price=price)
if created:
# fresh entry in db.
else:
# already there, maybe update?
book.save()
Based on the answers and comments, I had to create a different solution for my case, which included the use of unique_together on the base model. You may find this code useful as well, as I actually made it fairly generic.
I have custom code in the form.save() method that I want to utilize for creating a new object, so I don't want to simply not use the form.save() call. I do have to put my code check in the form.save() method, which I think is a reasonable place to put it.
I have a utility function to flatten iterables.
def flatten(l, a=list()):
"""
Flattens a list. Just do flatten(l).
Disregard the a since it is used in recursive calls.
"""
for i in l:
if isinstance(i, Iterable):
flatten_layout(i, a)
else:
a.append(i)
return a
In the ModelForm, I overwrite the validate_unique() method:
def validate_unique(self):
pass
This is about what my save method looks like:
def save(self, commit=True):
unique_fields = flatten(MyObject._meta.unique_together)
unique_cleaned_data = {k: v for k, v in self.cleaned_data.items() if k in unique_fields}
# check if the object exists in the database based on unique data
try:
my_object = MyObject.objects.get(**unique_cleaned_data)
except MyObject.DoesNotExist:
my_object = super(MyModelFormAjax, self).save(commit)
# -- insert extra code for saving a new object here ---
else:
for data, value in self.cleaned_data.items():
if data not in unique_fields:
# only update the field if it has data; otherwise, retain
# the old value; you may want to comment or remove this
# next line
if value:
setattr(my_object, data, value)
if commit:
my_object.save()
return my_object