Django ModelMultipleChoiceField for another model's many-to-many

I'm working on a DIY application (Django 1.5) and I've reached a roadblock. The main models involved are Guide, Tool, Item, Step. A Guide can have many Tools and Items, and a Tool or Item can belong to many Guides. The same goes for a Step - it can have many Tools and Items, and a Tool or Item can belong to many Steps. A Guide has many Steps and a Step belongs to a Guide.

Guide many-to-many Items

Guide many-to-many Tools

Guide one-to-many Steps

Step many-to-many Items

Step many-to-many Tools

The roadblock...

At the Guide-level, I want the Tool and Item options to be limitless. But at the Step-level, I want the Tool and Item options to be limited to those assigned to the Guide it belongs to. Basically, when creating/editing a Step, I want to list checkboxes for all the Tools and Items available through the Guide. The user selects those that are needed for the current Step. Each Step will have different combinations of Tools and Items (thus the need for checkboxes).

I discovered the ModelMultipleChoiceField for the Step's ModelForm class. There I can specify a queryset. BUT, how do I gain access to the instance of the Guide model to retrieve its Tools and Items so that I can properly build selections? I would like to provide queries similar to what you would do in a View...

Guide.objects.get(pk=n).tools.all()
Guide.objects.get(pk=n).items.all()

How can I achieve that via ModelMultipleChoiceField? I hope I was able to explained this clearly.

Thanks in advance for any help.

class Tool(models.Model):
    name = models.CharField(max_length=100)
    ...


class Item(models.Model):
    name = models.CharField(max_length=100)
    ...


class Guide(models.Model):
    models.CharField(max_length=100)
    description = models.CharField(max_length=500)
    tools = models.ManyToManyField(Tool, null=True, blank=True)
    items = models.ManyToManyField(Item, null=True, blank=True)
    ...


class Step(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()
    guide = models.ForeignKey(Guide)
    tools = models.ManyToManyField(Tool, null=True, blank=True)
    items = models.ManyToManyField(Item, null=True, blank=True)

EDIT: 5/2 After further reading, it looks like I have to override the __init__ method of ModelMultipleChoiceField, where I gain a reference to self.instance, allowing me to create my query like, self.instance.guide.tools.all() and self.instance.guide.items.all(). And then create the fields via fields['field_name'].

I'm at work now so I won't be able to try this out until later tonight. I'll report back my findings.

Answers


What I ended up doing is the following. I defined a method in my ModelForm class for creating the ModelMultipleChoiceField. The reason is at the point of requesting the Create Step page, there is no Guide associated with the Step, not until you save (POST...assuming validation is successful). But I do have access to the slug field of the Guide that the Step will be created for. I get if from the URL. And the slug field is unique in my app. So I pass the slug from my view to the form via the new method I created. From there, I'm able to get the tools assigned to the guide and make those options available on the form, in my template.

forms.py

class NewStepForm(forms.ModelForm):
    ...
    def create_tools_field(self, slug):
        self.fields['tools'] = forms.ModelMultipleChoiceField(
            queryset=Guide.objects.get(slug=slug).tools.all(),
            widget=forms.CheckboxSelectMultiple(attrs={'class': 'unstyled'})
        )
    ...

views.py

class NewStepView(View):

    form_class = NewStepForm
    initial = {'key': 'value'}
    template_name = "guides/guide_step_new.html"

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(NewStepView, self).dispatch(*args, **kwargs)

    def get(self, request, *args, **kwargs):
        slug = kwargs.get('slug')
        form = self.form_class(initial=self.initial)
        form.create_tools_field(slug)
        return render(request,  self.template_name, {'form': form})

Need Your Help

Ada: Using values of constructed objects

oop package ada

This post is a follow-up of a previous post:

How can i create and use generic calculation for statistical function Stdev ?

ssas business-intelligence olap-cube

How can create generic calculated member which i can reuse in order to calculate the statistical deviation on any logical related dimension(s) and measurement ? I need to be able to select any dime...