I have to create a program in Delphi using Access 2003 .mdb file as data repository.
The Access database has a table with a boolean (Yes/No in Access) field called "original".
I have mapped this field to a TDBCheckBox which shows checked for true and unchecked for false, and shows a half greyed check is the field has not been set.
What I want is oncreation of the field for the field to be set to false (checkbox unchecked) and save the field value as false IF the user has not explicitly set the field.
I have tried if (DVDQuery.FieldByName('Original').AsBoolean <> True) and (DVDQuery.FieldByName('Original').AsBoolean <> False )
then DVDQuery.FieldByName('Original').AsBoolean := False;
But this does not work for new records. I use a query to access the dataset as there a large number of dynamically created where statements to filter the dataset.
ANy help guidance is greatly appreciated.
Rob
Can you change the structure in the database? The proper place for default values is in the column definition. If you can update the structure, change the field to have a default value of "No". You will then never need to do any coding around this issue, and your data will be guaranteed correct even if entered directly through Access.
If you need to check the value in code, use if DVDQuery.FieldByName('Original').IsNull to determine whether the field is empty or not.
Finally, if you must change the value in code rather than as a database default, do it in the appropriate TDataset event (AfterInsert, AfterScroll, etc).
Check if the field is set a value or not in the BeforePost event of the DataSet:
procedure TForm1.DVDQueryBeforePost(DataSet: TDataSet);
begin
if DVDQuery.FieldByName('Original').IsNull then
DVDQuery.FieldByName('Original').AsBoolean := False;
if still relevant, check the status field of the field. It should be cbChecked, cbUnchecked or cbGray, which is what you are looking for.
Related
I am trying to insert a data to a field referenced by specific ID of row (or record) in the clientdataset at runtime.
I am using delphi and here's the structure of my case — mysql database > mysqluniprovider > uniquery > dataset provider > clientdataset > datasource > dbgrid.
The data I am trying to insert is generated during runtime by another code in the same procedure. Hence, dbnavigator will not work for me here. On the otherhand, I prefer to do this at the clientdataset level and do not want to direct to sql.
I was able to find the reference id by using the clientdataset.lookup/locate/findkey. But I could not be able to direct the cursor to the cell of the same row of reference id and specific field to insert the data.
I believed there must be a code component for this type of case just like cds.lookup/locate/findkey to update data in an existing table at runtime.
I will greatly appreciate any help on this.
The fact that you are using a TDBGrid to display the data is incidental, you change the data in the clientdataset and the TDBGrid will automatically update its display of the record. What you neeed to do is to use methods of of the clientdataset to move to the record and update its field data. You can use the clientdataset's Locate method to move to the record you want, as in:
ID := 99; // the ID of the record to change
if ClientDataSet1.Locate('ID', ID, []) then begin
ClientDataSet1.Edit; // Put the CDS into dsEdit mode so you can change its field data
ClientDataSet1.FieldByName('SomeField').AsString := 'Whatever';
ClientDataSet1.Post; // save the change(s) to the record
end;
See the online help for TField for its various AsXXX methods, such as AsInteger, AsFloat, etc.
Most of my application's forms use 2 or more db grids : basically, the user clicks a record in main grid and get child results in a secondary grid.
All my primary DBGrids FDQueries (that is the one with SELECT) have been set on the form but none are "active", they fire on FormShow.
I noticed that, wether I write :
FDQuery1.Active := True;
or
FDQuery1.Open;
the result is the same : my rows are displayed in the main DBGrid.
Accordingly, I call Close or Active := False on FormClose.
But there must be a difference between those approaches and this is the subject of my question : what is the difference between Query.Open and Query.Active := True; ?
What should I use if there is any significant difference ?
Thanks in advance
Math, getting less and less noob as you take the time to answer my questions :)
SIDE NOTE : my INSERT and UPDATE queries are set up with a CLEAR then, SQL.ADD, then Parameters declaration and finally an ExecSQL
Active is a property, Open is a method. When Active is set to true, it calls the Open method, when it is set to false, it calls Close. Active is useful as it can be used to check whether the dataset is actually open.
if Qry.Active then DoSomething;
SIDE NOTE : my INSERT and UPDATE queries are set up with a CLEAR then,
SQL.ADD, then Parameters declaration and finally an ExecSQL
Between Active and Open is no difference.(see whosrdaddy comment) They do the same thing - The dataset becomes active and returns the result from SELECT statement.
You can also use property Active to check if the dataset is active for example:
if not MyQuery.Active then
MyQuery.Open; // or MyQuery.Active := true;
ExecSQL execute queries that do not return a cursor to data (such as INSERT, UPDATE, DELETE, and CREATE TABLE).
When you have a select * from XXX query eventually you can get more fields than you expected, do you know if there is a way to check if new fields have been added since you created that query and defined its persistent fields ?.
Normally you can open your queries without having to worry if new fields have been added. If new fields are not within your persistent fields then you just won't see them. But on a Datasnap REST Server you get an AV when trying to return a Dataset from a query that now has more fields than when you created its persistent fields.
If would like to know if there is a quick check that I can do so I can return a more helpful error instead of an AV.
Something like :
MyQuery.Open;
if MyQuery.FieldDefs.Count <> MyQuery.Fields.Count then begin
raise Exception.Create('The number of returned Fields doesn''t match the expected');
end;
Thank you
If the dataset has persistent fields and you want those additional fields to be created when the query opens, you have to set dataset.FieldOptions.AutoCreateMode to acCombineAlways first.
Now after opening the query, you can check for the existance of additional fields with lcAutomatic in dataset.Fields.LifeCycles.
In case you are interested in which fields are new, just iterate dataset.Fields and check for field.LifeCycle = lcAutomatic.
BTW, using the setting above you probably might not need that check anymore.
In my cxGrid I have a Yes/No field which is by default 'NO' . Next to that field, I have another field,a LookupComboBox field that
gets its values from another table. It is empty by default however I would like that, when the value gets changed in this
particular field, my Yes/No field should change to 'YES' (Only in the row that I am currently editing) How am I to do this ? Also Not sure where to implement the code ....OnChange,Oneditvaluechanged,Onvalidate ???
Since your grid semms to be bound on datasets one easy way would be using the fieldchange event of your selection field.
For immediate behavior you should use a TcxEditRepositoryLookupComboBoxItem with ImmediatePost instead a of a Lookupfield in your dataset (which would anyway the worse approach with at least ADO)
procedure TForm4.MainSelectionChange(Sender: TField);
begin
if Main.State in [dsEdit,dsInsert] then
if not Sender.IsNull then
MainYesNo.Value := true;
{ maybe you are looking for that instead the code above
if Main.State in [dsEdit,dsInsert] then
MainYesNo.Value := not Sender.IsNull
}
end;
I am currently testing with:
A SQLConnection which is pointed towards an IB database.
A SQLDataset that has a SQLConnection field set to the one above.
A DatasetProvider that has the SQLDataset in (2) as its Dataset field value.
A ClientDataset, with the ProviderName field pointing to the provider in (3).
I use the following method (borrowed from Alister Christie) to get the data...
function TForm1.GetCurrEmployee(const IEmployeeID: integer): OleVariant;
const
SQLSELEMP = 'SELECT E.* FROM EMPLOYEE E WHERE E.EMPLOYEEID = %s';
begin
MainDM.SQLDataset1.CommandText := Format(SQLSELEMP, [Edit1.Text]);
Result := MainDM.DataSetProvider1.Data;
end;
Which populates the DBGrid with just one record. However, when I manually edit the record, click on Post, then try to commit the changes, using
MainDM.ClientDataset1.ApplyUpdates(0); // <<<<<<
It bombs, with the message "SQLDataset1: Cannot modify a read-only dataset."
I have checked the ReadOnly property of the Provider, and of the ClientDataset, and the SQL has no joins.
What could be causing the error?
It appears that your ClientDataSet.Data property is being populated from the Data property of the DataSetProvider. With the setup you described, you should be able to simply call ClientDataSet.Open, which will get the data from the DataSetProvider.
BTW, the default behavior of the DataSetProvider when you call the ClientDataSet.ApplyUpdates method is to send a SQL query to the connection object, and not the DataSet from which the data was obtained (assuming a homogeneous query). Make sure that your DataSetProvider.ResolveToDataSet property is not set to true.
Finally, on an unrelated note, your code above appears to be open to a SQL injection attack (though I have not tested this). It is safer to use a parameter to define the WHERE clause. If someone enters the following into Edit1 you might be in trouble (assuming the InterBase uses the drop table syntax): 1;drop table employee;
Check the LiveMode property of the TIBDataSet.