I am asking this because in ExtJS in Action book, Page 442, the author said:
"at the time of this writing,
DataWriterhas some undesirable
behaviors such as creating a Record
for a Store even if the create action
failed in the backend"
Is this still the case with ExtJS 3.3.1? If not, should DataWriter be used instead of our own CRUD code?
That is an interesting quote. The actions happen in sort of the reverse order.
When a new record is created in the store (a phantom record) and the save method is called on the store, the DataWriter will send information for any phantom records to the create action so that they may be created on the back end.
If the response from the server dictates that there was a failure and the correct record data is not sent back, I do not believe the record will remain in the store.
I believe the DataWriter can be valuable to performing CRUD operations on a store.
Related
I open up a Member form for Add/Editing members. It has its own entity manager and when the Save button is clicked I close the form and go back to a list form.
When the save is processed, I call a routine called CalculateOwing which calculates the members balance. This method is in a separate .cs file cause it can be called from many areas in the application.
Should the CalculateOwing method be in a separate entity manager or in the same entity manager as the member record being processed?
A response to this question can be seen at http://www.ideablade.com/forum/forum_posts.asp?TID=4686&title=need-advice-on-entity-manager
EDIT:
Including the response here.
"If you want the results of CalculateOwing to be part of the same database transaction, you need to call it before the Save completes and on the same EntityManager that the Save is using."
I want to listen change in my legacy system whenever there is any change in SF object (add/update/delete). So I have created outbound message and workflow. But in workflow I don't see any way to fire if object is deleted.
Is there anyway I can trigger outbound message on record delete? I know have heard that it can be done by trigger. But I don't want to write apex code for this.
To the best of my knowledge it cannot be done, the workflow actions are decoupled from the workflow rule (you can even reuse them) so they probably do not receive the transaction scope and when they execute the record is already gone and any reference inside action would point to a non-existing data. Thus the only way I know how to do it is via trigger.
Here is a workaround. However this will only be able to capture deletion made via std. Salesforce UI.
1.Create a custom checkbox field "Is Deleted"
2.Override the Del link with a custom VF page, that first updates the record status to "Is Deleted", and deletes the record.
3.Write workflow rule using the "Is Deleted" field.
Perhaps a compromise architecture would be to write an extremely small and simple after delete trigger that simply copies the deleted records in question to some new custom object. That new custom object fires your workflow rule and thus sends the outbound message you're looking for. The only issue with this would be to periodically clean up your custom object data that would grow in size as you deleted records from your other object. In other words, your "scratch" object would just need periodic cleaning - which could be done on a nightly schedule with batch Apex.
Here's a delete trigger that would do the trick using Opportunity as an example:
trigger AfterDelete on Opportunity (after delete)
{
List<CustObj__c> co = new List<CustObj__c>();
for(Opportunity o : Trigger.old)
{
CustObj__c c = new CustObj__c();
c.Name = o.Name;
c.Amount__c = o.Amount;
c.CloseDate__c = o.CloseDate;
c.Description__c = o.Description;
// etc.
co.add(c);
}
insert co;
}
It's not ideal but at least this would save you from having to code your own trigger-based outbound messages. These can only be done using the #Future annotation, btw, since callouts directly from triggers are forbidden. Hope that helps.
write a single email send in the trigger delete event. You have it in less than 1 hour.
I have a simple TClientDataSet component that I use to populate some data-aware components. However, if I insert data into my database using this dataset, I can't seem to find a proper way to sync it back into my TClientDataSet component.
How do I achieve this?
The TClientDataSet component has a Refresh method that does exactly that. Took some time for me to find this out in the docs, though. :)
From the docs:
From DB.pas
procedure Refresh;
Re-fetches data from the database to update a dataset's view of data.
Call Refresh to ensure that an application has the latest data from a database. For example, when an application turns off filtering for a dataset, it should immediately call Refresh to display all records in the dataset, not just those that used to meet the filter condition.
Note: The Refresh method does not work for all TDataSet descendants. In particular, TQuery components do not support the Refresh method if the query is not "live". To refresh a static TQuery, close and reopen the dataset.
TDataSet generates a BeforeRefresh event before refreshing the records and an AfterRefresh event afterwards.
Note: Most datasets try to maintain the current record position when you call refresh. However, this is not always possible. For example, the current record may have been deleted from the server by another user. Unidirectional datasets have no mechanism for locating the current record after a refresh, and always move back to the first record.
Warning: Unidirectional datasets refresh the data by closing and reopening the cursor. This can have unintended side effects if, for example, you have code in the BeforeClose, AfterClose, BeforeOpen, or AfterOpen event handlers.
Closing and opening the CDS didn't work for me. I'm using Delphi XE2, Update 3, with ADO tables connecting to an Access 2007 database. The only way I could refresh the data in my CDS was this:
procedure TForm1.PeopleRefreshButtonClick(Sender: TObject);
// this actually re-loads the data from the table!
begin
DM1.PeopleTable.Close;
DM1.PeopleTable.Open;
DM1.PeopleCDS.Refresh;
end;
A refresh button on the form closes, then opens the table. Then I refresh the ClientDataSet.
Basically, you must close the TClientDataset and then open it, loading the data from the database the same way you did originally. If the TClientDataset is connected to a TDataSetProvider, which is connected to a TDataset/TQuery descendant, all you have to do is close TClientDataset then open it.
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 putting together a small web app that writes to a database (Perl CGI & MySQL). The CGI script takes some info from a form and writes it to a database. I notice, however, that if I hit 'Reload' or 'Back' on the web browser, it'll write the data to the database again. I don't want this.
What is the best way to protect against the data being re-written in this case?
Do not use GET requests to make modifications! Be RESTful; use POST (or PUT) instead the browser should warn the user not to reload the request. Redirecting (using HTTP redirection) to a receipt page using a normal GET request after a POST/PUT request will make it possible to refresh the page without getting warned about resubmitting.
EDIT:
I assume the user is logged in somehow, and therefore you allready have some way of tracking the user, e.g. session or similar.
You could make a timestamp (or a random hash etc..) when displaying the form storing it both as a hidden field (just besides the anti Cross-Site Request token I'm sure you allready have there), and in a session variable (wich is stored safely on your server), when you recieve a the POST/PUT request for this form, you check that the timestamp is the same as the one in session. If it is, you set the timestamp in the session to something variable and hard to guess (timestamp concatenated with some secret string for instance) then you can save the form data. If someone repeats the request now you won't find the same value in the session variable and deny the request.
The problem with doing this is that the form is invalid if the user clicks back to change something, and it might be a bit to harsh, unless it's money you're updating. So if you have problems with "stupid" users who refresh and click the back-button thus accidentally reposting something, just using POST would remind them not to do that, and redirecting will make it less likely. If you have a problem with malicious users, you should use a timestampt too allthough it will confuse users sometimes, allthough if users is deliberately posting the same message over and over you probably need to find a way to ban them. Using POST, having a timestam, and even doing a full comparison of the whole database to check for duplicate posts, won't help at all if the malicious users just write a script to load the form and submit random garbage, automatically. (But cross-site-request protection makes that a lot harder)
Using a POST request will cause the browser to try to prevent the user from submitting the same request again, but I'd recommend using session-based transaction tracking of some kind so that if the user ignores the warnings from the browser and resubmits his query your application will prevent duplication of changes to the database. You could include a hidden input in the submission form with value set to a crypto hash and record that hash if the request is submitted and processed without error.
I find it handy to track the number of form submissions the user has performed in their session. Then when rendering the form I create a hidden field that contains that number. If the user then resubmits the form by pressing the back button it'll submit the old # and the server can tell that the user has already submitted the form by examining what's in the session to what the form is saying.
Just my 2 cents.
If you aren't already using some sort of session-management (which would let you note and track form submissions), a simple solution would be to include some sort of unique identifier in the form (as a hidden element) that is either part of the main DB transaction itself, or tracked in a separate DB table. Then, when you are submitted a form you check the unique ID to see if it has already been processed. And each time the form itself is rendered, you just have to make sure you have a unique ID.
First of all, you can't trust the browser, so any talk about using POST rather than GET is mostly nerd flim-flam. Yes, the client might get a warning along the lines of "Did you mean to resubmit this data again?", but they're quite possibly going to say "Yes, now leave me alone, stupid computer".
And rightly so: if you don't want duplicate submissions, then it's your problem to solve, not the user's.
You presumably have some idea what it means to be a duplicate submission. Maybe it's the same IP within a few seconds, maybe it's the same title of a blog post or a URL that has been submitted recently. Maybe it's a combination of values - e.g. IP address, email address and subject heading of a contact form submission. Either way, if you've manually spotted some duplicates in your data, you should be able to find a way of programmatically identifying a duplicate at the time of submission, and either flagging it for manual approval (if you're not certain), or just telling the submitter "Have you double-clicked?" (If the information isn't amazingly confidential, you could present the existing record you have for them and say "Is this what you meant to send us? If so, you've already done it - hooray")
I'd not rely on POST warnings from the browser. Users just click OK to make messages go away.
Anytime you'll have a request that needs to be one time only e.g 'make a payment', send a unique token down, that gets submitted back with the request. Throw the token out after it comes back, and so you can now tell when something is a valid submission (anything with a token that isn't 'active'). Expire active tokens after X amount of time, e.g. when a user session ends.
(alternately track the tokens that have come back, and if you have received it before then it is invalid.)
Do a POST every time you alter data, but never return an HTML response from a post... instead return a redirect to a GET that retrieves the updated data as a confirmation page. That way, there is no worry about them refreshing the page. If they refresh, all that will happen is another retrieve, never a data-altering action.