Nobody has started this yet — be first.
Business impact
This app exists to let someone log what they've read -- including a novella or a short book finished in a single sitting, which is not an edge case for a lot of readers, it's normal. Right now the API flatly refuses to let anyone record that: every same-day read gets rejected with a confusing error claiming the finish date is "before" the start date, when the user typed the exact same date in both fields. The workaround (back-date one of the two fields by a day, so the log is now factually wrong) is exactly the kind of "the tool doesn't trust my own data" moment that makes people stop using a tracker.
Problem
POST /api/books/ with status="read", date_started and date_finished both set to the same date returns 400 Bad Request with {"date_finished": ["date_finished cannot be before date_started."]}.
Current behavior
A same-day read is rejected with an error message that claims the finish date is before the start date, even though the two dates are identical.
Expected behavior
date_finished == date_started is valid and must be accepted (201). Only date_finished strictly before date_started should be rejected -- which is exactly what the error message already claims to check for.
Steps to reproduce
cd django/library_tracker source ../library_tracker_venv/bin/activate python manage.py shell -c " from rest_framework.test import APIClient c = APIClient() r = c.post('/api/books/', { 'title': 'One-Day Read', 'author': 'Someone', 'status': 'read', 'date_started': '2026-03-10', 'date_finished': '2026-03-10', }) print(r.status_code, r.data) "
Why this matters
This exact rule is implemented twice in this codebase: once in books/serializers.py (BookSerializer.validate, which is what the API actually runs on every request) and once in books/models.py (Book.clean, which the DRF create/update path never calls -- DRF writes straight to the database via Model.objects.create()/.save(), it does not call full_clean()). The two copies have drifted: one uses < (same-day is fine) and the other now uses <= (same-day is rejected). Reading the serializer alone looks internally consistent -- the bug only shows up when you notice it disagrees with the model's own version of the same rule.
Suggested approach
Compare the date comparison in BookSerializer.validate() (books/serializers.py) against the equivalent check in Book.clean() (books/models.py). They're meant to enforce the same rule; only one of them is currently correct.
Acceptance criteria
Verification
python manage.py test practicetickets.test_ticket04_same_day_finish_rejected -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/same-day-finish-rejectedFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/same-day-finish-rejectedSubmit 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 inhttp://127.0.0.1:8000/api/books/http://127.0.0.1:8000/admin/SECRET_KEYDEBUGALLOWED_HOSTSOr with Docker (migrations run automatically on container startup):
docker compose up --build
docker compose exec web python manage.py createsuperuser # optional, to use /admin/
Work the tickets in practicetickets/ (TICKET_01 through TICKET_07); each names one Django test:
python manage.py test practicetickets.test_ticket01_rating_boundary -v 2 # a single ticket
./practicetickets/run_tickets.sh # all 7, clean pass/fail summary
Tickets are independent and deliberately isolated from each other's bugs -- fix them in any order. python manage.py test books runs the project's own 4-test suite (separate from practicetickets/, which is never added to INSTALLED_APPS) and should report OK both before and after every ticket is fixed.
Level 1
Fix a bug
Read existing behaviour, correct it.