Deleting all entered data in a datastore in AppMaker - google-app-maker

I'm testing a project with two databases joined by a MANY-ONE relation (Devices -> Employees). Both datasources are Google Drive Tables. How can I delete all of the data in both of these tables, without deleting the tables themselves? I'd like to keep their metadata and relations intact but start with fresh data. Based on another answer, I've tried to run the following commands on button press:
var records = app.models.Device.newQuery().run();
app.deleteRecords(records);
var records = app.models.Employee.newQuery().run();
app.deleteRecords(records);
But I receive the error:
app.models.Device.newQuery is not a function
at AddDevice.Button1.onClick:1:33
And I wasn't sure where to go from there. Thanks in advance.

First of all, you should run this code on server side:
// server script
function deleteAll() {
var records = app.models.Employee.newQuery().run();
app.deleteRecords(records);
...
}
// Client script, for instance button's onClick event handler
google.script.run.deleteAll();
Then you can simplify your life, by specifying relation Owner. If you have it configured, then when you delete master record all related records will be deleted(cascade deletion). If you properly setup relations for all your models, then you'll need to delete records only from one model/table and all other records will be delete as well:
PS
As a quick and simple workaround, you can simply create a brand-new deployment(out of the box all models/tables will be empty).

Related

Clear filter query before running next script

The Setup
I have a project table displaying values from a Calculated SQL data model. The table has edit buttons for each row that when clicked open a page fragment for editing the relevant project.
Because the display table is showing values from a Calculated SQL model, the edit button runs this query to load the underlying project record from the Cloud SQL source model.
// onClick edit button event handler
var calcTableIds = widget.datasource.item.projectsTableId; // Calculated SQL model
var projectsTableIds = app.datasources.projects; // Cloud SQL model
projectsTableIds.query.filters.Id._equals = calcTableIds;
projectsTableIds.load();
app.showDialog(app.pageFragments.ProjectEdit);
The Problem
The issue I'm having is that after a project record has been edited, subsequent functions I attempt to run that reference the Cloud SQL model only display results for the last edited project record.
I believe I need to run a script upon closing the project edit window that will reverse the query filter but I haven't been able to figure out a solution on my own.
FYI, the script I'm attempting to run after editing a project is the AMU Export function which should export all records from my Cloud SQL model to a spreadsheet but instead only exports the last edited record.
As per the official documentation, you need to use the method clearFilters(). So when closing the fragment simply do:
app.datasources.THEDATASOURCE.query.clearFilters();
app.datasources.THEDATASOURCE.load();

How to programmatically link newly created records to a record from another table

