How to get single item in pyrebase? - python

Hopefully a pretty easy questions follows. When I get an item with Pyrebase's .get() method, like so:
for company_id in game[company_type]:
pyre_company = db.child("companies/data").order_by_child("id").equal_to(company_id).limit_to_first(
1).get()
company = pyre_company.val()
print(company)
break # Run only once for testing purposes
I get this following output, even though I use the .val()
OrderedDict([('-LEw2zHYiJ6p15iBhKuZ', {'id': 427, 'name': 'Bugbear Entertainment', 'type': 'developer'})])
But I only want the JSON Object
{'id': 427, 'name': 'Bugbear Entertainment', 'type': 'developer'}

This is because
db.child("companies/data").order_by_child("id").equal_to(company_id).limit_to_first(1).get()
is a Query, because you call the orderByChild() method on a Reference (as well as an equalTo() method btw).
As explained here in the JavaScript SDK doc:
Even when there is only a single match for the query, the snapshot is still a
list; it just contains a single item. To access the item,
you need to loop over the result:
ref.once('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var childKey = childSnapshot.key;
var childData = childSnapshot.val();
// ...
});
});
With pyrebase you should use the each() method, as explained here, which "Returns a list of objects on each of which you can call val() and key()".
pyre_company = db.child("companies/data").order_by_child("id").equal_to(company_id).limit_to_first(1).get()
for company in pyre_company.each():
print(company.val()) // {'id': 427, 'name': 'Bugbear Entertainment', 'type': 'developer'}

Related

Recursive programming in Odoo

Can we use recursion in Odoo's function?
In my code below
def create_lines(self, item_id=None, parent_id=None):
source_items = self.env['product.source']
duplicate_items = self.env['product.duplicate']
recs = source_items.search([['parent_id', '=', item_id]])
for rec in recs:
value = { 'parent_id': parent_id,
'name': rec.name,
'date': rec.date,
'description': rec.description
}
line = duplicate_items.create(value)
self.create_lines(self, rec.id, line.id)
I'm getting SQLite objects created in a thread can only be used in that same thread
Why is this happening? And how can we enable recursion in Odoo?
It turned out the error happened because I'm using interactive python debugger ipdb.set_trace(); inside the recursion.
Also I need to correct my recursion like this
def create_lines(self, item_id=False, parent_id=False):
source_items = self.env['product.source']
duplicate_items = self.env['product.duplicate']
recs = source_items.search([['parent_id', '=', item_id]])
for rec in recs:
value = { 'parent_id': parent_id,
'name': rec.name,
'date': rec.date,
'description': rec.description
}
line = duplicate_items.create(value)
childs = source_items.search([['parent_id', '=', rec_id]])
if (len(childs)):
self.create_lines(self, rec.id, line.id)
So it doesn't recursive infinitely.

how to change schema type for single document value

I want to change the default type from dict to string for a particular user.
DOMAIN = {
'item': {
'schema': {
'profile':{
'type': 'dict'
},
'username': {
'type': 'string'
}
}
}
}
suppose if I get a request from x user type should not change. If I get a request from y user type should change from dict to string. How to change for a particular item resource without affecting others.
TIA.
Your best approach would probably be to set up two different API endpoints, one for users of type X, and another for users of type Y. Both endpoints would consume the same underlying datasource (same DB collection being updated). You achieve that by setting the datasource for your endpoint, like so:
itemx = {
'url': 'endpoint_1',
'datasource': {
'source': 'people', # actual DB collection consumed by the endpoint
'filter': {'usertype': 'x'} # optional
'projection': {'username': 1} # optional
},
'schema': {...} # here you set username to dict, or string
}
Rinse and repeat for the second endpoint. See the docs for more info.

Adding to session dictionary

I'm trying to create a view function which updates the users cart (session) on add and removal.
def shoppingCartAdd(request):
data = json.loads(request.POST['post_data'])
if 'shopping_cart' not in request.session: #<-- create the session dict
request.session['shopping_cart'] = {}
if data["action"] == "add":
with open(MEDIA_ROOT + '/json/products.js', 'r') as json_file:
products = json.loads(json_file.read()) #<-- json file that contains product information
json_file.close()
item = products["products"][data["id"]] #<-- get the item info from json
#If the item is not in the session add it. Otherwise do something.
if data["id"] not in request.session['shopping_cart']:
request.session['shopping_cart'][data["id"]] = item
else:
print('Exists')
#Do something else.
#Remove the item from the dict.
if data["action"] == "remove":
request.session['shopping_cart'].pop([data["id"]], None)
context = {'shoppingCart' : request.session['shopping_cart']}
return JsonResponse(context)
My problem is that I cannot add more than two items to my dictinary. I'm not sure what I'm doing wrong here. If I click on the first item it'll create the session and add it properly, and if I try to add it again it'll print out "exists". But if I add a second and try to add it again, it will not print out "exists" on the 2nd item.
Here's a print of my session with two items. using print(request.session['shopping_cart'])
{
'38': {'name': 'hergh', 'price': 23, 'active': 'active', 'pk': 38, 'imageURL': '/media/gg_faUCQOg.jpg', 'type': 'sokker', 'amount': 13},
'39': {'name': 'dea', 'price': 1, 'active': 'active', 'pk': 39, 'imageURL': '/media/gg_6ECtbKE.jpg', 'type': 'sokker', 'amount': 1}
}
According to the documentation - https://docs.djangoproject.com/en/dev/topics/http/sessions/#when-sessions-are-saved
By default, Django only saves to the session database when the session has been modified – that is if any of its dictionary values have been assigned or deleted.
You can set the SESSION_SAVE_EVERY_REQUEST setting to True, this will force save the session on every request.

