Add extra field in Django QuerySet as timedelta type - sql

I have the following model:
class UptimeManager(models.Manager):
def with_length(self):
"""Get querySet of uptimes sorted by length including the current one. """
extra_length = Uptime.objects.extra(select={'length':
"""
SELECT
IF (end is null,
timestampdiff(second,begin,now()),
timestampdiff(second,begin,end))
FROM content_uptime c
WHERE content_uptime.id = c.id
"""
})
return extra_length
class Uptime(models.Model):
begin = models.DateTimeField('beginning')
end = models.DateTimeField('end', null=True) I call
host = models.ForeignKey("Host")
objects = UptimeManager()
...
then I call Uptime.objects.with_length().order_by('-length')[:10] to get list of longest uptimes.
But the length in template is of integer type. How to modify my code as the length of object returned by manager would be accessible in template as timedelta object?
I almost could do it by returning a list and converting number of seconds to timedelta objects, but then I have to do sorting, filtering etc. in my Python code which is rather ineffective in comparison to one well done SQL query.

Add a property to the model that looks at the actual field and converts it to the appropriate type.

My solution is to create a filter that determines type of length var and returns timedelta in case it's some integer type
from django import template
import datetime
register = template.Library()
def timedelta(value):
if isinstance(value, (long,int)):
return datetime.timedelta(seconds=value)
elif isinstance(value, datetime.timedelta):
return value
else: raise UnsupportedOperation
register.filter('timedelta',timedelta)
and use in template it's trivial
{{ uptime.length|timedelta }}

Related

Django REST Framework serializer for a list of dictionaries