Thanks in advance for your advice!
Background
I’m creating a database to track orders placed by customers.
An ‘Orders’ table stores general details about an order like the customer’s name, order date, and delivery-required date.
A separate ‘Order_Items’ table stores the specific items that the customer has ordered.
The is a one-to-many relationship between the ‘Orders’ table and ‘Order_Items’ table, i.e. one ‘Order’ can have many ‘Order_Items’, but each ‘Order_Item’ must be associated with only one ‘Order’.
Current State
Currently, I have a page where the user creates a new ‘Order’ record. The user is then taken to another page where they can create as many ‘Order_Item’ records as are needed for the order.
Desired State
What I would like to achieve is: When a user creates new ‘Order_Item’ records, it automatically allocates the current ‘Order’ record as the foreign key for the new ‘Order_Item’ record.
What I've Tried So Far
Manual Action By The User: One way of establishing the link between an 'Order' and all of its 'Order_Items' would be to add a drop-down widget which which effectively asks the user something like "Which order number do all of these items belong to"? The user's action would then establish the link between the two tables and associate one 'Order' with many 'Order_Items'. However, my goal is for this step to be handled programatically instead.
Official Documentation: I’ve referred to the offical documentation which was useful, but as I'm still learning I don’t really know exactly what to search for. The prefetch feature appeared promising but does not actually establish a link; it just loads associated records more efficiently.
App Maker Tutorials: I found an App Maker tutorial which creates an HR App where a user can create a list of ‘Departments’, then create a list of ‘Employees’, and then link an ‘Employee’ to a ‘Department’. However, in the example app this connection is established manually by the user. In my desired state I would like the link to be established programatically.
Manual Save Mode:
I’ve also tried switching to manual save mode so that the user has to create a draft ‘Orders’ record and then several draft ‘Order Items’ records and then save them all at once. However, I haven’t managed to make this work. I’m not sure whether the failure of this approach is because 1) I’m try to create draft records on more than one table, 2) I’m just not doing it correctly, or 3) I thought I read somewhere that draft records are deprecated.
Other Ideas
I'm very new to this field and am may be wrong, but I have a feeling I may need to use some scripting to establish the link. For example, maybe I could use a global variable to remember which 'Order' the user creates. Then, for each 'Order_Item' I could use the onBeforeCreate event to trigger a script that establishes the link between the 'Order_Item' and the 'Order' that was remembered from the previously established global variable.
Updated Question
Thanks Markus and Morfinismo for your answers. I have been using both answers with some success.
Morfinismo: I've successfully used the code you directed me to on existing records but cannot seem to get it to work for newly created records.
For example:
widget.datasource.createItem(); // This creates a new record
var managerRecord = app.datasources.Manager.item; // This sets the Manager of the currently selected parent record as a variable successfully.
var teamRecord = app.datasources.Teams.item; // This attempts to set the Manager of the currently selected record as a variable. However, the record that was created in line 1 is not selected. Therefore, App Maker does not seem to know which record this line of code relates to and returns the error Cannot set property ‘Manager’ of null.
// Assign the manager to the team.
teamRecord.Manager = managerRecord; // This successfully assigns the manager but only in cases where the previous line of code was successful (i.e. existing records and not newly created ones).
Do you have any suggestions or comments on how to apply this code to records that are created by the initial line of code in line 1?
I have found the easiest way to create related items for situations such as yours is to actually import a form with the datasource set to Parent: Child (relation) or Parent: Child (relation) (create). So in your case the datasource would need to be set to Order: Order_Items (relation).
You can get this accomplished in two different ways using the form widget wizard:
Option 1:
If your page datasource is set to Order_Items, drag your form on your page.
In the datasource selection section, your datasource in the form widget should default to `Inherited: Order_Items'. Click the 'Advanced' button in the bottom left corner, then from the datasources category find Order as your datasource, then select relations in the next field, and then Order_Items in the next field, choose 'Insert only' or 'Edit' form and then the appropriate fields you want in the form.
Now every item that gets created in that form will automatically be a child record of the currently selected record in your Order datasource.
Option 2:
If your page datasource is set to Order, drag your form on your page.
In the datasource selection section, your datasource in the form widget should default to Inherited: Order. Scroll down in your datasource selection section until you find Order: Order_Items (relation), then choose 'Insert only' or 'Edit' form and then the appropriate fields you want in the form.
Now every item that gets created in that form will automatically be a child record of the currently selected record in your Order datasource.
In your Order model, make sure that the security setting is set appropriately that a user is allowed to create relations of Order_Items in Order. That is the simplest approach in my opinion since you don't have to hard code the parent into your form or client/server scripts. It is automatically based on the currently selected parent, and is essentially doing the same thing that #Morfinismo explained in the client script section.
The comment I placed under your question included a link to the official documentation that explains what you need. Anyways, your question is not clear enough to determine whether you are creating the records via client script or server script, hence this is a very general answer.
To manage relations via client script:
var managerRecord = app.datasources.Manager.item;
var teamRecord = app.datasources.Teams.item;
// Assign the manager to the team.
teamRecord.Manager = managerRecord;
// Changes are saved automatically if the datasource in auto-save mode
// Add a team member to a Manager's team.
// Note: Retrieve Members on the client before proceeding, such as by using prefetch option in datasource - datasources Team -> Members)
var engineerRecord = app.datasources.TeamMember.item;
teamRecord.Members.push(engineerRecord);
To manage relations via server script:
// Get the record for the Team to modify.
var teamRecord = app.models.Teams.getRecord("team1");
// Assign a manager to the Team.
var managerRecord = app.models.EmployeeDB.getRecord("manager1");
teamRecord.Manager = managerRecord;
// Note: The new association is not saved yet
// Assign a team member to the Team.
var engineerRecord = app.models.EmployeeDB.getRecord("engineer1");
teamRecord.Members.push(engineerRecord);
// Save both changes to the database.
app.saveRecords([teamRecord]);
The above information is taken directly from the official documentation, which like I said, I referred to in the comment I placed under your question.

How to sort a table for which its datasource is a relation

I have a Page with a Table for which its datasource is a relation and needs to be sorted based on fields from another model:
Page
Datasource = Indicators
Table
Datasource = Indicators [one] : MetadataText [many] (relation)
The Table needs to be sorted based on a field from another Model called MetadataField, which has a one to many relation with MetadataText.
I have the datasource of MetadataField sorted. But the content in the Table appears in random order. When I first access the application, the Table is sorted by the order that the records were loaded. After view some records, the sorting of the records changes and keeps changing.
I am using Google Drive tables.
You can easily sort related records by one of the fields that belong to the related record itself, but only once (you'll received those records sorted from server).
But it seems, that you want to sort related records by their related record. App Maker will not be your friend in this case... but javascript will be! Since App Maker loads all related records you can safely sort them on client using javascript:
indicatorsDatasource.load(function() {
indicatorsDatasource.items.forEach(function(indicator) {
indicator.MetadataTexts.sort(function(a, b) {
return /* here goes your sorting logic */;
});
});
});
It will work in O(n * m * log(m)) in case you have n Indicators on the page and every indicator has m associated MetadataTexts. If you want to let users to sort related records by clicking table's header, you'll need to implement that logic on your own. So... all this hassle leads us to alternative solution! What if we decouple related records and introduce separated datasource for them? Having that you'll be able to use full power of App Maker's tables (sorting/paging) with almost no effort. You can take a look at implementation sample in Project Tracker template ViewProject page.

Preventing access to Dynamic Data tables programmatically

I have an admin section for many databases set up using Dynamic Data. When the user logs in, they are presented with a list of databases that they have access to modify (this list is kept in a separate database that is tied to the person's login information and also lists the tables they are allowed to see). Upon clicking on the database, a list of tables is then shown in a second gridview on the same default.aspx page for the Dynamic Data site.
I have found a way to limit the tables shown to the ones corresponding to the database entries of allowed tables, however, upon viewing the table entries, any foreign key associations to tables they do not have access to show up with the dynamic data hyperlinks that take you over to those tables directly.
Soooo, if you are still with me, thank you. My thought was to do a check in the global.asax file where the metamodels are bound and just not scaffold the tables that are supposed to be inaccessible. Can this be done programmatically? I would post some code but I'm not really sure what would be needed. Here is where I'm thinking I can make an edit:
foreach (var table in metaModel.Value.Tables)
{
var tablePermission = authorizedTables.Any(p => p == table.Name);
if (tablePermission) continue;
//Next lines are possible ways in?
var canThisBeSetSomehow = table.Attributes.OfType<ScaffoldTableAttribute>().FirstOrDefault();
table.Scaffold = false;
}
Assume that "authorizedTables" is a string[] that holds my table names in the above scenario. Is there a way to take the metamodel and edit it so that the remaining tables are inaccessible on a per user basis?

LINQ to SQL - updating records

Using asp.net 4 though C#.
In my data access layer I have methods for saving and updating records. Saving is easy enough but the updating is tedious.
I previously used SubSonic which was great as it had active record and knew that if I loaded a record, changed a few entries and then saved it again, it recognised it as an update and didn't try to save a new entry in the DB.
I don't know how to do the same thing in LINQ. As a result my workflow is like this:
Web page grabs 'Record A' from the DB
Some values in it are changed by the user.
'Record A' is passed back to the data access layer
I now need to load Record A again, calling it 'SavedRecord A', update all values in this object with the values from the passed 'Record A' and then update/ save 'SavedRecord A'!
If I just save 'Record A' I end up with a new entry in the DB.
Obviously it would be nicer to just pass Record A and do something like:
RecordA.Update();
I'm presuming there's something I'm missing here but I can't find a straightforward answer on-line.
You can accomplish what you want using the Attach method on the Table instance, and committing via the SubmitChanges() method on the DataContext.
This process may not be as straight-forward as we would like, but you can read David DeWinter's LINQ to SQL: Updating Entities for a more in depth explanation/tutorial.
let's say you have a product class OR DB, then you will have to do this.
DbContext _db = new DbContext();
var _product = ( from p in _db.Products
where p.Id == 1 // suppose you getting the first product
select p).First(); // this will fetch the first record.
_product.ProductName = "New Product";
_db.SaveChanges();
// this is for EF LINQ to Objects
_db.Entry(_product).State = EntityState.Modified;
_db.SaveChanges();
-------------------------------------------------------------------------------------
this is another example using Attach
-------------------------------------------------------------------------------------
public static void Update(IEnumerable<Sample> samples , DataClassesDataContext db)
{
db.Samples.AttachAll(samples);
db.Refresh(RefreshMode.KeepCurrentValues, samples)
db.SubmitChanges();
}
If you attach your entities to the context and then Refresh (with KeepCurrentValues selected), Linq to SQL will get those entities from the server, compare them, and mark updated those that are different
When LINQ-to-SQL updates a record in the database, it needs to know exactly what fields were changed in order to only update those. You basically have three options:
When the updated data is posted back to the web server, load the existing data from the database, assign all properties to the loaded object and call SubmitChanges(). Any properties that are assigned the existing value will not be updated.
Keep track of the unmodified state of the object and use Attach with both the unmodified and modified values.
Initialize a new object with all state required by the optimistic concurrency check (if enabled, which it is by default). Then attach the object and finally update any changed properties after the attach to make the DataContext change tracker be aware of those updated.
I usually use the first option as it is easiest. There is a performance penalty with two DB calls but unless you're doing lots of updates it won't matter.

Resources