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

JSON returning null values on response

java json web-services jackson

My response is giving me null values, but when I'm running the same in fiddler, I get a lot of data. I have written very less code and used online tools to generate my code. I wonder where I am mes...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.