safrs
safrs copied to clipboard
Read-only Columns
I was wondering if it's possible to create columns which are read-only when exposed via the API.
For example, I have a data model with a last_completed
column which I want to restrict to being set or updated only via a call to a custom RPC call named complete
, exposed with @jsonapi_rpc
. It also has some computed values such as due_date
which are exposed with @jsonapi_attr
:
import datetime
from dateutil.relativedelta import relativedelta
from safrs import SAFRSBase, jsonapi_attr, jsonapi_rpc
from typing import Optional
from app import db
class Task(SAFRSBase, db.Model):
__tablename__ = 'tasks'
id = db.Column(db.Integer, primary_key=True)
last_completed = db.Column(db.Date, nullable=True)
name = db.Column(db.String)
@jsonapi_rpc(http_methods=["POST"])
def complete(self):
self.last_completed = datetime.datetime.now().date()
@jsonapi_attr
def due_date(self) -> Optional[datetime.datetime]:
if self.last_completed:
return self.last_completed + relativedelta(
**{self.frequency_unit.value: self.frequency_value}
)
else:
return None
To clarify, I would like last_completed
and due_date
to be available via GET
but not PATCH
or POST
.
Attempting to set such a restriction with last_completed.http_methods = ['GET']
as suggested for relationships here doesn't work.
Perhaps there is something similar to the hidden columns feature that I am not seeing?
Many thanks :)
Hi,
due_date
in your example should not be available via PATCH
or POST
. It does show up in the swagger but it should be omitted from serialization when these requests are processe.
The OAS/swagger should take this into account (i.e. not show the attribute unless a setter
is defined), so I'll fix that.
Anyway, it is possible to update/override the swagger manually using the custom_swagger
argument to SAFRSAPI
, example. So you can use this approach to remove the attributes from the OAS spec.
Indeed, you can also use hidden columns in combination with jsonapi_attr
to create read-only attributes because hidden columns are also not (de)serialized. Note, column names prefixed with an underscore are automatically hidden.
Hi @thomaxxl,
Thanks so much for the speedy reply. Apologies for not getting back to you sooner.
I have made the following modifications and can confirm that the properties in question are silently rejected (i.e. ignored) when making a POST
or PATCH
request (there is a slight change also from the previous code where I am computing due_date
only once rather than on every request, and have made this and the last_completed
properties read-only):
import datetime
from dateutil.relativedelta import relativedelta
from safrs import SAFRSBase, jsonapi_attr, jsonapi_rpc
from typing import Optional
from app import db
class Task(SAFRSBase, db.Model):
__tablename__ = 'tasks'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
_due_date = db.Column(db.Date, nullable=True)
_last_completed = db.Column(db.Date, nullable=True)
@jsonapi_attr
def due_date(self):
return self._due_date
@jsonapi_attr
def last_completed(self):
return self._last_completed
@jsonapi_rpc(http_methods=["POST"])
def complete(self):
self._last_completed = datetime.datetime.now().date()
self._due_date = self._last_completed + relativedelta(
**{self.frequency_unit.value: self.frequency_value}
)
I'm happy to consider this issue closed if you are :)