Delete ndb entity using the key.id() - google-app-engine

I have the key id of the entity I want to delete, but when trying to delete using get_by_id, it is not deleting the entity (it's not doing anything).
The numerical id (I assume its a number, not a string) is stored in the 'key' get data:
d = car_database.Car()
ident = self.request.get('key')
d.get_by_id(ident).delete()
The id in this instance is: 5659313586569216
Thanks for the help

This is incorrect. Please look at the documentation on deleting entities - https://cloud.google.com/appengine/docs/python/ndb/entities#deleting_entities - it's pretty clear what you should be doing.
In your case you should be
ident = self.request.get('key')
d = car_database.Car.get_by_id(ident)
if d:
d.key.delete()
Note that get_by_id is in fact a classmethod so you don't need to instantiate in instance of Car to use it.
And delete is a method of the Key, not of Model.

Related

odoo domain search "id in ids"

I have a model B with a Many2many field referencing model A.
Now given an id of model A, I try to get the records of B that reference it.
Is this possible with Odoo search domains? Is it possible doing some SQL query?
Example
class A(models.Model):
_name='module.a'
class B(models.Model):
_name='module.b'
a_ids = fields.Many2many('m.a')
I try to do something like
a_id = 5
filtered_b_ids = self.env['module.b'].search([(a_id,'in','a_ids')])
However, this is not a valid search in Odoo. Is there a way to let the database do the search?
So far I fetch all records of B from the database and filter them afterward:
all_b_ids = self.env['module.b'].search([])
filtered_b_ids = [b_id for b_id in b_ids if a_id in b_id.a_ids]
However, I want to avoid fetching not needed records and would like to let the database do the filtering.
You should create the equivalent Many2many field in A.
class A(models.Model):
_name='module.a'
b_ids = fields.Many2many('module.b', 'rel_a_b', 'a_id', 'b_id')
class B(models.Model):
_name='module.b'
a_ids = fields.Many2many('module.a', 'rel_a_b', 'b_id', 'a_id')
In the field definition, the second argument is the name of the association table, and the two next ones are the name of the columns referencing the records of the two models. It's explained in the official ORM documentation.
Then you just have to do my_a_record.b_ids.
If you prefer doing an SQL request because you don't want to add a python field to A, you can do so by calling self.env.cr.execute("select id from module_b b, ...").fetchall(). In your request you have to join the association table (so you need to specify a name for it and its columns, as described in my code extract, otherwise they are automatically named by Odoo and I don't know the rule).
I think it's still possible to use search domains without the field in A but it's tricky. You can try search([('a_ids','in', [a_id])]) but I'm really not sure.
class A(models.Model):
_name='module.a'
class B(models.Model):
_name='module.b'
a_ids = fields.Many2many('module.a')
Now you want to search a_id = 5
To do so simply use browse or search ORM methods i.e,
a_id = 5
filtered_b_ids = self.env['module.b'].search([(a_id,'in',self.a_ids.ids)])
or
a_id = 5
filtered_b_ids = self.env['module.a'].search([(a_id)])

How to use ndb key with integer_id?

