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

MultipleObjectsReturned in core.views.serve.serve.docs #4350

Closed
xrmx opened this issue Jul 10, 2018 · 13 comments · Fixed by #5783
Closed

MultipleObjectsReturned in core.views.serve.serve.docs #4350

xrmx opened this issue Jul 10, 2018 · 13 comments · Fixed by #5783
Labels
Accepted Accepted issue on our roadmap Bug A bug

Comments

@xrmx
Copy link
Contributor

xrmx commented Jul 10, 2018

We get this exception while trying to serve some docs:

MultipleObjectsReturned: get() returned more than one Version -- it returned 2!
  File "django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "readthedocs/core/views/serve.py", line 96, in inner_view
    return view_func(request, project=project, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 74, in inner_view
    return view_func(request, subproject=subproject, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 156, in serve_docs
    version = project.versions.public(request.user).get(slug=version_slug)
  File "django/db/models/query.py", line 391, in get
    (self.model._meta.object_name, num)

The view code looks legit at a first look but there's a huge side effect of the user filtering in the public() method. It does not filter the projects by user but it adds to the queryset all the other user projects which is not what we need here:

# project of the version we are looking for
>>> versione.project
<Project: Documentazione API anagrafe del Comune di Tre Civette sul Comò>
# this queryset is fine
>>> versione.project.versions.all()
[<Version: Version master of Documentazione API anagrafe del Comune di Tre Civette sul Comò (861)>, <Version: Version bozza of Documentazione API anagrafe del Comune di Tre Civette sul Comò (860)>]
# but here we have other projects' version too
>>> versione.project.versions.public(user)
[<Version: Version stabile of design-docs (859)>, <Version: Version master of Documentazione API anagrafe del Comune di Tre Civette sul Comò (861)>, <Version: Version bozza of design-docs (852)>, <Version: Version bozza of Documentazione API anagrafe del Comune di Tre Civette sul Comò (860)>]

@xrmx
Copy link
Contributor Author

xrmx commented Jul 10, 2018

Wondering if not passing the user to public is the correct fix.

@stsewd
Copy link
Member

stsewd commented Jul 10, 2018

I have encountered this sometimes locally, but I wasn't able to replicate, and I think this has never happened in production.

@stsewd
Copy link
Member

stsewd commented Jul 10, 2018

Also, this had already been reported before #2613

@xrmx
Copy link
Contributor Author

xrmx commented Jul 10, 2018

Thanks for the heads up. I'll try to cook a test and a PR then.

xrmx added a commit to italia/docs.italia.it that referenced this issue Jul 11, 2018
We get this exception while trying to serve some docs:

MultipleObjectsReturned: get() returned more than one Version -- it returned 2!
  File "django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "readthedocs/core/views/serve.py", line 96, in inner_view
    return view_func(request, project=project, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 74, in inner_view
    return view_func(request, subproject=subproject, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 156, in serve_docs
    version = project.versions.public(request.user).get(slug=version_slug)
  File "django/db/models/query.py", line 391, in get
    (self.model._meta.object_name, num)

The view code looks legit at a first look but there's a huge side
effect of the user filtering in the public() method. It does not
filter the projects by user but it adds to the queryset all the
other user projects which is not what we need here. So just don't
pass the user to the public() method fixes to get the code work.

Fix readthedocs#4350
xrmx added a commit to italia/docs.italia.it that referenced this issue Jul 11, 2018
We get this exception while trying to serve some docs:

MultipleObjectsReturned: get() returned more than one Version -- it returned 2!
  File "django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "readthedocs/core/views/serve.py", line 96, in inner_view
    return view_func(request, project=project, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 74, in inner_view
    return view_func(request, subproject=subproject, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 156, in serve_docs
    version = project.versions.public(request.user).get(slug=version_slug)
  File "django/db/models/query.py", line 391, in get
    (self.model._meta.object_name, num)

The view code looks legit at a first look but there's a huge side
effect of the user filtering in the public() method. It does not
filter the projects by user but it adds to the queryset all the
other user projects which is not what we need here. So just don't
pass the user to the public() method fixes to get the code work.

Fix readthedocs#4350
xrmx added a commit to italia/docs.italia.it that referenced this issue Jul 11, 2018
We get this exception while trying to serve some docs:

MultipleObjectsReturned: get() returned more than one Version -- it returned 2!
  File "django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "readthedocs/core/views/serve.py", line 96, in inner_view
    return view_func(request, project=project, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 74, in inner_view
    return view_func(request, subproject=subproject, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 156, in serve_docs
    version = project.versions.public(request.user).get(slug=version_slug)
  File "django/db/models/query.py", line 391, in get
    (self.model._meta.object_name, num)

The view code looks legit at a first look but there's a huge side
effect of the user filtering in the public() method. It does not
filter the projects by user but it adds to the queryset all the
other user projects which is not what we need here.

Instead simplify the code to:
- return 404 if the requested version does not exist
- return 401 if the version is not private and the user is not admin
- search the file if the version is private and the user is the admin

Fix readthedocs#4350
xrmx added a commit to italia/docs.italia.it that referenced this issue Jul 11, 2018
We get this exception while trying to serve some docs:

MultipleObjectsReturned: get() returned more than one Version -- it returned 2!
  File "django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "readthedocs/core/views/serve.py", line 96, in inner_view
    return view_func(request, project=project, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 74, in inner_view
    return view_func(request, subproject=subproject, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 156, in serve_docs
    version = project.versions.public(request.user).get(slug=version_slug)
  File "django/db/models/query.py", line 391, in get
    (self.model._meta.object_name, num)

The view code looks legit at a first look but there's a huge side
effect of the user filtering in the public() method. It does not
filter the projects by user but it adds to the queryset all the
other user projects which is not what we need here.

Instead simplify the code to:
- return 404 if the requested version does not exist
- return 401 if the version is not private and the user is not admin
- search the file if the version is private and the user is the admin

Fix readthedocs#4350
@p3trus
Copy link

p3trus commented Oct 30, 2018

Just a short question, why is this issue closed? To me it appears that the fix has not been applied to readthedocs but to the fork only.

@xrmx xrmx reopened this Oct 30, 2018
@xrmx
Copy link
Contributor Author

xrmx commented Oct 30, 2018

@p3trus thanks for checking, it has been closed automatically by github :)

xrmx added a commit to italia/docs.italia.it that referenced this issue Nov 1, 2018
We get this exception while trying to serve some docs:

MultipleObjectsReturned: get() returned more than one Version -- it returned 2!
  File "django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "readthedocs/core/views/serve.py", line 96, in inner_view
    return view_func(request, project=project, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 74, in inner_view
    return view_func(request, subproject=subproject, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 156, in serve_docs
    version = project.versions.public(request.user).get(slug=version_slug)
  File "django/db/models/query.py", line 391, in get
    (self.model._meta.object_name, num)

The view code looks legit at a first look but there's a huge side
effect of the user filtering in the public() method. It does not
filter the projects by user but it adds to the queryset all the
other user projects which is not what we need here.

Instead simplify the code to:
- return 404 if the requested version does not exist
- return 401 if the version is private and the user is not admin
- serve the file if the version is private and the user is the admin

Fix readthedocs#4350
xrmx added a commit to italia/docs.italia.it that referenced this issue Nov 1, 2018
We get this exception while trying to serve some docs:

MultipleObjectsReturned: get() returned more than one Version -- it returned 2!
  File "django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "readthedocs/core/views/serve.py", line 96, in inner_view
    return view_func(request, project=project, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 74, in inner_view
    return view_func(request, subproject=subproject, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 156, in serve_docs
    version = project.versions.public(request.user).get(slug=version_slug)
  File "django/db/models/query.py", line 391, in get
    (self.model._meta.object_name, num)

The view code looks legit at a first look but there's a huge side
effect of the user filtering in the public() method. It does not
filter the projects by user but it adds to the queryset all the
other user projects which is not what we need here.

Instead simplify the code to:
- return 404 if the requested version does not exist
- return 401 if the version is private and the user is not admin
- serve the file if the version is private and the user is the admin

Fix readthedocs#4350
xrmx added a commit to italia/docs.italia.it that referenced this issue Nov 3, 2018
We get this exception while trying to serve some docs:

MultipleObjectsReturned: get() returned more than one Version -- it returned 2!
  File "django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "readthedocs/core/views/serve.py", line 96, in inner_view
    return view_func(request, project=project, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 74, in inner_view
    return view_func(request, subproject=subproject, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 156, in serve_docs
    version = project.versions.public(request.user).get(slug=version_slug)
  File "django/db/models/query.py", line 391, in get
    (self.model._meta.object_name, num)

The view code looks legit at a first look but there's a huge side
effect of the user filtering in the public() method. It does not
filter the projects by user but it adds to the queryset all the
other user projects which is not what we need here.

Instead simplify the code to:
- return 404 if the requested version does not exist
- return 401 if the version is private and the user is not admin
- serve the file if the version is private and the user is the admin

Fix readthedocs#4350
@stale
Copy link

stale bot commented Jan 10, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the Status: stale Issue will be considered inactive soon label Jan 10, 2019
@stsewd stsewd added Bug A bug Accepted Accepted issue on our roadmap and removed Status: stale Issue will be considered inactive soon labels Jan 10, 2019
@p3trus
Copy link

p3trus commented Jan 15, 2019

Any plans to merge this bugfix in the near future?

@xrmx
Copy link
Contributor Author

xrmx commented Jan 15, 2019

@p3trus this is the bug not the PR

@p3trus
Copy link

p3trus commented Jan 15, 2019

I know but the docsitalia team already fixed it.

@xrmx
Copy link
Contributor Author

xrmx commented Jan 15, 2019

@p3trus you probably missed I'm the one who is trying to get this merged :)

@p3trus
Copy link

p3trus commented Jan 15, 2019

@xrmx I didn't mean to sound nagging. Just wanted to know the timeframe if there is any.

@xrmx
Copy link
Contributor Author

xrmx commented Jan 15, 2019

@p3trus asking for ETA is nagging :) If you look at the PR you'll see clearly stated that it's not merged because coverage tests fail. They fail because code does not do what the tests expect and I suspect even before the proposed changes.

xrmx added a commit to italia/docs.italia.it that referenced this issue Apr 20, 2019
We get this exception while trying to serve some docs:

MultipleObjectsReturned: get() returned more than one Version -- it returned 2!
  File "django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "readthedocs/core/views/serve.py", line 96, in inner_view
    return view_func(request, project=project, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 74, in inner_view
    return view_func(request, subproject=subproject, *args, **kwargs)
  File "readthedocs/core/views/serve.py", line 156, in serve_docs
    version = project.versions.public(request.user).get(slug=version_slug)
  File "django/db/models/query.py", line 391, in get
    (self.model._meta.object_name, num)

The view code looks legit at a first look but there's a huge side
effect of the user filtering in the public() method. It does not
filter the projects by user but it adds to the queryset all the
other user projects which is not what we need here.

Instead simplify the code to:
- return 404 if the requested version does not exist
- return 401 if the version is private and the user is not admin
- serve the file if the version is private and the user is the admin

Fix readthedocs#4350
stsewd added a commit to stsewd/readthedocs.org that referenced this issue Jun 11, 2019
When filtering using `public` and using a user,
the queryset hit this

https://github.com/rtfd/readthedocs.org/blob/45df7fd0da44be9eab3c0cb2888f6a9a15421fc5/readthedocs/builds/querysets.py#L22-L24

When the user is authenticated, we call to `get_objects_for_user`
which gets all the versions from all the user's projects.
Overriding any previous filter (`project.versions` in this case)

We don't see this in production because we serve from another domain.
And we don't see this in the corporate site because we override the
serve_docs view.

Fix readthedocs#4350
Closes readthedocs#4356
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Accepted Accepted issue on our roadmap Bug A bug
Projects
None yet
3 participants