In brief, what is the default value for a binary field in Odoo?
More specifically, I'm trying to construct a computed field based on whether or not certain documents have been included in a record (i.e. a sort of status bar on the number of completed fields in the record).
As a toy example if bin1 and bin2 are binary fields and bool is boolean, then my progress would be computed as
progress = 100.0 * (1*bool + 1*(bin1 is not None) + 1*(bin2 is not None)) / 3
Fortunately, this computation works fine after the record is saved. However, while in Edit mode the progress is shown as if it were 2/3.
This brings be to the question of default values for binary fields or any ideas on how to extract the information about whether of not a binary field is filled or not.
An empty binary field is False a valued one contains base64 encoded string.
So, before you do your computatation you must do something like:
if item.bin_field:
bin_val = item.bin_field.decode('base64')
Your check if failing because you are doing an "identity comparison" so you are basically saying "is my value identical to None?" instead of checking if is boolean-ly false.
Related
I'm working on a small web-app that includes a filter with around 10/15 different fields,
since the user doesn't have to fill all the fields in order to be able to post the form, I implemented the below logic in order to avoid filtering the database with empty strings or values and obtain 0 results.
if not form.Price_from.data == "" and not form.Price_from.data == None:
Posts_filtered = Posts_filtered.filter(Post.Price >= form.Price_from.datas)
By doing this, I filter the column Post. Price only if the field form.Price_from field contains a value.
Since I have 10-15 fields, you can imagine that I have a lot of lines doing the same thing and I really do not like it. Since I'd like to add more fields in the future, my question is if there is a more efficient way to perform this action?
Do you think it would be faster to implement it with js? By doing that I should not need to send a post request every time and I'd save time.
Thank you!
you can use a helper function and loop through every field:
def is_filled(raw_data):
try:
value = raw_data[0]
if value == '':
return False
except (IndexError, TypeError):
return False
return True
(use raw_data from the field instead of data for this function)
you can more info here: https://stackoverflow.com/a/47450458/11699898
While looking at the Django docs on querying JSONField, I came upon a note stating:
Due to the way in which key-path queries work, exclude() and filter() are not guaranteed to produce exhaustive sets. If you want to include objects that do not have the path, add the isnull lookup.
Can someone give me an example of a query that would not produce an exhaustive set? I'm having a pretty hard time coming up with one.
This is the ticket that resulted in the documentation that you quoted: https://code.djangoproject.com/ticket/31894
TL;DR: To get the inverse of .filter() on a JSON key path, it is not sufficient to only use .exclude() with the same clause since it will only give you records where the JSON key path is present but has a different value and not records where the JSON key path is not present at all. That's why it says:
If you want to include objects that do not have the path, add the isnull lookup.
If I may quote the ticket description here:
Filtering based on a JSONField key-value pair seems to have some
unexpected behavior when involving a key that not all records have.
Strangely, filtering on an optional property key will not return the
inverse result set that an exclude on the same property key will
return.
In my database, I have:
2250 total records 49 records where jsonfieldname = {'propertykey': 'PropertyValue'}
296 records where jsonfieldname has a 'propertykey' key with some other value
1905 records where jsonfieldname does not have a 'propertykey' key at all
The following code:
q = Q(jsonfieldname__propertykey="PropertyValue")
total_records = Record.objects.count()
filtered_records = Record.objects.filter(q).count()
excluded_records = Record.objects.exclude(q).count()
filtered_plus_excluded_records = filtered_records + excluded_records
print('Total: %d' % total_records)
print('Filtered: %d' % filtered_records)
print('Excluded: %d' % excluded_records)
print('Filtered Plus Excluded: %d' % filtered_plus_excluded_records)
Will output this:
Total: 2250
Filtered: 49
Excluded: 296
Filtered Plus Excluded: 345
It is surprising that the filtered+excluded value is not equal to the total record count. It's surprising that the union of a expression plus its inverse does not equal the sum of all records. I am not aware of any other queries in Django that would return a result like this. I realize adding a check that the key exists would return a more expected results, but that doesn't stop the above from being surprising.
I'm not sure what a solution would be - either a note in the documentation that this behavior should be expected, or take a look at how this same expression is applied for both the exclude() and filter() queries and see why they are not opposites.
I have the model stock.production.lot. I added to this model two one2many fields, incoming_moves and outgoing_moves, both of them pointing to model stock.move. I have also added a float field named qty on stock.move.
And I added two computed fields in stock.production.lot which calculate the incoming and the outgoing quantity.
Now, I want to forbid lots whose outgoing quantity is higher than the incoming one. For this, I have made a constraint in stock.move. I know the most reasonable option would be make this constraint in stock.production.lot, but the problem I see is that in #api.constrains decorator I cannot use dot notation, so I can only write #api.constrains('incoming_moves', 'outgoing_moves'), and this means that if I modify the quantity of a move, this constraint is not even being executed.
So my constraint in stock.move is:
#api.one
#api.constrains('qty', 'restrict_lot_id')
def _check_qties(self):
if (self.in_vqty - self.out_vqty) < 0:
raise ValidationError(
_('The incoming quantity (Kg) of the lot is lower than '
'the outgoing one.')
)
It seems to work great in any case, except for the following one: if I modify the quantity of several moves of the same lot at the same time (from the one2many field of the lot form), the ORM write method behaviour makes the constraint raise the error inspite of the quantities are OK.
Example
I have a lot which an incoming move (InM1) of 8 and three outgoing
moves (OutM1, OutM2, OutM3) of 1, 3 and 4 (lot quantity => 8 - (1+3+4) = 0, it is OK). I go to the form of this
lot and edit the one2many of outgoing_moves. From there, I set OutM1
to 2 and OutM3 to 3, so the total quantity of the lot will be 8 -
(2+3+3), which is 0 and is OK too. But, when I click on Save button, the
constraint raises the error. Why?
This is what is happening: after clicking on Save, ORM write
method of stock.move is called. First time, it wants to update
OutM1, and it does it, but just after that, the constraint is checked
and as OutM3 has not been updated yet, it receives 8 - (2+3+4), and
that is the reason of the error raising.
So I tried to remove the constraint and do the check inside ORM write method, but I face the same problem. I put my checks after the super, but the ORM write method is called twice, the first time to update OutM1 and the second one to update OutM3. Between those actions the exception is raised too, due to the same reason I explained above.
How could I solve this?
I have a simple object filter that uses price__lt and price__gt. This works on a property on my product model called price, which is a CharField [string] (decimal saw the same errors, and caused trouble with aggregation so reverted to string).
It seems that when passing in these values to the filter, they are treated in a strange way, eg 10 is treated as 100. for example:
/products/price/10-200/ returns products priced 100-200. the filters are being passed in as filterargs: FILTER ARGS: {'price__lt': '200', 'price__gt': '10'} . This also breaks in the sense that price/0-170 will NOT return products priced at 18.50; it is treating the 170 as 'less than 18' for some reason.
any idea what would cause this, and how to fix it? Thanks!
The problem, as Jeff suggests, is that price is a CharField and thus is being compared using character-by-character string comparison logic, i.e. any string of any length starting with 1 will be less than any string of any length starting with 2, etc.
I'm curious what problems you were having with having price be an IntegerField, as that would seem to be the straightforward solution, but if you need to keep price as a CharField, here's a (hacky) way to make the query work:
lt = 200
gt = 10
qs = Product.objects.extra(select={'int_price': 'cast(price as int)'},
where=['int_price < %s', 'int_price > %s'],
params=[lt, gt])
qs.all() # the result
This uses the extra method of Django's QuerySet class, which you can read about in the docs here. In a nutshell, it computes an integer version of the string price using SQL's cast expression and then filters with integers based on that.
I have a model which looks like this:
class MyModel(models.Model)
value = models.DecimalField()
date = models.DatetimeField()
I'm doing this request:
MyModel.objects.aggregate(Min("value"))
and I'm getting the expected result:
{"mymodel__min": the_actual_minimum_value}
However, I can't figure out a way to get at the same time the minimum value AND the associated date (the date at which the minimum value occured).
Does the Django ORM allow this, or do I have to use raw SQL ?
What you want to do is annotate the query, so that you get back your usual results but also have some data added to the result. So:
MyModel.objects.annotate(Min("value"))
Will return the normal result with mymodel__min as an additional value
In reply to your comment, I think this is what you are looking for? This will return the dates with their corresponding Min values.
MyModel.objects.values('date').annotate(Min("value"))
Edit: In further reply to your comment in that you want the lowest valued entry but also want the additional date field within your result, you could do something like so:
MyModel.objects.values('date').annotate(min_value=Min('value')).order_by('min_value')[0]
This will get the resulting dict you are asking for by ordering the results and then simply taking the first index which will always be the lowest value.
See more