Combination of two fields to be unique in Python Eve

In Python Eve framework, is it possible to have a condition which checks combination of two fields to be unique?
For example the below definition restricts only firstname and lastname to be unique for items in the resource.
people = {
# 'title' tag used in item links.
'item_title': 'person',
'schema': {
'firstname': {
'type': 'string',
'required': True,
'unique': True
},
'lastname': {
'type': 'string',
'required': True,
'unique': True
}
}
Instead, is there a way to restrict firstname and lastname combination to be unique?
Or is there a way to implement a CustomValidator for this?
You can probably achieve what you want by overloading the _validate_unique and implementing custom logic there, taking advantage of self.document in order to retrieve the other field value.
However, since _validate_unique is called for every unique field, you would end up performing your custom validation twice, once for firstname and then for lastname. Not really desirable. Of course the wasy way out is setting up fullname field, but I guess that's not an option in your case.
Have you considered going for a slighty different design? Something like:
{'name': {'first': 'John', 'last': 'Doe'}}
Then all you need is make sure that name is required and unique:
{
'name': {
'type':'dict',
'required': True,
'unique': True,
'schema': {
'first': {'type': 'string'},
'last': {'type': 'string'}
}
}
}
Inspired by Nicola and _validate_unique.
from eve.io.mongo import Validator
from eve.utils import config
from flask import current_app as app
class ExtendedValidator(Validator):
def _validate_unique_combination(self, unique_combination, field, value):
""" {'type': 'list'} """
self._is_combination_unique(unique_combination, field, value, {})
def _is_combination_unique(self, unique_combination, field, value, query):
""" Test if the value combination is unique.
"""
if unique_combination:
query = {k: self.document[k] for k in unique_combination}
query[field] = value
resource_config = config.DOMAIN[self.resource]
# exclude soft deleted documents if applicable
if resource_config['soft_delete']:
query[config.DELETED] = {'$ne': True}
if self.document_id:
id_field = resource_config['id_field']
query[id_field] = {'$ne': self.document_id}
datasource, _, _, _ = app.data.datasource(self.resource)
if app.data.driver.db[datasource].find_one(query):
key_names = ', '.join([k for k in query])
self._error(field, "value combination of '%s' is not unique" % key_names)
The way I solved this issue is by creating a dynamic field using a combination of functions and lambdas to create a hash that will use
which ever fields you provide
def unique_record(fields):
def is_lambda(field):
# Test if a variable is a lambda
return callable(field) and field.__name__ == "<lambda>"
def default_setter(doc):
# Generate the composite list
r = [
str(field(doc)
# Check is lambda
if is_lambda(field)
# jmespath is not required, but it enables using nested doc values
else jmespath.search(field, doc))
for field in fields
]
# Generate MD5 has from composite string (Keep it clean)
return hashlib.md5(''.join(r).encode()).hexdigest()
return {
'type': 'string',
'unique': True,
'default_setter': default_setter
}
Practical Implementation
My use case was to create a collection that limits the amount of key value pairs a user can create within the collection
domain = {
'schema': {
'key': {
'type': 'string',
'minlength': 1,
'maxlength': 25,
'required': True,
},
'value': {
'type': 'string',
'minlength': 1,
'required': True
},
'hash': unique_record([
'key',
lambda doc: request.USER['_id']
]),
'user': {
'type': 'objectid',
'default_setter': lambda doc: request.USER['_id'] # User tenant ID
}
}
}
}
The function will receive a list of either string or lambda function for dynamic value setting at request time, in my case the user's "_id"
The function supports the use of JSON query with the JMESPATH package, this isn't mandatory, but leave the door open for nested doc flexibility in other usecases
NOTE: This will only work with values that are set by the USER at request time or injected into the request body using the pre_GET trigger pattern, like the USER object I inject in the pre_GET trigger which represents the USER currently making the request

how to set value in relational field in openerp

I have written code to migrate data from sql server 2008 to PostGreSQL using OpenERPLib in Python for OpenERP. I want to set the value of "categ_id" column of type "Many2one" of "crm.opportunity2phonecall" object. Here below is my existing code.
scheduleCall = {
'name': 'test',
'action': ['schedule'],
'phone': "123456",
'user_id': 1,
"categ_id": 10,
'note': mail['body']
}
SCHEDULECALL_MODEL.create(scheduleCall)
SCHEDULECALL_MODEL = OECONN.get_model("crm.opportunity2phonecall")
In the above code i have set the hard-coded value "10" for "categ_id" field as per my requirement. When i execute above code, it gives me an error -
TypeError: unhashable type: 'list'
Try assigning a list instead of an integer as follows:
categ_id: [10]
anyway, as Atul said in his comment, update OpenERP with xmlrpc, it is safe and stable, and suppports different versions of OpenERP
Okay, I got the solution.
What i had done is - define one method in python which returns categ_id and set its value in "scheduleCall" dict and surprisingly its work. Here is my code.
scheduleCall = {
'name': 'test',
'action': ['schedule'],
'phone': "123456",
'user_id': 1,
"categ_id": get_categid_by_name('Outbound'),
'note': mail['body']
}
SCHEDULECALL_MODEL.create(scheduleCall)
SCHEDULECALL_MODEL = OECONN.get_model("crm.opportunity2phonecall")
And here is the method that i had define.
def get_categid_by_name(name):
"""return category id"""
categ_id = False
ids = CATEG_MODEL.search([('name', '=', name)])
categ_id = ids[0]
return categ_id
CATEG_MODEL = OECONN.get_model("crm.case.categ")
Hope it'll help to others.

Categories