I'm just getting familiar with cakephp (thanks to the developer before me), and ran into something funny. I have finally found out what went wrong, but still do not know why. In very pseudo code:
a controller function calls bar() twice in the same scope:
$value = 'A';
$this->foo->bar($value);
// do other stuff
$value = 'B';
$this->foo->bar($value);
bar() basically just calls cakephp's save() model to write $value to table foobar$:
$AppModel->save(array(
'AppModel'=> array('value'=>$value)
));
I expected that save() would create two rows in foobar$, however this was not the case. It first created a row with value A, then updated that row to value B. When the second call ran, it recognized the DB id generated by the previous call, decided it was the same entry and made it an update instead of insert. It sort of makes sense, but they still are separate calls, right? What obvious thing am I missing here? Thanks a lot.
After saving something to the database, Cake sets the $Model->id to the last insert id.
When saving, if there's either an id field in the data array it is supposed to save, or if there's an id set on $Model->id, Cake will update this entry instead. Both of these update the entry 42:
$Model->save(array('id' => 42, 'value' => 'foo'));
$Model->id = 42;
$Model->save(array('value' => 'foo'));
To make sure you're creating a new entry, call Model::create(), as described here.
I typically always put a model create call before a save. If the array your saving has the primary key already it will update the row, otherwise it does an insert:
Insert:
$Model->create();
$Model->save(array('value'=>'foo'));
Update
$Model->create();
$Model->save(array('id'=>1,'value'=>bar'));
Related
My code in ServiceDetailController
$users = $this->ServiceDetail->Users->find()->extract('first_name')->where(['position' => 2]);
I get an error:
Call to undefined method Cake\ORM\ResultSet::where()
Debugging basics: Check if the method exists in the called object. If not ask yourself why. Then check what extract() returns and you'll get your answer. You basic problem, as a metaphor is that you try to drink from a bottle before opening it. You can't extract without having a result first.
Extract does not return a query object. First build your query, then execute it, all() for example, and then call extract on the result object because it implements a collection that allows you to call extract() on it.
Is there an easy way to update a field on a get of a get_or_create?
I have a class ItemCategory and I want to either create a new entry or get the already created entry and update a field (update_date).
What I do is:
item,created= ItemCategory.get_or_create(cat=cat_id,item=item_id)
if created == True:
print "created"
else:
item.update_date = datetime.now
item.somethingelse = 'aaa'
item.save()
This works for a while in my loop. But after 50-100 get/create it crashes:
peewee.InterfaceError: Error binding parameter 4 - probably unsupported type.
Maybe I should use upsert(), I tried but wasn't able to get anything working. Also it's not probably the best solution, since it makes a replace of the whole row instead of just a field.
I like peewee, it's very easy and fast to use, but I can't find many full examples and that's a pity
Newbie mistake
item.update_date = datetime.now()
I am not 100% sure this is the only answer though. I modified my code so many times that it might be also something else.
Regarding my question about create_or_update , I've done this:
try:
Item.create(...)
except IntegrityError:
Item.update(...)
peewee is really great, I wonder why no one ever asked for a create_or_update.
I pass an NDB Key() with a parent to a deferred function. In this function I retrieve the entity again. But I cannot use the passed key to get the entity directly. I have to change the key order pairing in the ndb.Key().
deferred.defer(my_deferred.a_function, entity.key)
The entity.key() looks like :
Key('Parents', 'my_parent', 'Childs', 'my_child') # the first pair is the parent?
my_deferred.py :
def a_function(key) :
entity = ndb.Key(key) # the pass entity.key does not work !!!!!
Giving exception : ValueError: Key() must have an even number of positional arguments.
entity = ndb.Key('Childs', key.id(), parent = key.parent()).get() # this one works fine
I do not understand why the entity.key() method does not give me a key, which I can use directly? Or is there another way to get the entity, without "changing" the key. And I do not understand the ValueError excpetion.
Update : Thanks to Gregory
entity = key.get() # works fine
first, answering your code specific question, passing the key properly, it is not a callable:
deferred.defer(my_deferred.a_function, entity.key)
next, on the actual design of the code itself, there are some things that need tweaking.
the deferred api serializes your code, so there really is no need to re-query entity from the datastore. if you insist on this though, passing the entity.key to the deferred method, it's already an instance of ndb.Key, so there's no need to construct a new Key object.
I can't test this right now, but what about:
entity = ndb.Key(*key.flat())
The Key constructor accepts a few different kinds of input, and since flat() Returns a tuple of flattened kind and id values (kind1, id1, kind2, id2, ...)., unpacking the tuple should pass in the necessary inputs . Per the same link, this should also work:
entity = ndb.Key(pairs=key.pairs())
I have the following django method:
def setCurrentSong(request, player):
try:
newCurrentSong = ActivePlaylistEntry.objects.get(
song__player_lib_song_id=request.POST['lib_id'],
song__player=player,
state=u'QE')
except ObjectDoesNotExist:
toReturn = HttpResponseNotFound()
toReturn[MISSING_RESOURCE_HEADER] = 'song'
return toReturn
try:
currentSong = ActivePlaylistEntry.objects.get(song__player=player, state=u'PL')
currentSong.state=u'FN'
currentSong.save()
except ObjectDoesNotExist:
pass
except MultipleObjectsReturned:
#This is bad. It means that
#this function isn't getting executed atomically like we hoped it would be
#I think we may actually need a mutex to protect this critial section :(
ActivePlaylistEntry.objects.filter(song__player=player, state=u'PL').update(state=u'FN')
newCurrentSong.state = u'PL'
newCurrentSong.save()
PlaylistEntryTimePlayed(playlist_entry=newCurrentSong).save()
return HttpResponse("Song changed")
Essentially, I want it to be so that for a given player, there is only one ActivePlaylistEntry that has a 'PL' (playing) state at any given time. However, I have actually experienced cases where, as a result of quickly calling this method twice in a row, I get two songs for the same player with a state of 'PL'. This is bad as I have other application logic that relies on the fact that a player only has one playing song at any given time (plus semantically it doesn't make sense to be playing two different songs at the same time on the same player). Is there a way for me to do this update atomically? Just running the method as a transaction with the on_commit_success decorator doesn't seem to work. Is there like a way to lock the table for all songs belonging to a particular player? I was thinking of adding a lock column to my model (boolean field) and either just spinning on it or pausing the thread for a few milliseconds and checking again but these feel super hackish and dirty. I was also thinking about creating a stored procedure but that's not really database independent.
Locking queries were added in 1.4.
with transaction.commit_manually():
ActivePlayListEntry.objects.select_for_update().filter(...)
aple = ActivePlayListEntry.objects.get(...)
aple.state = ...
transaction.commit()
But you should consider refactoring so that a separate table with a ForeignKey is used to indicate the "active" song.
I have this field whose value i have to increment the field value by a specific value.
I am using this
$data['quantity'] = 'Order.quantity+1';
which doesnt works for me quantity is a integer coloumn here.
Also will it work when nothing is in database?.
Regards
Himanshu Sharma
I used updateAll in my code to increment views in an article. Therefore, every time an article is visited, I call the following function from within my view action in my articles controller:
function incrementViewCount($id) {
$this->updateAll(
array('Article.viewed' => 'Article.viewed+1'),
array('Article.id' => $id)
);
}
Then in your controller…
$this->MyModel->incrementViewCount(123);
Basically similar to the tutorial suggested in the previous answer.
you can use updateAll() for this
a little googling reveals this pretty quick:
http://cakephp.1045679.n5.nabble.com/Auto-Increment-A-Field-td3491697.html
You can try this code also. Works fine for simple ++/-- operations without the need of additional querying.
https://gist.github.com/denchev/8209318
Using saveField:
<?php
$this->Article->id = $id;
$this->Article->saveField('viewed', (int)$this->Article->field('viewed') + 1);
?>