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

Make todo.Task swappable #70

Closed
AdrienLemaire opened this issue Apr 23, 2019 · 8 comments
Closed

Make todo.Task swappable #70

AdrienLemaire opened this issue Apr 23, 2019 · 8 comments

Comments

@AdrienLemaire
Copy link

AdrienLemaire commented Apr 23, 2019

Usecase: Extend the Task model to add a generic foreignkey to another model.
This will allow us then to set triggers to complete a task when an action is performed on its related model.
Concrete example: Link a wagtail page to a task when creating it, then auto-complete the task when the wagtail Page status field has been changed.

3rd-part code using todo:

# my_app/models.py
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from todo.models import Task


class LinkedTask(Task):
    """
    Link a task to another model, eg a Page model.
    """
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey("content_type", "object_id")

# settings.py
TODO_TASK_MODEL = "my_app.LinkedTask"

todo suggested modification using swapper

import swapper

class BaseTask(models.Model):
    # rename current Task to BaseTask

class Task(BaseTask):
    class Meta:
        swappable = swapper.swappable_setting('todo', 'Task')

If you agree with this feature request, I can do a PR for it tomorrow (japan timezone)

@AdrienLemaire
Copy link
Author

@shacker fyi, since this is a blocker for me, I'll get started on it now.

@AdrienLemaire AdrienLemaire mentioned this issue Apr 24, 2019
15 tasks
@shacker
Copy link
Owner

shacker commented Apr 24, 2019

@Fandekasp Since the goal here is to trigger task changes when something elsewhere in the project changes, I think a more flexible (and less invasive) solution would be to take advantage of Django's native signals system.

Django includes a “signal dispatcher” which helps allow decoupled applications get notified when actions occur elsewhere in the framework. In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place. They’re especially useful when many pieces of code may be interested in the same events.

That would give you or others the flexibility to create additional signals handlers (or arguments to a single handler) with limitless flexibility, no need for model changes, and no need for another dependency (I try to keep the number of required dependencies to a minimum).

Then other parts of the app could trigger signals on more than just model changes - they could respond to REST inputs, javascript events, model save() triggers, or anything at all.

Can you make an argument in favor of model changes over using signals? Let me know what you think

@AdrienLemaire
Copy link
Author

@shacker Thanks for the suggestion.

I do not see how I can build my feature on signals alone. Maybe you'll have good insight.
As discussed in #69, I'm working on a complex Wagtail workflow, that would go as follow:

  • Create a Page with associated todo task "Write the title & keywords". A user will pick the task, then can navigate to the Page in question from the task.
  • When the title & keywords have been submitted, a hook could auto-close the title task, and create a new "write the body" task for the same article.
  • Similarly, once the writing is completed, the task will be closed and a review task will be open for reviewers to pick. Then we have a client check, and finally the publication.

As you can see, I need a ManyToOne relationship (foreignkey) between a Task and a Page (in the next release, we'll handle similar workflow with different models, which is why I wrote the contentype generic relation in the example).
How would you be able to close the proper related task when submitting a page with signals alone?

@shacker
Copy link
Owner

shacker commented Apr 24, 2019

You can do anything from your custom signals. I've created a sample pull request demonstrating how the system could be used. This is very bare bones but demonstrates that the flexibility is unlimited:

#72

I really think this is the way to go. Less invasive, more flexible, more extensible, more portable to other use cases.

In fact... I need to think about this a bit more, but I'm not even sure django-todo needs to provide anything in this department. Any project can define its own signal handlers and receivers in whatever way makes sense for them. But maybe there is some core stuff (like the complete toggle) we should provide as a reference.

@shacker
Copy link
Owner

shacker commented Apr 24, 2019

Your mention of needing "a hook to auto-close the title task" is a good example of a need that's specific to your use case, rather than a general use case. Easily provided by signals.

As to your need for a db relation between Task and Page, that is something you can create from within your project.

@AdrienLemaire
Copy link
Author

AdrienLemaire commented Apr 24, 2019

@shacker if I understand properly, your thinking goes as follow:

  • django-todo models shouldn't have relations with external apps.
  • I could create a table to store tuples of task ids, page ids, and workflow step ids within the wagtail-todo app
  • signals connected to tasks & page actions could do a lookup on that table to figure out what is connected to what.

This doesn't sound optimal :( I'll spend more time clarifying my understanding of django signals, wagtail hooks, and Django good patterns.
In any case, great support, thanks a bunch!

@shacker
Copy link
Owner

shacker commented Apr 25, 2019

django-todo models shouldn't have relations with external apps.

More specifically, fine to create those relationships, but you should create them from within your own project. Modifying core todo models is a pretty tectonic shift and un-needed by anyone else until now. Django projects take a million different forms. The best goal of this app is to stay vanilla and let people integrate it however they need to.

I could create a table to store tuples of task ids, page ids, and workflow step ids within the wagtail-todo app

Sure. Or just simple FKs or M2Ms between your models and Task or TaskList. Whatever you need!

signals connected to tasks & page actions could do a lookup on that table to figure out what is connected to what.

Absolutely. Then you can always write whatever code you need to do whatever you need without needing to modify any models or migrate anything, or to require migrations in a 3rd-party app!

The optimal design pattern is that each app should limit its scope as much as possible. "Small pieces, loosely joined" (the Unix philosophy). Emphasis on "loosely joined." It's what prevents software from becoming spaghetti.

@AdrienLemaire
Copy link
Author

@shacker fair enough. Thanks for your support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants