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

Creating model instance does not respect "viewonly" relationship attribute #188

Open
ktal90 opened this issue Nov 10, 2018 · 1 comment
Open

Comments

@ktal90
Copy link

ktal90 commented Nov 10, 2018

I have a semi-complex relationship using a secondary. This relationship is useful to me because when querying, but I do not want to write to it. SQLAlchemy has the viewonly option that I can set on the relationship to bypass setting that relationship when I insert a new record. Eve-SQLAlchemy, however, has issues when building the model instance for a resource that has this relationship associated with it. Eve-SQLAlchemy finds this relationship as a list, which is fine, and during insert time, building the model instance fails. This is unfortunate because evaluating this field when inserting is not necessary, since I've already set this as a "viewonly" field. Following is an example of my model. The relationship in question is "organization":

class CommonColumns(Base):
    __abstract__ = True
    _created = Column(DateTime, default=func.now())
    _updated = Column(DateTime, default=func.now(), onupdate=func.now())
    _etag = Column(String(40))

    @declared_attr
    def created_by_id(cls):
        return Column(Integer,
                      ForeignKey('users.id'),
                      default=get_current_user_id)

    @declared_attr
    def updated_by_id(cls):
        return Column(Integer,
                      ForeignKey('users.id'),
                      default=get_current_user_id,
                      onupdate=get_current_user_id)

    @declared_attr
    def created_by(cls):
        return relationship(
            'Users',
            foreign_keys='{}.created_by_id'.format(cls.__name__))

    @declared_attr
    def updated_by(cls):
        return relationship(
            'Users',
            foreign_keys='{}.updated_by_id'.format(cls.__name__))

    @declared_attr
    def organization(cls):
        return relationship(
            'Organizations',
            primaryjoin='{}.created_by_id==Users.id'.format(cls.__name__),
            secondary='join(Users, Organizations, Users.organization_id == Organizations.id)',  # noqa
            viewonly=True)

class Users(Base):
    __tablename__ = 'users'
    _created = Column(DateTime, default=func.now())
    _updated = Column(DateTime, default=func.now(), onupdate=func.now())
    id = Column(Integer, primary_key=True, autoincrement=True)
    organization_id = Column(Integer,
                             ForeignKey('organizations.id'),
                             nullable=False)
    organization = relationship('Organizations',
                                back_populates='users')

class Organizations(Base):
    __tablename__ = 'organizations'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(128))
    users = relationship('Users', back_populates='organization')

Is there a better way to set up this relationship and/or how can we properly handle building up the model instead of it failing to do so?

dkellner added a commit that referenced this issue Nov 12, 2018
@dkellner
Copy link
Collaborator

I've never used viewonly myself, thanks for bringing this up. I've just tried to reproduce your problems with this (your example is incomplete, so I had to do some guesswork, e.g. you never use CommonColumns and get_current_user_id is not defined).

I've come up with https://github.com/pyeve/eve-sqlalchemy/tree/viewonly/eve_sqlalchemy/examples/viewonly and tried to POST to /nodes, which works fine and GET-ing gives created_by and organization (see below). Can you maybe amend this example to show your problems with viewonly?

$ curl -i -X POST -H "Content-Type: application/json" -d '{}' localhost:5000/nodes
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 227
Location: http://localhost:5000/nodes/1
Server: Eve/0.7.10 Werkzeug/0.11.15 Python/3.6.6
Date: Mon, 12 Nov 2018 07:54:55 GMT

{"_updated": "Mon, 12 Nov 2018 07:54:55 GMT", "_created": "Mon, 12 Nov 2018 07:54:55 GMT", "_etag": "d4c403081de4d25f9005fa33ef10fcaa4f2b0063", "id": 1, "_links": {"self": {"title": "Node", "href": "nodes/1"}}, "_status": "OK"}

$ curl -i -X POST -H "Content-Type: application/json" -d '{}' localhost:5000/nodes
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 227
Location: http://localhost:5000/nodes/2
Server: Eve/0.7.10 Werkzeug/0.11.15 Python/3.6.6
Date: Mon, 12 Nov 2018 07:54:57 GMT

{"_updated": "Mon, 12 Nov 2018 07:54:57 GMT", "_created": "Mon, 12 Nov 2018 07:54:57 GMT", "_etag": "9901a9d0f565369c2e49774aa3b3e3b4764f5aaf", "id": 2, "_links": {"self": {"title": "Node", "href": "nodes/2"}}, "_status": "OK"}

$ curl localhost:5000/nodes | python -m json.tool
{
    "_items": [
        {
            "created_by": 1,
            "updated_by": 1,
            "organization": [
                1
            ],
            "id": 1,
            "_updated": "Mon, 12 Nov 2018 07:54:55 GMT",
            "_created": "Mon, 12 Nov 2018 07:54:55 GMT",
            "_etag": "d4c403081de4d25f9005fa33ef10fcaa4f2b0063",
            "_links": {
                "self": {
                    "title": "Node",
                    "href": "nodes/1"
                }
            }
        },
        {
            "created_by": 1,
            "updated_by": 1,
            "organization": [
                1
            ],
            "id": 2,
            "_updated": "Mon, 12 Nov 2018 07:54:57 GMT",
            "_created": "Mon, 12 Nov 2018 07:54:57 GMT",
            "_etag": "9901a9d0f565369c2e49774aa3b3e3b4764f5aaf",
            "_links": {
                "self": {
                    "title": "Node",
                    "href": "nodes/2"
                }
            }
        }
    ],
    "_links": {
        "parent": {
            "title": "home",
            "href": "/"
        },
        "self": {
            "title": "nodes",
            "href": "nodes"
        }
    },
    "_meta": {
        "page": 1,
        "max_results": 25,
        "total": 2
    }
}

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