I see the document
https://developers.google.com/appengine/docs/python/ndb/keyclass#Key_integer_id
Returns the integer id in the last (kind, id) pair, or None if the key
has an string id or is incomplete.
see I think the id of a key can be a int ; so I write
r = ndb.Key(UserSession, int(id)).get()
if r:
return r.session
but the dev_server.py , will always raise
File "/home/bitcoin/down/google_appengine/google/appengine/datastore/datastore_stub_util.py", line 346, in CheckReference
raise datastore_errors.BadRequestError('missing key id/name')
BadRequestError: missing key id/name
I chanage the int(id) -> str(id)
seems right ;
so my question is , How to use ndb key with integer_id ?
the model is
class UserSession(ndb.Model):
session = ndb.BlobProperty()
The type of the id you use when reading the entity must match the type of the id you used when you wrote the entity. Normally, integer ids are assigned automatically when you write a new entity without specifying an id or key; you then get the id out of the key returned by entity.put(). It is generally not recommended to assign your own integer ids; when the app assigns the keys, the convention is that they should be strings.
There's an easier way to fetch:
UserSession.get_by_id(int(id))
https://developers.google.com/appengine/docs/python/ndb/modelclass#Model_get_by_id
If that doesn't work, I suspect that id is wrong or empty.
There must be something wrong with your variable 'id'.
Your code here should be no problem, and it's better to user long instead of int.
You can try your code on interactive console of development server with specific integer id.
It may be easier to identify your entities in the sessions with their keys instead of their ids. There really is no need to extract the ID from the key to identify the session (other than maybe saving a bit of memory. I think the way your thinking is based on a RDB. I learned that using the key actually makes entity/session identifications easier.
'id' is also a python builtin function. Maybe you are taking that by mistake.

ndb retrieving entity key by ID without parent

I want to get an entity key knowing entity ID and an ancestor.
ID is unique within entity group defined by the ancestor.
It seems to me that it's not possible using ndb interface. As I understand datastore it may be caused by the fact that this operation requires full index scan to perform.
The workaround I used is to create a computed property in the model, which will contain the id part of the key. I'm able now to do an ancestor query and get the key
class SomeModel(ndb.Model):
ID = ndb.ComputedProperty( lambda self: self.key.id() )
#classmethod
def id_to_key(cls, identifier, ancestor):
return cls.query(cls.ID == identifier,
ancestor = ancestor.key ).get( keys_only = True)
It seems to work, but are there any better solutions to this problem?
Update
It seems that for datastore the natural solution is to use full paths instead of identifiers. Initially I thought it'd be too burdensome. After reading dragonx answer I redesigned my application. To my suprise everything looks much simpler now. Additional benefits are that my entities will use less space and I won't need additional indexes.
I ran into this problem too. I think you do have the solution.
The better solution would be to stop using IDs to reference entities, and store either the actual key or a full path.
Internally, I use keys instead of IDs.
On my rest API, I used to do http://url/kind/id (where id looked like "123") to fetch an entity. I modified that to provide the complete ancestor path to the entity: http://url/kind/ancestor-ancestor-id (789-456-123), I'd then parse that string, generate a key, and then get by key.
Since you have full information about your ancestor and you know your id, you could directly create your key and get the entity, as follows:
my_key = ndb.Key(Ancestor, ancestor.key.id(), SomeModel, id)
entity = my_key.get()
This way you avoid making a query that costs more than a get operation both in terms of money and speed.
Hope this helps.
I want to make a little addition to dargonx's answer.
In my application on front-end I use string representation of keys:
str(instance.key())
When I need to make some changes with instence even if it is a descendant I use only string representation of its key. For example I have key_str -- argument from request to delete instance':
instance = Kind.get(key_str)
instance.delete()
My solution is using urlsafe to get item without worry about parent id:
pk = ndb.Key(Product, 1234)
usafe = LocationItem.get_by_id(5678, parent=pk).key.urlsafe()
# now can get by urlsafe
item = ndb.Key(urlsafe=usafe)
print item

One To Many and Duplicate entry

I use JPA->Hibernate. PlayFramework. I want to have relationship.
Category - 1:n -> Tag
Every category can have many tags, but tags do not know about it.
So, i do like this:
#Entity
public class Category ... {
#OneToMany
public List<Tag> tags = new LinkedList<Tag>();
}
I have test:
#Test
public void playWithTags() {
Tag tag1 = new Tag("tag1").save(); // managed by playframework
Category cat1 = new Category("cat1");
cat1.tags.add(tag1);
cat1.save();
// check if tag1 and cat1 were saved
assertEquals(1, Tag.count());
assertEquals(1, Category.count());
Category cat2 = new Category("cat2");
cat2.tags.add(tag1);
cat2.save();
}
The result is:
16:18:01,555 ERROR ~ Duplicate entry '1' for key 'tags_id'
16:18:01,555 ERROR ~ Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelp
....
java:908)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.sql.BatchUpdateException: Duplicate entry '1' for key 'tags_id'
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:2020)
It seems that cat2.save() try to do more then it should
If if use merge() instead of save() it works good:
cat2.merge();
BUT WHY?
I have fixed the problem. The problem was in that, that I used NOT THAT annotation. So i just changed #OneToMany to #ManyToMany and voilĂ  - No any restrictions anymore.
But if saying about the OneToMany then it seems there was a unique-restriction on database-level which prevented us to put not-unique values to tags_id. Therefore we could not put same tag to One category. I.e. it wanted One category for Many tags, but if tags were already 'used' - no way.. I tried to put unique=true/false in #JoinTable -> #JoinColumn - but it does not help. For me it's still strange, but at least current problem was fixed.
You're mixing up two concepts: Primary key and foreign key.
There can be only one PK but FK just means "there must be an element with this ID in some other table". FK doesn't constrain uniqueness.
[EDIT] Your problem is that you're mixing entities. How did you get the tag1 which is returned by save()?
This entity must be one which you get from Hibernate, not the result from new. Even if it looks insane, you must do this in save():
session.save(tag);
return session.load(tag.getId());
This way, you get an entity that is managed by Hibernate. Only when the entity is managed by Hibernate, Hibernate knows when it has to save the entity and when it has already been saved.
So when you do cat2.tags.add(tag1); in your example above, Hibernate thinks "oh, I don't know anything about this tag, it must be a new one".
And tries to save the tag again.

best method to keep and update unique data entries google app engine python

What is the best method to create a database for the given example model and assign each entry with a unique key/name which I already have and to overwrite it if the given key/name shows up again. From what I read you are supposed to use keyname? But I am not getting it to overwrite.
class SR(db.Model):
name = db.StringProperty()
title = db.StringProperty()
url = db.StringProperty()
s = SR(key_name="t5-2rain")
s.name = 't5-2rain'
s.title = 'kaja'
s.url = 'okedoke'
db.put(s)
If I enter this again with the same key name but different title value, will create another entry how do I overwrite an existing value with the same key-name.
Basically how do I populate a table with unique identifiers and overwrite values if the same unique identifier already exist?
I realize I can search for an existing name or key name etc, call that object make the changes to the instances and repopulate but, I would imagine there has to be a better method than that for overwriting especially if I am trying to put a list where some values may be overwrites and some not.
You've got the right idea already.
If 2 SR entities were constructed with the same key_name argument, then they will have the same Key path. Writing one will overwrite any old SR entity which had that key_name argument.
You should be able to observe this by querying the datastore for the entity with its unique key:
s = db.get(db.Key.from_path('SR', 't5-2rain'))

Resources