Skip to content

Question — Is this the right way for adding a field that depend on a relationship? #300

@cglacet

Description

@cglacet

I have a naive question but I don't know the words to search for. I'm sure this has been asked one million times but I'm clueless.

I have some blog posts with likes on them and I would like to query a list of blog posts with a flag on each post to tell wether the given user liked the post or not.

My current solution:

  • I added a field in my schema (under schema.Blog.has_user_liked)
  • I added lazy="dynamic" on models.Blog.likes so I can filter likes for a particular user

I wonder if this is the right approach as it makes "a lot" of disjoint SQL requests (at least one to get the blog posts and one per blog post). It sounds like a lot to me, but as I understand there is a compromise here because this will allow to only retrieve some blog posts (not ALL) and still have decent performances since it will not join very large tables. Note that I never plan on using this has_user_liked field anywhere else but in a web view with pagination.

Is this the "right" solution? Is there a better way to achieve this? I would really appreciate any comment/critic about this.

Details about this solution

My model looks like this:

class User(database.Base):
    user_id = IDColumn()

class Blog(database.Base):
    blog_id = IDColumn()
    likes = relationship("Like", back_populates="blog", lazy="dynamic")

class Like(database.Base):
    blog_id = IDColumn(ForeignKey(Blog.blog_id))
    user_id = IDColumn(ForeignKey(User.user_id))
    blog = relationship("Blog", back_populates="likes")

My schema looks like this:

class User(SQLAlchemyObjectType):
    class Meta:
        model = models.User
        interfaces = (Node,)

class Blog(SQLAlchemyObjectType):
    class Meta:
        model = models.Blog
        interfaces = (Node,)

    has_user_liked = graphene.Field(
        graphene.Boolean, user_id=graphene.String(required=True)
    )

    def resolve_has_user_liked(self: models.Blog, info, user_id):
        return self.likes.filter(user_id == user_id).count()

class Like(SQLAlchemyObjectType):
    class Meta:
        model = models.Like
        interfaces = (Node,)


class Query(graphene.ObjectType):
    node = Node.Field()
    blogs = SQLAlchemyConnectionField(Blog)

If I make a request like this one:

query Test($userId: String!) {
    blogs{
        edges {
            node {
                id
                hasUserLiked(userId: $userId)
            }
        }
    }
}

I get the expected result (in my test DB I have 3 posts, one of which has one like from the given user):

{
    "data": {
        "blogs": {
            "edges": [
                {
                    "node": {
                        "id": "Blog:fdb231cd-2a19-4d1f-9443-5717f44275af",
                        "hasUserLiked": false
                    }
                },
                {
                    "node": {
                        "id": "Blog:375afe8e-a52a-4098-98b7-f04a4b504097",
                        "hasUserLiked": false
                    }
                },
                {
                    "node": {
                        "id": "Blog:01580847-bb33-40ef-93e4-8025a1e31145",
                        "hasUserLiked": true
                    }
                }
            ]
        }
    }
}

The "problem" or rather my question concerns the generated SQL queries, from what I can see in SQLAlchemy's log, here is what the requests are:

SELECT count(*) AS count_1 
FROM (SELECT "user".blog.blog_id AS user_blog_blog_id, "user".blog.author AS user_blog_author, "user".blog.date AS user_blog_date, "user".blog.title AS user_blog_title, "user".blog.content AS user_blog_content, "user".blog.image AS user_blog_image, "user".blog.avatar AS user_blog_avatar 
FROM "user".blog) AS anon_1

SELECT count(*) AS count_1 
FROM (SELECT "user".blog.blog_id AS user_blog_blog_id, "user".blog.author AS user_blog_author, "user".blog.date AS user_blog_date, "user".blog.title AS user_blog_title, "user".blog.content AS user_blog_content, "user".blog.image AS user_blog_image, "user".blog.avatar AS user_blog_avatar 
FROM "user".blog) AS anon_1

SELECT "user".blog.blog_id AS user_blog_blog_id, "user".blog.author AS user_blog_author, "user".blog.date AS user_blog_date, "user".blog.title AS user_blog_title, "user".blog.content AS user_blog_content, "user".blog.image AS user_blog_image, "user".blog.avatar AS user_blog_avatar 
FROM "user".blog 
 LIMIT %(param_1)s

SELECT "user".blog.blog_id AS user_blog_blog_id, "user".blog.author AS user_blog_author, "user".blog.date AS user_blog_date, "user".blog.title AS user_blog_title, "user".blog.content AS user_blog_content, "user".blog.image AS user_blog_image, "user".blog.avatar AS user_blog_avatar 
FROM "user".blog 
 LIMIT %(param_1)s

-- User's likes

SELECT count(*) AS count_1 
FROM (SELECT "user"."like".like_id AS user_like_like_id, "user"."like".blog_id AS user_like_blog_id, "user"."like".user_id AS user_like_user_id 
FROM "user"."like" 
WHERE %(param_1)s = "user"."like".blog_id) AS anon_1

SELECT count(*) AS count_1 
FROM (SELECT "user"."like".like_id AS user_like_like_id, "user"."like".blog_id AS user_like_blog_id, "user"."like".user_id AS user_like_user_id 
FROM "user"."like" 
WHERE %(param_1)s = "user"."like".blog_id) AS anon_1

-- {'param_1': UUID('fdb231cd-2a19-4d1f-9443-5717f44275af')}

SELECT count(*) AS count_1 
FROM (SELECT "user"."like".like_id AS user_like_like_id, "user"."like".blog_id AS user_like_blog_id, "user"."like".user_id AS user_like_user_id 
FROM "user"."like" 
WHERE %(param_1)s = "user"."like".blog_id) AS anon_1

SELECT count(*) AS count_1 
FROM (SELECT "user"."like".like_id AS user_like_like_id, "user"."like".blog_id AS user_like_blog_id, "user"."like".user_id AS user_like_user_id 
FROM "user"."like" 
WHERE %(param_1)s = "user"."like".blog_id) AS anon_1

-- {'param_1': UUID('375afe8e-a52a-4098-98b7-f04a4b504097')}

SELECT count(*) AS count_1 
FROM (SELECT "user"."like".like_id AS user_like_like_id, "user"."like".blog_id AS user_like_blog_id, "user"."like".user_id AS user_like_user_id 
FROM "user"."like" 
WHERE %(param_1)s = "user"."like".blog_id) AS anon_1

SELECT count(*) AS count_1 
FROM (SELECT "user"."like".like_id AS user_like_like_id, "user"."like".blog_id AS user_like_blog_id, "user"."like".user_id AS user_like_user_id 
FROM "user"."like" 
WHERE %(param_1)s = "user"."like".blog_id) AS anon_1

-- {'param_1': UUID('01580847-bb33-40ef-93e4-8025a1e31145')}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions