My question concerns a few methods in the Laravel Eloquent ORM. Particularly, 3 methods have caught my attention:
firstOrCreate
firstOrNew
updateOrCreate
I clearly understand what these methods do but did not find a way to determine which operation was performed after calling them.
For example, after calling firstOrCreate, how do I figure out if the model was found in the database or created? I can run a find query first and then create the record if it was not found but that would be very inefficient.
Similarly, how to find out if an object was created or found in the database in case of firstOrNew and if the object was updated or created in case of updateOrCreate.
provided you have the timestamps turned on for those tables you could simply check their values in the returned object.
The "exists" property will return true if the model is backed by the database (false if "new" occurred).
The "wasRecentlyCreated" property will return true if the record was just created (false if it already existed).
This should let you disambiguate between all the cases you mentioned.
Related
How is it possible to implement dynamic choices in Google APpengine ndb?
class Choice(ndb.Model):
name = ndb.StringProperty()
class List(ndb.Model):
choices = ndb.KeyProperty(Choice, choices=Choice.query(keys_only=True).fetch(), repeated=True)
This code exit with TypeError: __init__() got an unexpected keyword argument 'keys_only'.
You have a typo in your code that is causing the error you mentioned:
(keys-only=True)
should be
(keys_only=True)
Also, that argument needs to be passed to .fetch(), not to .query():
choices=Choice.query().fetch(keys_only=True)
But regardless of that, getting the list of choices that way is not possible, defining it at the ndb model definition level cannot be dynamic.
You can implement it at the application level, obtaining the list of available choices and checking the values being added to the choices property against that list before .put(). It may be a bit tricky if you need to do that transactionally since you can't make the (non-ancestor) query inside transactions. To address a similar problem I used memcache to store the list of choices obtained by query (outside a transaction) and get it from there inside transactions.
I put the following code in AppController or RecipesController in order to see the return value of loadModel. According to the loadModel API, it should return true when single model found and instance created.
However, I both got 1 which I considered it as true, even though safasfasfafafafas doesn't exist.
$this->log($this->loadModel('safasfasfafafafas')); // random string
$this->log($this->loadModel('Recipe')); // exist in my model
I'm quite new on cakephp, may I know where I have missed?
The docs are incorrect, please refer to a more recent version and also look at the source code as even the most recent docs are not really correct (or let's say they are misleading).
http://api.cakephp.org/2.5/class-Controller.html#_loadModel
http://api.cakephp.org/2.5/source-class-Controller.html#717-746
As can be seen, loadModel() will always return true, also ClassRegistry::init(), which is used to actually load the model, will make use of an instance of AppModel in case the actual requested model doesn't exist, so the exception will only be thrown in case even that class cannot be found.
See also http://book.cakephp.org/2.0/en/models.html#understanding-models
Also note that the model won't try to access the table when it's being instantiated, so the possible missing table exception will only be thrown once you actually try to use the model, for example for querying records.
I have a situation where, in a model's afterSave callback, I'm trying to access data from a distant association (it's a legacy data model with a very wonky association linkage). What I'm finding is that within the callback I can execute a find call on the model, but if I exit right then, the record is never inserted into the database. The lack of a record means that I can't execute a find on the related model using data that was just inserted into the current.
I haven't found any mention of when data is actually committed with respect to when the afterSave callback is engaged. I'm working with legacy code, but I see no indication that we're specifically engaging transactions, so I'm trying to figure out what my options might be.
Thanks.
UPDATE
The gist of the scenario is this: We're taking event registrations, but folks can be wait listed. A user can register (or be registered) for a given Date. After a registration is complete, I need to check the wait list for the existence of a record for the registering user (WaitList.user_id) on the date being registered for (WaitList.date_id). If such a record exists, it can be deleted because it's become an active registration.
The legacy schema puts me in a place where the registration isn't directly tied to a date so I can't get the Date.id easily. Instead, Registration->Registrant->Ticket->Date. Unintuitive, I know, but it is what it is for now. Even better (sarcasm included), we have a view named attendees that rolls all of this info up and from which I would be able to use the newly created Registration->id to return Attendee.date_id. Since the record doesn't exist, it's not available in the view.
Hopefully that provides a little more context.
What's the purpose of the find query inside of your afterSave?
Update
Is it at all possible to properly associate the records? Or are we talking about way too much refactoring for it to be worth it? You could move the check to the controller if it's not possible to modify the associations between the records.
Something like (in psuedo code)
if (save->isSuccessful) {
if (onWaitList) {
// delete record
}
}
It's not best practice, but it will get you around your issue.
I'm trying to peform an update statement using WCF RIA Services, but everytime I update I keep getting "An entity with the same identity already exists in this EntitySet. Any insight on where I can start looking or figuring out what is wrong?
Step 1
LoadOperation<Analysis> AnalysisLP = ((App)Application.Current)._context.
Load(((App)Application.Current)._context.GetAnalysisQuery().
Where(o => o.ProjectID == Convert.ToInt32(((App)Application.Current).Project.ProjectID)));
Step 2
AnalysisLP.Completed += delegate
{
if (!AnalysisLP.HasError)
{
Analysis = AnalysisLP.Entities.FirstOrDefault();
};
Step 3
((App)Application.Current)._context.Analysis.Attach(Analysis);
((App)Application.Current)._context.SubmitChanges(OnSubmitCompleted, null);
Can anyone help me, what is it i'm doing wrong??
thanks
Your object Analysis comes from the EntitySet via a query but is still attached to that EntitySet.
You just need to change its properties and call SubmitChanges. Do not try to attach it again.
To avoid the “An Entity with the same identity already exists in the EntitySet” exception, Entities that are updated, modified or deleted must always be fully refreshed from server upon saving, there can be no references held in memory to the previous instances of the entities. To prevent orhpaned instances from hanging around, I follow these rules:
Entity instances should not have any property changed event handlers assigned directly to them, rather use OnCreated or OnPropertyNameChanged partial methods instead.
When entities are added to an EntitySet, do not assign parent Entity instance references, use the foreign key ID property instead (myEntity.ParentalID = SelectedParent.ParentalID rather than myEntity.Parent = SelectedParent) because the SelectedParent probably isn’t getting reloaded upon saving because it isn’t part of the unit of work, so that reference will be held after the save and refresh.
Any combo boxes that are used as populate sources for Entity properties of the Entity need to have their EntitySet reloaded after saving as well; otherwise those related Entities populating the combo will hold references to the previous entity instance.
Google is proposing changing one entry at a time to the default values ....
http://code.google.com/appengine/articles/update_schema.html
I have a model with a million rows and doing this with a web browser will take me ages. Another option is to run this using task queues but this will cost me a lot of cpu time
any easy way to do this?
Because the datastore is schema-less, you do literally have to add or remove properties on each instance of the Model. Using Task Queues should use the exact same amount of CPU as doing it any other way, so go with that.
Before you go through all of that work, make sure that you really need to do it. As noted in the article that you link to, it is not the case that all entities of a particular model need to have the same set of properties. Why not change your Model class to check for the existence of new or removed properties and update the entity whenever you happen to be writing to it anyhow.
Instead of what the docs suggest, I would suggest to use low level GAE API to migrate.
The following code will migrate all the items of type DbMyModel:
new_attribute will be added if does not exits.
old_attribute will be deleted if exists.
changed_attribute will be converted from boolean to string (True to Priority 1, False to Priority 3)
Please note that query.Run returns iterator returning Entity objects. Entity objects behave simply like dicts:
from google.appengine.api.datastore import Query, Put
query = Query("DbMyModel")
for item in query.Run():
if not 'new_attribute' in item:
item['attribute'] = some_value
if 'old_attribute' in item:
del item['old_attribute']
if ['changed_attribute'] is True:
item['changed_attribute'] = 'Priority 1'
elif ['changed_attribute'] is False:
item['changed_attribute'] = 'Priority 3'
#and so on...
#Put the item to the db:
Put(item)
In case you need to select only some records, see the google.appengine.api.datastore module's source code for extensive documentation and examples how to create filtered query.
Using this approach it is simpler to remove/add properties and avoid issues when you have already updated your application model than in GAE's suggested approach.
For example, now-required fields might not exist (yet) causing errors while migrating. And deleting fields does not work for static properties.
This doesn't help OP but may help googlers with a tiny app: I did what Alex suggested, but simpler. Obviously this isn't appropriate for production apps.
deploy App Engine Console
write code right inside the web interpreter against your live datastore
like so:
from models import BlogPost
for item in BlogPost.all():
item.attr="defaultvalue"
item.put()