14 Apr 2026

feedDjango community aggregator: Community blog posts

Representing 'Jobs to be Done' in a project

I have had this idea/theory for a while, that most software tools would benefit from a simple built in todo list built into the product directly. The main idea is for users to create jobs themselves or for the system itself to create jobs for the user to complete. The general thesis for this idea comes from a direct reference to the term "Jobs to be done". Any piece of software that gets used, exists to complete a job and do it better than the solution before it.

Well over the last couple of months I have turned this idea into a reality inside Hamilton Rock, or at least the first version of it and so far it seems very promising. The general API design is a few custom signals, a model and some signal reciever functions for the custom signals. The rest of the project interacts solely through the custom signals job_requested and job_completed. Each does as you would expect, job_requested requests a job be created, job_completed complete the related job to the model instance given. The core of the job model has a status, type and a generic foreign key which forms the target related to the job. Jobs can auto-completed when a condition has been met, it's just a matter of firing off the completed signal. Example stub of the API is below.

job_requested = Signal()
job_completed = Signal()

class Job(models.Model):
    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        help_text=_("The type of object this job is related to"),
    )
    object_id = models.PositiveIntegerField(
        help_text=_("The ID of the related object"),
    )
    target = GenericForeignKey("content_type", "object_id")
    
    assigned_to = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assigned_jobs",
        help_text=_("User this job is assigned to (default notification recipient)"),
    )
    
    job_type = models.CharField(
        max_length=50,
        choices=jobTypeChoices.choices,
        help_text=_("The type of job"),
    )
    
    status = models.CharField(
        max_length=20,
        choices=StatusChoices.choices,
        default=StatusChoices.PENDING,
        db_index=True,
        help_text=_("Current status of the job"),
    )
    created_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text=_("When the job was completed or cancelled"),
    ) 
    updated_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text=_("When the job was completed or cancelled"),
    ) 
    completed_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text=_("When the job was completed or cancelled"),
    )
    expires_at = models.DateTimeField(
        null=True,
        blank=True,
        db_index=True,
        help_text=_("When this job should be auto-cancelled if still pending"),
    )
    metadata = models.JSONField(
        default=dict,
        blank=True,
        help_text=_("Additional job-specific data"),
    )
    
@receiver(job_requested)
def handle_job_requested(  # NOQA: PLR0913
    sender,
    target,
    job_type,
    metadata=None,
    idempotent=False,
    assigned_to=None,
    **kwargs,
):
    # Creates a Job
    pass
    
@receiver(task_completed)
def handle_task_completed(sender, target, task_type, metadata=None, **kwargs):
    # Completes a Job
    pass

Currently all jobs are created by the system and we have yet to directly expose them to our users, however there have been two interesting insights from where I have used them to date (onboarding & staff pages). First in onboarding it has created a flexible way to add/remove steps for me as a developer and given that completing a job is part of the internal state rather than just a custom form or series of forms, the logic to visualise where users are stalling in our flow becomes easy to visualise. It's how many pending jobs at each step and how long have they been pending?

Second when an handled exception case occurs in the application which requires the attention of a staff user, instead of a log statement needing to be processed by some system and being perhaps overfilled with extra context, or there being extra noise in a developer tool such as Sentry, we simply create a job of the type we need. Then create a staff page to list and handle jobs of this type.

Finally this jobs architecture has enabled a central point to control how we send notifications (emails etc) from the system. Every notification is linked to a Job, which then means it forces notifications to not deal with too many actions at a time and again allows a natural tracking of which notifications have been actioned or not.

In future, I'm likely to expose these jobs to our users more directly, giving them a native solution to spend just the right amount of time in our product and no more, so what we build is simple & useful.

What do you think? Would you like this as a package for you to play with? Let me know and I may just break it out and release it!

14 Apr 2026 5:00am GMT

13 Apr 2026

feedDjango community aggregator: Community blog posts

Built with Django Weekly Roundup: Mar 23 to Apr 13

Hey, Happy Monday!

Why are you getting this: You signed up to receive this newsletter on Built with Django. I share recent projects, jobs, and useful Django links. If you no longer want this, you can unsubscribe anytime.

News and Updates

Sponsor

This issue is sponsored by TuxSEO, your content team on auto-pilot.

Generate SEO-focused content briefs and drafts faster, and ship consistently without the usual content bottlenecks.

Projects

Jobs

From the Community

Support

You can support this project by using one of the affiliate links below. These are always going to be projects I use and love! No "Bluehost" crap here!

13 Apr 2026 6:00pm GMT

11 Apr 2026

feedDjango community aggregator: Community blog posts

djust 0.4.0 — The Developer Experience Release

djust 0.4.0 ships 30+ features focused on developer experience: flash messages, keyboard shortcuts, form recovery, scaffolding generators, debug tooling, and security hardening. Build real-time Django apps with less code than ever.

11 Apr 2026 5:00pm GMT