Model Resources¶
The design of the model resource configuration provides several areas to control the data provided via the request and the responses.
Class Variables¶
url_prefix = '/'
url_name = None
# for controlling presentation with output fields
serial_fields = None
serial_field_relations = None
# for modifying incoming data/queries with a function
process_get_inputs = None
process_post_inputs = None
process_put_inputs = None
process_patch_inputs = None
process_delete_inputs = None
# for modifying database objects before or after commits
before_commit = {}
after_commit = {}
Set Up Variables¶
Two variables are used for automatic URL generation with the function get_urls(). It is a convenience function. The url_prefix defaults to root, but can be set to whatever is needed. The url_name is the name of the resource and together they make up the URL.
It only comes in to play when adding resources.
url_prefix = ‘/’
url_name = None
If the url_name is left as None, a URL can be created from the model class name. For example, if a model class is CustomerOrder and its primary key is id, using a pluralizer, the default URLs will be:
/customer-orders
/customer-orders/<int:id>
Serialization¶
There are several avenues for controlling the output from model resources. Because the model classes use SQLAlchemy models wrapped with DBBase that have serialization / deserialization as a core process you can:
Configure the Model Class serialization: This method would mean that the primary usage of DBBase model classes would be the same within the Flask environment or without as well.
Configure the model resource class. This can be done by HTTP method or for all methods.
More detailed informations is available at: https://github.com/sidorof/DBBase
Model Resource Serial Fields¶
Serial fields are the names of columns and other variables and functions as found in the Model classes. Serial field relations are the serial fields for related models as configured in relationships. For example, the author might be related via the book model.
The default for serial_fields and serial_field_relations looks like the following.
class MyResource(ModelResource):
model_class = MyModel
serial_fields = None
serial_field_relations = None
The default serial fields and serial field relations use what is found from the model class.
Here is an example where both the serial fields and serial field relations are specified. Note that serial_field_relations is a dict with keys for each relationship to be specified.
class MyResource(ModelResource):
model_class = MyModel
serial_fields = ["id", "field1", "field2", "field3"]
serial_field_relations = {
"RelatedModel1": ["id", "field1", "field2", "field3"],
"RelatedModel2": ["id", "field1", "field2", "field3"],
}
Here is an example where the serial fields and relations vary by method. At first, it might seem implausible that this would be ever necessary, but consider a model that is not complete unless it has been processed. The GET variables would return serial fields and relations for that model, but the POST, PUT, and PATCH methods would trigger a job. In that case, the serial fields and relations can be related to that job. Or, the default values for the Job model might be sufficient and that method could set as None. In this example, you would be using an after_commit process to create the job.
class MyResource(ModelResource):
model_class = MyModel
serial_fields = {
"get": ["id", "field1", "field2", "field3"],
"post": ["uuid", "job_name", "started_at"],
"put": ["uuid", "job_name", "started_at"],
"patch": ["uuid", "job_name", "started_at"],
}
serial_field_relations = {
"get": {
"RelatedModel1": ["id", "field1", "field2", "field3"],
"RelatedModel2": ["id", "field1", "field2", "field3"],
}
}
Model Resource Modifications¶
Model resources can cover a fair amount of ground for a datacentric API, but when the vanilla version will not fit, there are remedies as a fall-back solution rather than simply writing your own.
Model resources are preset to take functions that can insert your necessary logic into the right point in the processing cycle.
There are three insertion points. The details change, depending upon the HTTP method, but there are more similarities than differences.
Process Input Functions: Modify the input data or query and continue processing or close it out early.
- Adjustments Before or After Commits
Before Commit: Just prior to a commit, either a function or class can be inserted. This gives the possibility of a rollback of the session or diversion to another process.
After Commit: After a commit, either a function or class can be inserted. This can then be a trigger process or complete substitution of another class for the return trip.
Process Input Functions¶
Process input functions take the form of a function that can process the input data for the HTTP methods.
The format template for these functions is process_{method}_input.
Since HTTP methods have different requirements for input data, the inputs vary according to the method. And, since these functions are inserted into an on-going process, the arguments and returns for the functions are specific. However, the returns follow a common format of (status, result). The status is a True / False indicating that you would like the method to continue with the possibly altered data. False indicates that the method should end. A failure must have a tuple of a text message and a response status code that will be made into a return result and returned to the front end.
Input Variables¶
kwargs: as passed into the method
data: the data gleaned from the request in a dictionary form: This is the data prior to deserializations, so the variable names would be in camel case still.
query: the SQLAlchemy query that can be modified: This is the Flask-SQLAlchemy version of query, equivalent to the SQLAlchemy’s session.query(Model), for example session.query(Book). So an additional filter as may be necessary would be done by
query = query.filter_by(my_var='test').
And finally, this is an unexecuted query that the normal program will execute afterwards.
Returns¶
Since part of the point of these functions is to determine whether to go forward or not, the returns must be in the form {“status” True, “query”: query, “data”: data} where status is either True to continue or {“status”: False, “message”: message, “status_code”: status_code} to exit early. So, if a dictionary is not returned an error will be triggered about the process_input_function itself.
Use the following formats as a guide.
For example, suppose you want to add a process input function for POST.
class MyResource(ModelResource):
model_class = MyClass
def process_post_input(self, data):
# your magic here
if status:
return {"status" True, "data": data}
return {
"status": False,
"message": message,
"status_code": status_code
}
Adjustments Before or After Commits¶
Being able to jump in prior to a commit or just after can be very helpful. Possible areas:
Triggering another process to run instead of saving, or run directly after saving.
A record could be marked as inactive rather than deleted.
A separate job could be created and sent to a queue, the job object returned in place of the original record.
A process can be run which diverts to an exit of the HTTP method with a message and status code.
The process inputs all had separate names and the input and return variables varied with the HTTP method, while this family of functions are more similar.
These functions must return a status of True to continue to output a data item after adjustments. If a status of False is used, the process will exit the HTTP method with a message and a status code.
Note
By diverting the process to return a message and status code, it is now essentially an RPC.
To make the interface a little cleaner a ModelResource before / after commit is organized as a dict. For example:
MachineLearningModelResource(ModelResource):
model_class = MachineLearningModel
after_commit = {
"post": submit_job
"put": submit_job
}
So your submit_job function would be called on POST or PUT, otherwise not.
The format of the before / after functions is similar to the following:
Method |
Args |
Returns a tuple |
|||
status, result, status_code |
|||||
before_commit after_commit |
self |
your_function |
data item |
status_code |
True, item, status_code |
False, message, status_code |