Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read-only Columns #113

Open
JM0804 opened this issue Apr 25, 2022 · 2 comments
Open

Read-only Columns #113

JM0804 opened this issue Apr 25, 2022 · 2 comments

Comments

@JM0804
Copy link

JM0804 commented Apr 25, 2022

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 :)

@thomaxxl
Copy link
Owner

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.

@JM0804
Copy link
Author

JM0804 commented Apr 26, 2022

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 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants