django-treebeard - Model with tree

Hey there!
I’m designing a Deals platform, and i came up with the following task.
We’re going to have a proposal, and that proposal will contain many scope.
The scopes will be the tree, and will look someting like:

Users
  -> Create
     -> Send an email everytime a user is created
  -> Read

I lookup to django-treebeard. But i could not found a way of implementing what i’m trying to achieve. The doubt that i have is, should my model look like this:

from django.db import models
from treebeard.mp_path import MP_Node

class Proposal(models.Model):
    # fields

class Scope(MP_Node):
    proposal = models.ForeignKey(to=Proposal, ...)
    task = models.CharField(max_length=123)

Or Look like this:

from django.db import models
from treebeard.mp_path import MP_Node

class Proposal(MP_Node):
    # Other Fields
    task = models.CharField(max_length=123)

PS: I will be editing, creating these models only in the admin.

That depends upon your data model. (It’s not really clear what you’re modelling here.)

In your first example, a Proposal can occupy multiple locations in your hierarchical tree. (Multiple Scope objects can all refer to the same Proposal.)

In your second example, each Proposal only occupies one location within the hierarchy.

You write:

However, strictly speaking, you can create multiple “trees” within the same table. (You can have multiple root nodes.)

So can multiple Scope objects at different levels of the same (or different) trees all refer to the same (or different) proposals?

Or is the real case a situation where only the root node can refer to a Proposal? If so, then I suggest to you that neither of these is a correct modeling.

Thanks for the reply.

Maybe i’m thinking this wrong, but my thought is: I will have a Proposal, and within that proposal, i can see all the Scopes related to that Proposal. That’s why i first mentioned the ForeignKey to the Proposal version.

In terms of only Django my Proposal model will look like:

class Proposal(AutoTimeStampModel):
    # AutoTimeStampModel is a Abstract model that add a few fields.
    TYPE_OUTSOURCING = "out-sourcing"
    TYPE_PROJECT = "project"
    TYPE_CHOICES = (
        (TYPE_OUTSOURCING, _("Out Sourcing")),
        (TYPE_PROJECT, _("Project")),
    )

    COST_TYPE_ALL = "all"
    COST_TYPE_HOURLY = "hourly"
    COST_TYPE_DAILY = "daily"
    COST_TYPE_WEEKLY = "weekly"
    COST_TYPE_MONTHLY = "monthly"
    COST_TYPE_YEARLY = "yearly"
    COST_TYPE_CHOICES = (
        (COST_TYPE_ALL, _("All")),
        (COST_TYPE_HOURLY, _("Hourly")),
        (COST_TYPE_DAILY, _("Daily")),
        (COST_TYPE_WEEKLY, _("Weekly")),
        (COST_TYPE_MONTHLY, _("Monthly")),
        (COST_TYPE_YEARLY, _("Yearly")),
    )

    customer = models.ForeignKey(
        verbose_name=_("Customer"),
        help_text=_("To who the proposal will be created"),
        to=Customer,
        on_delete=models.CASCADE,
    )
    title = models.CharField(
        verbose_name=_("Title"),
        help_text=_("The title that will show up to the customer"),
        max_length=128,
    )
    type = models.CharField(
        verbose_name=_("Type"),
        help_text=_("What's the type of the proposal"),
        choices=TYPE_CHOICES,
    )
    cost = models.DecimalField(
        verbose_name=_("Cost"),
        help_text=_("How much this proposal costs to be accepted"),
        null=True,
    )
    cost_type = models.CharField(
        verbose_name=_("Cost Type"),
        help_text=_("What's the type of Cost. This may depend on the project type"),
        max_length=10,
        choices=COST_TYPE_CHOICES,
    )

And from here i imagine that i could go to Scopes page and create a Scope for a Proposal

Ok, but how does this relate to a tree structure of the scopes?

Can any scope at any level in the tree refer to a proposal?

Or do you have multiple trees in the Model where only the root object of that tree refers to a proposal?

And if you’ve got a proposal and want to see

How does that relate to the children and parents of those scopes? (Do you want them retrieved as well?)

I think so.
Because all nodes, even if they are inside another, refer to a single proposal.

Yes, but do they all refer to the same proposal, or do they refer to different proposals?

Oh, they all refer to the same proposal.

So then is it more accurate to say that the root node of a tree refers to a proposal, and that all members of that tree inherit that relationship from the root?

(The problem with the structure you’ve proposed is that there’s nothing to enforce that common relationship. Every node within an individual scope tree could refer to a different proposal.)

I think that’s the case.
I also don’t know if this (django-treebeard) is the best approach to the problem.

Today we do this job manually, on a spreadsheet. And we create all of this Groups with tasks within them. So in the End the Scope is just a list of Groups with tasks, and to me this looked like a tree and i had heard of django-treebeard before, that’s why i tried to use it.

We haven’t identified this either way here.

It may be, it may not be.

That decision can only be made with a complete and clear understanding of what it is you’re trying to model - and the relationships between those entities.

You’re now possibly identifying three different (?) objects - Scope, Groups, Tasks, that may or may not have a hierarchical relationship among them.

They may just exist as separate models in a “container” relationship. (A Scope contains Groups. A Group contains Tasks.)

If your relationships are always these three objects in that fixed relationship, then my initial reaction is no - this does not constitute a need for a tree. I’d be inclined to create those three separate models and leave it at that.

Thanks for the time you spent replying here!

They are all the “same”. A task is a single activity (just a name), a group is a task that have subtasks, and the scope is all the activities.

If i approached this with a relationship i think it will look like this:

class Task(models.Model):
    proposal = models.ForeignKey(to=Proposal, on_delete=models.CASCADE)
    name = models.CharField(...)
    parent = models.ForeignKey(to="self", on_delete=models.CASCADE, null=True)

# Shell session
proposal = Proposal.objects.create(...)
group = Task.objects.create(name="Users", proposal=proposal)
create_user = Task.objects.create(name="Create User", parent=group, proposal=proposal)
send_email = Task.objects.create(name="Send an email...", parent=create_user, proposal=proposal)
delete_user = Task.objects.create(name="Delete User", parent=group, proposal=proposal)

another_group = Task.objects.create(name="CMS", proposal=proposal)
create_post = Task.objects.create(name="Create a Post", parent=another_group, proposal=proposal)

So the Scope here will be all the 6 Tasks.
There is no “concrete” Scope, it’s just the name of this set of tasks. Maybe i was not clear on my thoughts.

That’s really helpful, thanks.

However, I still have a problem understanding why you want to have a “proposal” reference on every task, if the tasks are grouped together as a set and are all related to the same Proposal.

It’s also still not clear to me how “deep” these relationships can get.

Can those subtasks have yet another layer of subtasks within them?

What I’m trying to determine here is whether or not a tree is an appropriate data structure.

I haven’t seen anything so far that leads me in that direction.

I think that i may go one or two levels in depth, but only one level is fine.
Like a Task with a Subtask (Group with tasks).

I think i don’t need to.
Referencing the “Group” task will do it.