I'm new to Django REST Framework and searching for a method to serialize a list of dictionaries.
Target data is a time-series. Each dictionary contains hourly value and each list contains hourly values of a day.
class Hourly:
hour = None
value = None
def __init__(self, hour, value):
self.hour = hour
self.value = value
class Daily:
date = None
vlist = None
def __init__(self, date, vlist):
self.date = date
self.vlist = vlist
It is easy to create a serializer for the hourly class by following the Django REST Framework API guide.
class HourlySerializer(serializers.Serializer):
hour = serializers.IntegerField(required=True, min_value=0, max_value=23)
value = serializers.FloatField(required=True)
def create(self, validated_data):
return Hourly(**validated_data)
def update(self, instance, validated_data):
instance.hour = validated_data.get('hour', instance.hour)
instance.value = validated_data.get('value', instance.value)
return instance
My objective is simple: creating a serializer for the daily data.
(1) The serializer must restrict the length of data as 24. (2) It should have an ability to validate each hourly data.
#1. Using ListField
In case of restricting the data length, ListField has arguments min_length and max_length #.
I think this kind of code may be possible #, however, the API guide does not say anything about defining the child of a ListField as a serializer.
class DailySerializer(serializers.Serializer):
date = serializers.DateField(required=True)
vlist = serializers.ListField(child=HourlySerializer(), min_length=24, max_length=24)
#2. Using ListSerializer
The API guide says one can nest serializers just like they are fields. If the nested value is a list of items, many=True can be used to initiate ListSerializer. However, there is no argument about restricting the data length.
class DailySerializer(serializers.Serializer):
date = serializers.DateField(required=True)
vlist = HourlySerializer(many=True)
Here are my questions:
Is the method using ListField is possible with no problem? Or, can I pass min_length and max_length arguments with many=True?
The API guide does not say anything about defining create() and update() for ListField or ListSerializer. There are only cases for a nested serializer without many option # and a ListSerializer without non-list field #. Is there any example about this?
Answer to my own question.
class DailySerializer(serializers.Serializer):
date = serializers.DateField(required=True)
vlist = serializers.ListField(child=HourlySerializer(), min_length=24, max_length=24)
def create(self, validated_data):
return Daily(**validated_data)
def update(self, instance, validated_data):
instance.date = validated_data.get('date', instance.date)
instance.vlist = validated_data.get('vlist', instance.vlist)
return instance
Above ListField based DailySerializer is tested with the below input.
[
{'date': '2000-01-01', 'vlist': [{'hour':'00', 'value': '012'}, {'hour':'01', 'value': '123'}, ...],
{'date': '2000-01-02', 'vlist': [{'hour':'00', 'value': '012'}, {'hour':'01', 'value': '123'}, ...],
...,
]
To deal with the additional multi-day nest, serializer in the view is defined with many=True.
serializer = DailySerializer(data=data, many=True)
Test results are below:
Using the ListField method does not make any problem during validation.
Changing some hourly value as an arbitrary string is detected as an non-integer value.
Daily data with its length not equal to 24 is also detected.
Using naive create() and update() methods with ListField can be problematic.
obj_list = serializer.validated_data returns a List of OrderedDict objects.
obj_list = serializer.save() returns a List of Daily objects.However, obj_list[0].vlist is a List of OrderedDict objects, not a List of Hourly objects.

Odoo 10 selection fields value

How can i get selection fields value in odoo 10?
def compute_default_value(self):
return self.get_value("field")
I tried this,
def compute_default_value(self):
return dict(self._fields['field'].selection).get(self.type)
Also tried this,but it is not working.
Please help me, i could not find the solution.
Thank you.
You can do this in a following manner:
self._fields['your_field']._desription_selection(self.env)
This will return the selection list of pairs (value, label).
If you just need possible values, you can use get_values method.
self._fields['your_field'].get_values(self.env)
But it's not a common way. Most of the time people define selections differently and then use those definitions. For example, I commonly use classes for those.
class BaseSelectionType(object):
""" Base abstract class """
values = None
#classmethod
def get_selection(cls):
return [(x, cls.values[x]) for x in sorted(cls.values)]
#classmethod
def get_value(cls, _id):
return cls.values.get(_id, False)
class StateType(BaseSelectionType):
""" Your selection """
NEW = 1
IN_PROGRESS = 2
FINISHED = 3
values = {
NEW: 'New',
IN_PROGRESS: 'In Progress',
FINISHED: 'Finished'
}
You can use this class wherever you want, just import it.
state = fields.Selection(StateType.get_selection(), 'State')
And it's really handy to use those in the code. For example, if you want to do something on a specific state:
if self.state == StateType.NEW:
# do your code ...
I don't get the question fully, but let me try to answer. Why not just define the selection as method and use it for both situations:
from datetime import datetime
from odoo import models, fields
class MyModel(models.Model):
_name = 'my.model'
def month_selection(self):
return [(1, 'Month1'), (2, 'Month2')]
def compute_default_value(self):
selection = self.month_selection()
# do whatever you want here
month = fields.Selection(
selection=month_selection, string='Month',
default=datetime.now().month, required=True)

Odoo: Access field by it's name (given as string)

I have a model, where I want to access a field, given by a string. Example:
def test(self):
field = 'name'
name = getattr(self, field)
This works fine - name is set to self.name. But then I want to access a related field:
def test2(self):
field = 'partner_id.name'
name = getattr(self, field)
That doesn't work (because 'partner_id.name' does not exist on self). Any idea how to do it right?
getattr doesn't support the dot notation, only simple attribute names. You can however create a simple function that does:
def getfield(model, field_name):
value = model
for part in field_name.split('.'):
value = getattr(value, part)
return value
You would use it like this:
def test2(self):
field = 'partner_id.name'
name = getfield(self, field)
You need to use the object that contain partner_id.name
def test2(self):
field = 'name'
object = self.pool.get('res.partner').browse(cr, uid, self.partner_id.id)#v7
#object = self.env['res.partner'].browse(self.partner_id.id)#v8
name = getattr(object, field)
I also came across another solution, inspired by the mail template system:
from openerp.tools.safe_eval import safe_eval as eval
def test2(self):
field = 'partner_id.name'
field = 'object.' + field
name = eval(field, {'object': self})

How to not store an integer field in openerp

I have an integer field in which i just display a value calculated with an 'on_change' function.
Is there anyway to not store that value in my Database(another way than using a function field)?
I also tried to put store=False but i guess it doesn't work with an integer field
here's my integer field:
'quantite_op': fields.integer('Quantité produite par opération',store=False,readonly=True),
And the onchange function:
def onchange_num_of(self,cr,uid,ids,of_num,operation_nom,quantite,context=None):
res = {}
if of_num:
ordre_f = self.pool.get('ordres_fabrication').browse(cr, uid,of_num,context=context)
res['delai_of'] =ordre_f.delai
res['quantite_of'] =ordre_f.quantite
if operation_nom:
record =self.search(cr, uid, [('of_num','=',of_num),('operation_nom','=',operation_nom)])
qte=0
for item in record:
prod = self.browse(cr, uid,item,context=context)
qte += prod.quantite
res['quantite_op'] = qte+quantite
return {'value': res}
Thanks
you will always have that field in your db, except it's a non-stored functional field. but you can manipulate the create or write methods of the model. you can set your integer field on None resp. False in that methods, so no value will be stored in that db column.

Return .str of ObjectID using pymongo

How would I return just the string component of an BSON ObjectId using pymongo. I'm able to encode a string into an Object id by importing ObjectId from bson.objectid; but am unable to do the reverse.
When I try:
for post in db.votes.find({'user_id':userQuery['_id']}):
posts += post['_id'].str
I get an ObjectId has no attribute str error.
Thanks!
The standard way in python to get object's string representation is using the str builtin function:
id = bson.objectid.ObjectId()
str(id)
=> '5190666674d3cc747cc12e61'
try this:
for post in db.votes.find({'user_id':userQuery['_id']}):
posts += str(post['_id'])
BTW, you can use MongoKit to deal with the special bson data structure.
from bson.objectid import ObjectId
class CustomObjectId(CustomType):
mongo_type = ObjectId # optional, just for more validation
python_type = str
init_type = None # optional, fill the first empty value
def to_bson(self, value):
"""convert type to a mongodb type"""
return ObjectId(value)
def to_python(self, value):
"""convert type to a python type"""
return str(value)
def validate(self, value, path):
"""OPTIONAL : useful to add a validation layer"""
if value is not None:
pass # ... do something here
This custom ObjectId can turn the bson ObjectId to python str.
Visit http://mongokit.readthedocs.org/mapper.html#the-structure for more information.