question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

`select_related` doesn't work: raises AttributeError

See original GitHub issue

Describe the bug If using select_related, it raises AttributeError

Seems like the error is related to one-to-many relation

To Reproduce short:

# query:
t: Tournament = await Tournament.filter(name=tournament_name).select_related("events").first()

# exception
  File "/opt/project/api/stuff.py", line 51, in run
    t: Tournament = await Tournament.filter(name=tournament_name).select_related("events").first()
    │                     │          │           └ 'tournament'
    │                     │          └ <classmethod object at 0x7f95e62e5df0>
    │                     └ <class '__main__.Tournament'>
    └ <Tournament: 1>

  File "/usr/local/lib/python3.8/site-packages/tortoise/queryset.py", line 797, in _execute
    instance_list = await self._db.executor_class(
                          │    └ <member '_db' of 'QuerySet' objects>
                          └ <tortoise.queryset.QuerySet object at 0x7f95e5349c70>
  File "/usr/local/lib/python3.8/site-packages/tortoise/backends/base/executor.py", line 149, in execute_select
    setattr(ins, model_name, obj)
            │    │           └ <Event: 1>
            │    └ 'events'
            └ <Tournament: 1>

AttributeError: can't set attribute

Expected behavior Don’t raise, just load selected relations

Additional context

Full example:

from tortoise import Tortoise, fields, run_async
from tortoise.models import Model


class Tournament(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()

    events: fields.ReverseRelation["Event"]

    def __str__(self):
        return self.name


class Event(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()
    tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField(
        "models.Tournament",
        related_name="events",
    )

    def __str__(self):
        return self.name


async def run():
    await Tortoise.init(db_url="sqlite://:memory:", modules={"models": ["__main__"]})
    await Tortoise.generate_schemas()

    tournament_name = "tournament"
    tournament = await Tournament.create(name=tournament_name)
    await Event.create(name="First", tournament=tournament)
    await Event.create(name="Second", tournament=tournament)

    t: Tournament = await Tournament.filter(name=tournament_name).first()
    print(t, t.events)
    # tournament <tortoise.fields.relational.ReverseRelation object at 0x7f7e4b693e50>

    t: Tournament = await Tournament.filter(name=tournament_name).prefetch_related("events").first()
    print(t, t.events, list(t.events))
    # tournament <tortoise.fields.relational.ReverseRelation object at 0x7f7e4b6b3760> [<Event: 1>, <Event: 2>]

    t: Tournament = await Tournament.filter(name=tournament_name).select_related("events").first()
    print(t, t.events, list(t.events))
    # Traceback (most recent call last):
    #   File "/opt/project/api/stuff.py", line 47, in <module>
    #     run_async(run())
    #   File "/usr/local/lib/python3.8/site-packages/tortoise/__init__.py", line 636, in run_async
    #     loop.run_until_complete(coro)
    #   File "/usr/local/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    #     return future.result()
    #   File "/opt/project/api/stuff.py", line 42, in run
    #     t: Tournament = await Tournament.filter(name=tournament_name).select_related("events").first()
    #   File "/usr/local/lib/python3.8/site-packages/tortoise/queryset.py", line 797, in _execute
    #     instance_list = await self._db.executor_class(
    #   File "/usr/local/lib/python3.8/site-packages/tortoise/backends/base/executor.py", line 149, in execute_select
    #     setattr(ins, model_name, obj)
    # AttributeError: can't set attribute

if __name__ == "__main__":
    run_async(run())

relates to #500 and #501

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:2
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
mahenzoncommented, Oct 15, 2020

@DrJackilD I’m sorry, I don’t understand your point. What’s wrong with one-to-many relationship?

There’re two ways to do it: 1 - in two queries. at first query main objects, then collect their ids and then fetch related objects 2 - in one query, using join.

anyways in both cases we’ll have to create python objects and connect them together

Creating tables and a relation with foreign key:

CREATE TABLE authors (
	author_id serial PRIMARY KEY,
	username VARCHAR ( 50 ) UNIQUE NOT NULL
);

CREATE TABLE articles (
	article_id serial PRIMARY KEY,
	title VARCHAR ( 50 ) NOT NULL,
	body text NOT NULL,
	author_id INT NOT NULL,
    CONSTRAINT fk_author
      FOREIGN KEY(author_id)
	  REFERENCES authors(author_id)
);

INSERT INTO authors (username) VALUES ('john');
INSERT INTO authors (username) VALUES ('james');
INSERT INTO authors (username) VALUES ('sam');
INSERT INTO articles (title, body, author_id) VALUES ('python lesson', 'text body', 1);
INSERT INTO articles (title, body, author_id) VALUES ('tortoise lesson', 'another body', 1);
INSERT INTO articles (title, body, author_id) VALUES ('django lesson', 'some body text', 2);

Queries:

-- selecting author by article.author_id ( =1)
SELECT author_id, username
FROM authors
WHERE author_id = 1;
author_id username
1 john
-- selecting all authored posts by author.author_id
SELECT article_id, title, body, author_id
FROM articles
WHERE articles.author_id = 1;
article_id title body author_id
1 python lesson text body 1
2 tortoise lesson another body 1
SELECT au.author_id, au.username, ar.article_id, ar.title, ar.body
FROM authors au
LEFT OUTER JOIN articles ar on au.author_id = ar.author_id
WHERE username ILIKE 'j%';
author_id username article_id title body
1 john 1 python lesson text body
1 john 2 tortoise lesson another body
2 james 3 django lesson some body text
-- selecting one selected author's articles
SELECT au.author_id, au.username, ar.article_id, ar.title, ar.body
FROM authors au
LEFT OUTER JOIN articles ar on au.author_id = ar.author_id
WHERE username = 'james';
author_id username article_id title body
2 james 3 django lesson some body text
-- selecting some selected authors articles
SELECT au.author_id, au.username, ar.article_id, ar.title, ar.body
FROM authors au
LEFT OUTER JOIN articles ar on au.author_id = ar.author_id
WHERE username ILIKE '%a%';
author_id username article_id title body
2 james 3 django lesson some body text
3 sam NULL NULL NULL
-- all authors with their articles
SELECT au.author_id, au.username, ar.article_id, ar.title, ar.body
FROM authors au
LEFT OUTER JOIN articles ar on au.author_id = ar.author_id;
author_id username article_id title body
1 john 1 python lesson text body
1 john 2 tortoise lesson another body
2 james 3 django lesson some body text
3 sam NULL NULL NULL
1reaction
Rebbit13commented, Jul 30, 2022

Describe the bug If using select_related, it raises AttributeError

Seems like the error is related to one-to-many relation

To Reproduce short:

# query:
t: Tournament = await Tournament.filter(name=tournament_name).select_related("events").first()

# exception
  File "/opt/project/api/stuff.py", line 51, in run
    t: Tournament = await Tournament.filter(name=tournament_name).select_related("events").first()
    │                     │          │           └ 'tournament'
    │                     │          └ <classmethod object at 0x7f95e62e5df0>
    │                     └ <class '__main__.Tournament'>
    └ <Tournament: 1>

  File "/usr/local/lib/python3.8/site-packages/tortoise/queryset.py", line 797, in _execute
    instance_list = await self._db.executor_class(
                          │    └ <member '_db' of 'QuerySet' objects>
                          └ <tortoise.queryset.QuerySet object at 0x7f95e5349c70>
  File "/usr/local/lib/python3.8/site-packages/tortoise/backends/base/executor.py", line 149, in execute_select
    setattr(ins, model_name, obj)
            │    │           └ <Event: 1>
            │    └ 'events'
            └ <Tournament: 1>

AttributeError: can't set attribute

Expected behavior Don’t raise, just load selected relations

Additional context

Full example:

from tortoise import Tortoise, fields, run_async
from tortoise.models import Model


class Tournament(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()

    events: fields.ReverseRelation["Event"]

    def __str__(self):
        return self.name


class Event(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()
    tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField(
        "models.Tournament",
        related_name="events",
    )

    def __str__(self):
        return self.name


async def run():
    await Tortoise.init(db_url="sqlite://:memory:", modules={"models": ["__main__"]})
    await Tortoise.generate_schemas()

    tournament_name = "tournament"
    tournament = await Tournament.create(name=tournament_name)
    await Event.create(name="First", tournament=tournament)
    await Event.create(name="Second", tournament=tournament)

    t: Tournament = await Tournament.filter(name=tournament_name).first()
    print(t, t.events)
    # tournament <tortoise.fields.relational.ReverseRelation object at 0x7f7e4b693e50>

    t: Tournament = await Tournament.filter(name=tournament_name).prefetch_related("events").first()
    print(t, t.events, list(t.events))
    # tournament <tortoise.fields.relational.ReverseRelation object at 0x7f7e4b6b3760> [<Event: 1>, <Event: 2>]

    t: Tournament = await Tournament.filter(name=tournament_name).select_related("events").first()
    print(t, t.events, list(t.events))
    # Traceback (most recent call last):
    #   File "/opt/project/api/stuff.py", line 47, in <module>
    #     run_async(run())
    #   File "/usr/local/lib/python3.8/site-packages/tortoise/__init__.py", line 636, in run_async
    #     loop.run_until_complete(coro)
    #   File "/usr/local/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    #     return future.result()
    #   File "/opt/project/api/stuff.py", line 42, in run
    #     t: Tournament = await Tournament.filter(name=tournament_name).select_related("events").first()
    #   File "/usr/local/lib/python3.8/site-packages/tortoise/queryset.py", line 797, in _execute
    #     instance_list = await self._db.executor_class(
    #   File "/usr/local/lib/python3.8/site-packages/tortoise/backends/base/executor.py", line 149, in execute_select
    #     setattr(ins, model_name, obj)
    # AttributeError: can't set attribute

if __name__ == "__main__":
    run_async(run())

relates to #500 and #501

Seems like this issue not actual any more. At list on version 0.19.2 it works well.

Console output:

tournament <tortoise.fields.relational.ReverseRelation object at 0x7f6ff27197f0>
tournament <tortoise.fields.relational.ReverseRelation object at 0x7f6ff27192b0> [<Event: 1>, <Event: 2>]
tournament First [('name', 'First'), ('id', 1), ('tournament_id', 1)]```

Read more comments on GitHub >

github_iconTop Results From Across the Web

Django ORM select_related AttributeError - Stack Overflow
if i start python manage.py shell from terminal the "kv = temp_test_keywords.select_related()" command works fine, why in my .py code doesn't?
Read more >
select_related() from a plain model with a ForeignKey ... - GitHub
Note that this raises AttributeError : it doesn't try to query the DB, is just fails an attribute lookup (which is a lot...
Read more >
23739 (django 1.7.1 defer() throws AttributeError when using ...
Entering the following code (in a Django shell) works fine: ModelB.objects.filter(name='').select_related('model_a').defer('model_a__name').
Read more >
django.db.models.query — Django 1.7.11 documentation
if not isinstance(k, (slice,) + six.integer_types): raise TypeError ... values().delete() doesn't work currently - make sure it raises an ...
Read more >
Having trouble optimize a query where it uses ... - Reddit
But it fails with 'Prefetch' object has no attribute 'split', which I traced through the source, and it looks like select_related can't work...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found