In my Django form I need to perform different field values comparisons (+ some additional checks). If all of these conditions are met I want to raise error and inform user that he can't perform this operation. Right now I'm doing it in clean method and code looks like these:
if self.instance.foo != cleaned_data['foo'] and \
Bar.objects.filter(baz=self.instance.id).count():
cleaned_data['foo'] = self.instance.foo
raise forms.ValidationError("You can't change it")
That's working fine, but I'd also like to reject the change and restore previous value (before the update). The assignment cleaned_data['foo'] = self.instance.foo obviously is not working because cleaned_data is not being returned due to raising ValidationError. How can I achieve this?
It turned out that you can access fields' values via self.data. So I ended up doing self.data['foo'] = self.instance.foo and it's working now.
Related
I'm using django 1.11 on python 3.7
In a method I want to execute some database queries, mainly updating links between objects and I want to use this method to perform a check on what needs to be updated in a sync-operation. The following is an implementation:
results = {}
with transaction.atomic():
sid = transaction.savepoint()
for speaker_user in speaker_users:
# here my code checks all sorts of things, updates the database with
# new connections between objects and stores them all in the
# results-dict, using a lot of code in other classes which
# I really dont want to change for this operation
if sync_test_only:
transaction.savepoint_rollback(sid)
else:
transaction.savepoint_commit(sid)
return results
This snippet is used in a method with the sync_test_only parameter that should only fill the results-dict without doing the database changes that go along with it.
So this method can be used to do the actual work, when sync_test_only is False, and also only report back the work to-be-done, when sync_test_only is True
Is this what the transaction.atomic() is designed for? Does this actually work in my use-case? If not, what would be a better way to achieve this behaviour?
Another option would be to use exceptions, like the docs suggest (read the part under the title "You may need to manually revert model state when rolling back a transaction"):
class MyException(Exception):
pass
def f(do_commit=False):
results = {}
try:
with transaction.atomic():
for speaker_user in speaker_users:
pass
if not do_commit:
raise MyException
except MyException:
# do nothing here
pass
return results
I suggest creating a custom exception so you don't accidently catch something that was raised somewhere else in the code.
I am extending product.template, and I've added a field called uom_class. When I change this field when editing a product, I'd like to clear what is entered in the Many2One field called "Unit of Measure" (uom_id in product.template). I am doing this because I am also changing the domain for the uom_id, and the existing selection ("Units" by default) will probably not be in that domain.
I've tried the following, as suggested for earlier versions, but it did not work.
#api.onchange('uom_class')
def onchange_uom_class(self):
# [...] not shown: previously set new domain for uom_id
result['value'] ={'uom_id':False}
return result
I've seen other posts suggest I need to add assign it an empty product.uom record, but I have no idea how to do that. Any help would be greatly appreciated.
Well, I figured this one out.
I just declared
uom_id = False
For some reason, returning the domain works, but not returning the value. Either that, or I just have no idea what I'm doing and I'm returning the value wrong... which is entirely possible.
If I want to check for the existence and if possible retrieve an object, which of the following methods is faster? More idiomatic? And why? If not either of the two examples I list, how else would one go about doing this?
if Object.objects.get(**kwargs).exists():
my_object = Object.objects.get(**kwargs)
my_object = Object.objects.filter(**kwargs)
if my_object:
my_object = my_object[0]
If relevant, I care about mysql and postgres for this.
Why not do this in a try/except block to avoid the multiple queries / query then an if?
try:
obj = Object.objects.get(**kwargs)
except Object.DoesNotExist:
pass
Just add your else logic under the except.
django provides a pretty good overview of exists
Using your first example it will do the query two times, according to the documentation:
if some_queryset has not yet been evaluated, but you
know that it will be at some point, then using some_queryset.exists()
will do more overall work (one query for the existence check plus an
extra one to later retrieve the results) than simply using
bool(some_queryset), which retrieves the results and then checks if
any were returned.
So if you're going to be using the object, after checking for existance, the docs suggest just using it and forcing evaluation 1 time using
if my_object:
pass
I have the code:
name = MakesiteNameForm(datdict)
if name.is_valid:
name.save()
datsite = Makesite.objects.get(sitename=request.POST['sitename'])
datsite.ref_id.add(RefID.objects.create(url=request.POST['url'],description=request.POST['description']))
datsite.save()
So i have this bit of code what I want to use to create and save some manytomany items but when I try using this method is says that Makesite matching query does not exist. which i think means it hasn't saved but then later I call site = Makesite.objects.all() and I can clearly see the value of what request.POST['sitename'] is sitting inside the querydict. So is there anyway to query this better? or is there something about the save() i missing?
Edit: that form saves a value sitename values into the Makesite table
The save() call doesn't create objects, it just saves the object to the database, inserting a new row in case it's a new object, or updating it.
First, form.is_valid() is a method, but you're not calling it, so you're always trying to save name. That may or may not be related to your error, but it's wrong anyway, and maybe that's where the query error is coming from, not the get() call below. Fix it and see what happens.
Writing custom validators for Formalchemy is simple. During the validation of SOMEFIELD I can access another fields value using field.parent.SOMEOTHERFIELD.value.
Is it possible to change SOMEOTHERFIELD's value during the validation of SOMEFIELD? Or should I separate changing related field values from the validation process altogether?
gawel's answer was a step but did not solve my problem (see comment under his answer). I changed the value of field.parent.model.SOMEOTHERFIELD but the change was not committed to the db with session.commit().
After trying out many things I found out that you must use fieldset.sync() before field.parent.model.SOMEOTHERFIELD = value. Only then the change is committed.
You can use field.parent.model.SOMEOTHERFIELD = value