|
|
|
Resource model declaration
A resource model is the single, definitive source of metadata about your data. It contains declarations about the essential fields and behaviors of the data in your application. Presently, each model maps to a single database table and the following list provides other basic information:
A resource model example
This example model defines a persons resource model that has first_name and birth_date fields:
// File: /app/models/persons.groovy
fields = [
first_name: [type: 'CharField'],
birth_date: [type: 'DateField'],
]
The first_name and birth_date fields are fields of the resource model. Each field is assigned metadata that modify its behavior at runtime (for example, validation rules) and field attribute maps to a database column.
The previous persons model would create a database table as shown in the following example:
CREATE TABLE persons (
first_name VARCHAR(30) NOT NULL,
birth_date DATE,
id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 100, INCREMENT BY 1) NOT NULL,
updated TIMESTAMP NOT NULL
)
Technical notes:
- The name of the table,
persons, is automatically derived from file name /app/models/persons.groovy but can be overridden. See the following Table customization section.
- An
id field is added automatically. Keys are generated by default and begin at 100, incrementing by 1.
- An
updated field is added automatically.
- The CREATE TABLE SQL in this example is formatted using Derby syntax. At present only the Derby database is supported.
Fields
The most important part of a resource model — and the only required part — is the list of fields it declares. Fields are specified by a data structure assigned to fields as shown in the previous example.
Field name restrictions
Three restrictions exist on resource model field names:
- A field name cannot be a Java, Groovy, or SQL reserved word.
- At present, a field name cannot contain more than one underscore in a row, due to the way the query lookup syntax works.
- Field names with upper case characters are also prohibited in M3.
The following example shows these restrictions:
// ILLEGAL!
// 'while' and 'where' are reserved words
// 'my__field' has two underscores
// 'mixedCase' has mixed case characters
fields = [
while: [type:'CharField'],
where: [type:'CharField'],
my__field: [type:'CharField'],
mixedCase: [type:'CharField']
]
Field types
Each field in your resource model must have a specified type string. This string maps to a field class. The resource model uses the field class types to determine the following things:
- The database column type (for example,
INTEGER, VARCHAR).
- The minimal validation requirements.
The supported datatypes for a resource declaration are:
BooleanField
A true/false field. Valid values for true are true, 't', 'true', 'True', '1', or 1 and valid values for false are false, 'f', 'false', 'False', '0', 0.
CharField
A string field, for small- to large-sized strings. For large amounts of text, use TextField.
| Argument | Required | Description |
| max_length | No | The maximum character length is by default 50. However, CharField can take max_length as extra argument and which specifies the maximum character length of the field. The max_length is enforced at the database level and in the Resource Model validation. |
In the following example, name values can have up to 50 characters and address up to 255.
fields = [
name: [type: 'CharField'],
address: [type: 'CharField', max_length:255],
]
DateField
A field representing a date (day, month, and year). Valid values take the form of yyyy-MM-dd.
DateTimeField
A field representing an instance or timestamp (second, minute, hour, day, month, and year). Valid values take the form of any of yyyy-MM-dd, yyyy-MM-dd HH:mm, or yyyy-MM-dd HH:mm:ss.
DecimalField
A decimal number represented in Java by a java.math.BigDecimal.
| Argument | Required | Description |
| max_digits | Yes | The maximum number of digits allowed in the number. |
| decimal_places | Yes | The number of decimal places to store with the number (the decimal number's scale) |
EmailField
A CharField that checks that the value is a valid e-mail address using a regular expression.
FloatField
A floating-point number represented in Java by a float.
IntegerField
An integer.
TextField
A large text field.
TimeField
A field representing a time independent of a date (second, minute, and hour). Valid values take the form of HH:mm and HH:mm:ss.
USStateField
A CharField that checks that the value is a two letter state abbreviation.
USPhoneField
A CharField that checks that the value is a valid phone number using a regular expression.
Using fields
A resource declaration using the types described in the previous sections looks like the following example:
// File: /app/models/employees.groovy
fields = [
name: [type: 'CharField', max_length:50],
birth_date: [type: 'DateField'],
age: [type: 'IntegerField'],
salary: [type: 'DecimalField', max_digits: 9, decimal_places: 2],
takes_lunch_at: [type: 'TimeField'],
favorite_moment: [type: 'DateTimeField'],
favorite_number: [type: 'FloatField'],
biography: [type: 'TextField'],
address :[type :'CharField', max_length: 60],
city: [type: 'CharField', max_length: 45],
state: [type: 'USStateField'],
phone: [type: 'USPhoneField'],
email: [type :'EmailField'],
]
Field options
The following optional arguments are available to all field types:
required
Default is false, which means all fields by default are not required to have a value. If true, the field is required. This constraint is enforced during:
-
create() and update() on a Collection
-
validate() on a Member instance
- POST and PUT on the HTTP resource
Note: Empty string-based values are stored as empty strings, not as null.
Example:
fields = [
first_name: [type: 'CharField', max_length: 25], // optional by default
last_name: [type: 'CharField', max_length: 35, required: true]
email: [type: 'EmailField', required: false] // permissible to explicitly declare false
]
Named collections
Resources are accessed using collection-like constructs. A filter can be applied to such collection to create a new collection that is a subset of the original. A filter condition can be applied either programmatically or using HTTP at runtime. Filters can also be specified in the model declaration by defining a named collection:
// File: /app/models/persons.groovy
fields = [
first_name: [type: 'CharField', max_length: 30],
birth_date: [type: 'DateField'],
is_child: [type: 'BooleanField'],
]
collections = [
children: [member_filters: [is_child: true]],
christmas_birthdays: [member_filters: [birth_date__day: '25', birth_date__month: '12']],
]
Database generation
ZRM generates a database table for each resource using a top down mapping scheme. By default, the name of the model file maps to the name of the database table and the field names map to column names. Each table also has a special field, id, that is the unique identifier for that resource.
The tables are created at the application startup. For each resource, ZRM checks to determine whether the corresponding table exists. If the table exists then ZRM leaves it intact, otherwise a new table is created. If you modify a model file and want the table be recreated, you must manually drop the table and restart the application.
Loading initial data fixtures
In addition to creating the database tables at application initialization, ZRM searches for initial_data.json files across all dependencies in /app/models/fixtures directories and loads its data into the database.
Note: The virtualized directory concepts are ignored in this case.
If the database has already been initialized, the data is not loaded until the database is deleted or the table is manually dropped. The JSON format of initial_data.json is specific but easy to reproduce.
Given the following resource model declaration, the subsequent initial_data.json file would load two persons instances into the backing database table:
fields = [
first_name:[type:'CharField'],
birth_date:[type:'DateField'],
is_child:[type: 'BooleanField'],
]
Valid initial_data.json:
[
{
"type": "persons",
"fields": {
"id" : "1",
"first_name" : "Tom",
"birth_date" : "2005-12-12",
"is_child" : "0",
"updated" : "2007-09-24 03:23:34.234"
}
},
{
"type": "persons",
"fields": {
"id" : "2",
"first_name" : "Bill",
"birth_date" : "2001-03-1",
"is_child" : "1",
"updated" : "2007-09-23 03:23:34.234"
}
}
]
Your initial_data.json file can contain populate multiple resource model types from the same file. Just ensure that the appropriate type value is included.
Table customization
You can customize the table name against which ZRM generates and maps your resource declaration. For example, if your application sets the resource type name to be articles but needs the database table name to be magazine_editorials, then you can specify the following in your resource model file:
// /app/models/articles.groovy
fields = [ ... ]
db_table = 'magazine_editorials'
|