I'm writing a database access app for storing some data and want to ask a few questions about the model/view architecture.
(Using: Qt 4.7.4, own build; PostgreSQL 9.0; Targets: WinXP, Win7 (32/64 bit))
Let me first explain what I am trying to achieve and where I am currently.
I have two pages (subclassed QWidgets inserted in a QStackedWidget) with a QTableView bound to a model. Each view is bound to a table in the PostgreSQL server. You can add/edit/delete/sort/filter items.
Each page can be seen by only one type of users, lets call the roles Role1 and Role2.
The submit strategies of everything connected to the model are OnManualSubmit.
(Transaction isolation level = Serializable.) When two users want to edit(for example) the same row, I want to do a "SELECT ... FOR UPDATE" query - to make sure that when someone edits something, he will merge his changes with newer ones (if any, just like in SVN for example). But I see only a submitAll() method the QSqlTableModel.
Maybe catching the signals beforeUpdate(), beforeDelete(), beforeInsert() and performing manually "SELECT ... FOR UPDATE" is one option.
The other way I think is to subclass QSqlTableModel. What is the clean and nice way to achieve this?
I want to periodically update the QSqlTableView for each of the pages (one page is seen at most, Role1 users have access only to Page1 and the same for Role2 => Page2).
The first thing that came to my mind is to use a QTimer and manually call select() of the QSqlTableModel, but... not sure if this is the cool way.
I also want to periodically check if the connection to the database is ok, but I think that a QTimer + QSqlDatabase::isOpen () will do.
Now, the 2 tables have the same primary keys and some columns are the same. I want when a user with Role1 changes a row in Table1 to automatically change corresponding columns of Table2 and vice versa. Should I create a trigger in Postgres?
BTW, the database is small - each of the two tables is around 3-4000 rows with ~10 columns (varchars mostly, 1 text and 2 date colunms).
Thanks for reading and Happy New Year! :)
I think you should consider doing something of the following:
Instead of using QSqlTableModel as a model I'd implement my own model as a subclass of QAbstractTableModel. This will allow you a lot of control over what you can do in terms of data manipulation.
One thing that this will require is for certain fields in the table you would need to implement subclass of QAbstractItemDelegate that will allow for modification of data in the table as I am fairly sure you don't want to allow users updating any field in the table as for example primary key is likely have to be left alone.
For question 2 I would suggest implementing a field called transaction_counter for every row so you don't have to select every row in the table just the updated ones the transaction_counter will be updated on every row update and the new one will be inserted on the new row insert. One thing that will be required is that the counter is unique across the table. For example if initial state of the table is: row1 has counter = 0 and row2 has counter = 0. If row1 is updated counter set to 1. When row1 is then updated again counter on it is set to 2. When row2 is now updated counter on it is set to 3, etc. You can certainly do the data refreshes now using QTimer and this will be much more advantageous to for example checking the data as one user may be updating the same table as another user with the same Role.
For Question 3. I don't see any reason why not custom models and especially if you decide to separate data from the model you can manipulate data separately from it's display. Sort of Data->Model->View->Controller implementation. Each one can be maintained separately as long as you have a feedback mechanism for your delegates.
For Question 4. The answer is sure or you can implement the trigger in your application.
Hope this helps. Have a great New Year!
Related
I'm trying to get a better understanding of what Rows and Rowsets are used for in PeopleCode? I've read through PeopleBooks and still don't feel like I have a good understanding. I'm looking to get more understanding of these as it pertains to Application Engine programs. Perhaps walking through an example may help. Here are some specific questions I have:
I understand that Rowsets, Row, Record, and Field are used to access component buffer data, but is this still the case for stand alone Application Engine programs run via Process Scheduler?
What would be the need or advantage to using these as opposed to using SQL objects/functions (CreateSQL, SQLExec, etc...)? I often see in AE programs where the CreateRowset object is instantiated and uses a .Fill method with a SQL WHERE Clause and I don't quite understand why a SQL was not used instead.
I've seen in PeopleBooks that a Row object in a component scroll is a row, how does a component scroll relate to the row? I've seen references to rows having different scroll levels, is this just a way of grouping and nesting related data?
After you have instantiated the CreateRowset object, what are typical uses of it in the program afterwards? How would you perform logic (If, Then, Else, etc..) on data retrieved by the rowset, or use it to update data?
I appreciate any insight you can share.
You can still use Rowsets, Rows, Records and fields in stand alone Application Engines. Application Engines do not have component buffer data as they are not running within the context of a component. Therefore to use these items you need to populate them using built-in methods like .fill() on a rowset, or .selectByKey() on a record.
The advantage of using rowsets over SQL is that it makes the CRUD easier. There are built-in methods for selecting, updating, inserting and deleting. Additionally you don't have to worry about making a large number of variables if there were multiple fields like you would with a SQL object. Another advantage is when you do the fill, the data is read into memory, where if you looped through the SQL, the SQL cursor would be open longer. The rowset, row, record and field objects also have a lot of other useful methods such as allowing you to executeEdits (validation) or copy from one rowset\row\record to another.
This question is a bit less clear to me but I'll try and explain. If you have a Page, it would have a level 0 row. It then could have multiple Level 1 rowsets. Under each of those it could have a level 2 rowsets.
Level0
/ \
Level1 Level1
/ \ / \
Level2 Level2 Level2 Level2
If one of your level1 rows had 3 rows, then you would find 3 rows in the Rowset associated with that level1. Not sure I explained this to answer what you need, please clarify if I can provide more info
Typically after I create a rowset, I would loop through it. Access the record on each row, do some processing with it. In the example below, I look through all locked accounts and prefix their description with LOCKED and then updated the database.
.
Local boolean &updateResult;
local integer &i;
local record &lockedAccount;
Local rowset &lockedAccounts;
&lockedAccounts = CreateRowset(RECORD.PSOPRDEFN);
&lockedAccounts.fill("WHERE acctlock = 1");
for &i = 1 to &lockedAccounts.ActiveRowCount
&lockedAccount = &lockedAccounts(&i).PSOPRDEFN;
if left(&lockedAccount.OPRDEFNDESCR.value,6) <> "LOCKED" then
&lockedAccount.OPRDEFNDESCR.value = "LOCKED " | &lockedAccount.OPRDEFNDESCR.value;
&updateResult = &lockedAccount.update();
if not &updateResult then
/* Error handle failed update */
end-if;
end-if;
End-for;
I have a One to Many relation : One row in modelParent with Many row in modelChild
In a PanelList with the all modelParent rows, I want to hide the delete Icone on the row if the row has children link to this row.
What test should I do ?
Right way
Don't hide the delete button, but show confirmation dialog that would tell user how many related records would be deleted with the selected record. You can find code samples in Project Tracker, People Skills and lots of other templates.
Easy to implement way (worse performance)
Configure prefetch for your ParentModel's datasource to include records from ChildModel
Bind delete button's visibe property to this:
#datasource.item.<NameOfYourRelationField>.length === 0
Harder to implement way (better performance)
You can create calculated model with special flag that you can use on UI to show/hide the delete button. This approach will also require lots of extra scripting for CRUD operations.
I have a Google App Maker project where I have a Many-To-Many Relation between 2 tables A and B.
I can build a page for table A where I have a table of related B records and add new ones using a dialog.
However, this approach enables me to create new B records and associate them to an A record but how can I make the user able to select an existing B record and associate it to an A record via UI?
I will need to have it work both ways (Select A records from B record page as well).
UPDATE:
I was to accomplish this by creating a separate dialog for selecting existing records which would show all available B records in a table widget with a SELECT button on each row. clicking the button will add the record to the B related records of the A record. However, I know there has to be an automatic way built by Google that will be less work and better design.
Thanks and I appreciate your help.
Your solution is a good one, another solution, depending on the number of existing records, is to use the multi-select widget. (You can bind the items to B's datasource, and values to the relation on a record in A. But as I mentioned, this only works well if the number of items in B is small.)
Making this easier is something we've been looking in to, the main challenge is the correct UI in this case depends a lot on the kind of app you're writing.
It took me a while to figure out the bindings for a multi-select but I think I am right in saying that they are
MultiSelect
Datasource: inherited: A
Values: #datasource.item.B
Option: #datasource.B.items
With this that Values are/is what you are updating in A(ie. A.B), but the Options you are updating it with are listed in B(ie. B.items).
Devin Taylor is right in his assertion that if you have a lot of records it may not work so well.
Im using Asp.net (VB.net), in my Database :
have One table called (Trade), the same rows of this table are used from 3 different users, These users can make different updates on this table, they should see the basic informations of the table (I mean by the Basic, before the table (trade) has been updated)
The problem is here when the first user wants to modify the table's rows, the second and third user cannot see the basic information any more, and if they decide to change or update some data, the first will lose his updated rows..
The data will be overwritten every time the users make updates on the table.
What I want, is to know if there is a way to do like a copy, or an image of the table for the 3 users, and every user can update normally, without creating the same Table with the same rows 3 times??!
Update
My table structure is: Trade(trName, Carrier, POl, POD, Vgp, Qgp) There is no primary key..
Thank you..
Solution to your problem could be two copies of the original table. Show the original table always to the user as the initial data. And in second table keep the updated data always. Now the trick comes here to maintain the log, for that you have to maintain the log table, this table will have all the fields of original table along with one additional column "UserId", this will have the ID of user who has changed the value. Now each time before updating the data, copy it in the log table. If this suits your need then post the fields of your table then we can workout on the table structures.
I am looking for the best way to store information that is entered within an Oracle ApEx app that is sectioned off like a wizard.
Basically there are a number of fields on the screen, i.e. text box, text area, select list, checkboxes as well as radiogroup buttons but are not attached to any particular database tables.
What I would like to do is basically have the user enter required answers say on one pane, which might have 5 items in total, i.e., 2 textfields, 1 radiogroup, 1 selectlist and 1 checkbox and when they press the "Next>" button before going to the next pane, store these answers into a ApEx Collection against a particular Id and perform the same process on the following pane of answers entered.
I am using Oracle ApEx 4.1.2. Basically want to store away values on the fly and reuse at a later stage.
As I mentioned in my last comment above, Plouf came through with the goods. I didn't require an ApEx Collection after all - regular items did the trick.