Nobody has started this yet — be first.
Business impact
Any authenticated user can currently PATCH (deactivate) any other user's poll -- a poll's creator has zero special standing except that they, specifically, are the one person forbidden from touching it. In practice: a user creates a poll, a completely unrelated user closes it out from under them, and the creator gets a 403 Forbidden if they try to fix it themselves. This is a genuine authorization bug (anyone can tamper with anyone else's poll) as well as a usability dead end (the actual owner can't manage their own content), and it's the kind of thing that generates angry support tickets ("why can't I edit my own poll?") right alongside abuse reports ("someone closed my poll and I don't know who").
Problem
IsCreatorOrReadOnly.has_object_permission is supposed to allow write access only to a poll's creator (reads are already open to everyone via SAFE_METHODS). As written, it grants write access to everyone except the creator.
Current behavior
PATCH /api/polls/{id}/ by the poll's own creator returns 403 Forbidden, while the exact same request from any other authenticated user returns 200 and applies the change.
Expected behavior
The user who created a poll can PATCH/PUT/DELETE it. Every other authenticated user gets 403 Forbidden on the same request. Read access (GET list/detail/results) is unaffected either way.
Steps to reproduce
cd django/poll_app source .venv/bin/activate python manage.py runserver &
curl -s -X POST http://127.0.0.1:8000/api/auth/register/ -H "Content-Type: application/json"
-d '{"username":"dave","email":"dave@example.com","password":"d-strong-password-1"}'
curl -s -X POST http://127.0.0.1:8000/api/auth/register/ -H "Content-Type: application/json"
-d '{"username":"erin","email":"","password":"e-strong-password-2"}'
Why this matters
This single comparison is the entire enforcement of "only the creator can modify their poll" -- there's no other check anywhere in PollViewSet. It's also already covered by two pre-existing tests in polls/tests/test_api.py (test_creator_can_deactivate_poll and test_non_creator_cannot_deactivate_poll), which currently fail because of it.
Suggested approach
Look at exactly one line in polls/permissions.py -- has_object_permission -- and what a boolean comparison operator needs to mean for "is this user not the creator, so we should read-only them" versus "is this user the creator, so we should let them write."
Acceptance criteria
Verification
.venv/bin/python manage.py test practicetickets.test_ticket01_permission_check_inverted -v 2 && .venv/bin/python manage.py test polls.tests.test_api.PollAPITests.test_creator_can_deactivate_poll polls.tests.test_api.PollAPITests.test_non_creator_cannot_deactivate_poll -v 2
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
$git checkout -b fix/permission-check-invertedFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/permission-check-invertedSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign incurl -s -X POST http://127.0.0.1:8000/api/polls/ -H "Authorization: Token $TOKEN_DAVE"
-H "Content-Type: application/json" -d '{"question":"Best editor?","options":["vim","emacs"]}'
curl -s -o /dev/null -w "%{http_code}\n" -X PATCH http://127.0.0.1:8000/api/polls/1/
-H "Authorization: Token $TOKEN_DAVE" -H "Content-Type: application/json" -d '{"is_active": false}'
curl -s -o /dev/null -w "%{http_code}\n" -X PATCH http://127.0.0.1:8000/api/polls/1/
-H "Authorization: Token $TOKEN_ERIN" -H "Content-Type: application/json" -d '{"is_active": false}'
http://127.0.0.1:8000/api/http://127.0.0.1:8000/admin/.env.example.envSECRET_KEYDEBUGDB_*CELERY_BROKER_URLmanage.py testOr via Docker (Postgres, Redis, Django with hot reload, and a Celery worker, all wired together, migrations run automatically on startup):
docker compose up --build
Work the tickets in practicetickets/ (ticket01 through ticket07); each names one dedicated Django test module:
./practicetickets/run_tickets.sh # all 7, clean pass/fail summary
./practicetickets/run_tickets.sh -v # summary + each test's full output
.venv/bin/python manage.py test practicetickets.test_ticket01_permission_check_inverted -v 2 # a single ticket
Three of the seven tickets (01, 02, 03) also collaterally break pre-existing tests in polls/tests/; run .venv/bin/python manage.py test polls -v 2 to confirm the main suite is back to fully green once those are fixed. No Redis or Celery worker is required for any of this -- settings.py forces CELERY_TASK_ALWAYS_EAGER = True whenever "test" appears in sys.argv.
Level 1
Fix a bug
Read existing behaviour, correct it.