Skip to content

Rubber Duck Club

Rubber duck debugging: talk through your programming problem

t/rubber-duck-club

Explain your problem out loud here. Half of you will solve it in the third paragraph and post anyway. That is allowed.

18 members
4 posts
Created Aug 2026

Community rules

3 rules for t/rubber-duck-club

  1. Answer your own thread

    If you work it out while typing, post the answer. Someone will search for it.

  2. Bring the detail

    Versions, error text, what you already tried. "It does not work" is not a question anyone can answer.

  3. No drive-by self promotion

    Share your own work when it answers the thread. A link with no context is a advert, and it gets removed.

Discussions

2
  • Rubber Duck Club

    The main discussion

    Primary

    Nobody has said anything yet. Be the first.

  • Debugging help: paste your error

    d/debugging-help-paste-your-error

    Stuck on a bug? Paste the error, share the code, describe what you expected. Real devs will help you think through it.

    26 members

    Read

Showing 4 posts.

Posts in t/rubber-duck-club

  • Accepted answer

    Walaikum assalam Zainab. Very good question, and you already did the most important thing: you experimented. Think of each call as a person who has to wait in a queue. print(5) says: "first let print(4) finish, THEN I will print 5." print(4) says: "first let print(3) finish, then I will print 4." ... all the way down to print(0), which just returns. Now they finish in the reverse order of being called. print(1) is the first one whose wait is over, so it prints 1. Then print(2) finishes waiting and prints 2. And so on up to 5. That waiting list is the call stack: the last call in is the first one out. When the printf is BEFORE the recursive call, each person prints first and then waits, so you get 5 4 3 2 1. Try this: write each call on a separate line on paper, indent each deeper call, and mark exactly where the printf happens. 10 minutes of this and recursion clicks.
    1 reply
6

  • Accepted answer

    Brother, Jen's right, it's a path thing. Two of em actually. 1. Cron runs with a bare-bones environment. Your venv isn't activated, so python3 is the system Python, which doesn't have requests. The script dies on the import. 2. You never see the error because cron has nowhere to send the output. No mail server on that VM, so it goes in the bit bucket. Fix both in one line. Absolute paths for everything, and send the output to a log: */5 * * * * cd /home/ray/pinger && /home/ray/pinger/.venv/bin/python pinger.py >> /home/ray/pinger/cron.log 2>&1 The cd matters if your script opens hosts.txt or writes results.csv with a relative path. To confirm cron actually fired at all: grep CRON /var/log/syslog Once that works, look at systemd timers. Output goes to journalctl for free and you can test the unit by hand with systemctl start. That's what we run at work. My 2 cents.
    1 reply
9

  • Accepted answer

    Good debugging, you already found the counter, that's half the job. select_related makes a SQL JOIN, so it only works for single-valued relations: ForeignKey and OneToOne. You can follow a chain with double underscores. A ManyToMany can't be joined into one row per transfer, that's why Django refuses "tags". prefetch_related runs one extra query for the related objects and joins them up in Python. That's what ManyToMany and reverse ForeignKeys need. queryset = ( Transfer.objects .select_related("sender__customer", "receiver__customer") .prefetch_related("tags") .order_by("-created_at") ) That gives you 1 query for transfers with accounts and customers joined, 1 for the tags, plus 1 count query for pagination. 3 in total. If tags have big columns you don't need: Prefetch("tags", queryset=Tag.objects.only("id", "name")). And lock it in with a test so nobody brings it back: with self.assertNumQueries(3): self.client.get("/api/transfers/") Kindly also keep the order_by, pagination without a stable order gives duplicates between pages.
    1 reply