Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django forms and ModelMultipleChoiceField when working with existing records

I'm trying to do something which I would think is quite common, but I'm just really unsure how to tackle this.

What I currently do: Currently I produce a list of check boxes (using 'CheckboxSelectMultiple') on my form which auto ticks all the users who are in 'members' from the list of all Users in django (produced by "queryset=User.objects.all()").

What I want to actually do: I don't want to list every user in Django, I just want to show a list of Users in 'members'.

How I think it can be done: I think I can do this by modifying the queryset to be something like "project.members.all()" where project = Project (a specific instance of project). But how do I pass this context to my form?

# models.py
class Project(models.Model):
    name = models.CharField(max_length=100)
    members = models.ManyToManyField(User, related_name="members", blank=True, null=True)


# forms.py
class ProjectSettings(forms.Form):
    summary = forms.CharField(max_length=200)
    members = forms.ModelMultipleChoiceField(queryset=User.objects.all(), widget=forms.CheckboxSelectMultiple())


#template snippet
        <form method="post" action="/projects/{{ project.slug }}/settings/save/">
    {% for field in form %}
    <div class="form-row">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
        {% if field.help_text %}
        <p class="help-text">{{ field.help_text }}</p>
        {% endif %}
    </div>
    {% endfor %}


# view.py
def update_project(request, project_slug):
    if request.method == 'POST':
        form = ProjectSettings(request.POST)
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            project.summary = form.cleaned_data['summary']
            project.members = form.cleaned_data['members']
            project.save()
            return HttpResponseRedirect('/projects/' + project.slug + '/')
    else:
        data_dict = {'summary': project.summary, 'members': project.members.all()}
        form = ProjectSettings(initial=data_dict)

    return render_to_response('update_project.html', {'form': form}, context_instance=RequestContext(request))

I hope this makes sense, and I do apologise as I really think this is something simple I'm missing here - but I haven't been able to find a specific example showing how to do this with non-model forms.

Thanks in advance,

Jamie

like image 930
Jamie Avatar asked Jan 21 '23 04:01

Jamie


1 Answers

I'm guessing that you are leaving some stuff out of your Project model. Your view is also calling project.members.all() and project.summary with out getting a project in the else statement.

Assuming that you have a summary field in the projects model, if you want to use a form and not a model form, then:

forms.py

class ProjectSettings(forms.Form):
    summary = forms.CharField(max_length=200)

    def __init__(self, qs=None, *args, **kwargs):
        super(ProjectSettings, self).__init__(*args, **kwargs)
        if qs:
            self.fields['members'] = forms.ModelMultipleChoiceField(queryset=qs, widget=forms.CheckboxSelectMultiple())

and in your views.py you would pass in the qs to the form:

def update_project(request, project_slug):
    project = None
    if project_slug:
        project = get_object_or_404(Project, name=project_slug) # somehow get your project object  

    qs = project.members.all()

    if request.method == 'POST':
        form = ProjectSettings(qs, request.POST)
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            project.summary = form.cleaned_data['summary']
            project.members = form.cleaned_data['members']
            project.save()
            return HttpResponseRedirect('/projects/' + project.slug + '/')
    else:
        form = ProjectSettings(qs)

    return render_to_response('update_project.html', {'form': form}, context_instance=RequestContext(request))

Using a ModelForm makes sense here if you do indeed have your summary field in your Project model.

forms.py:

class ProjectSettings(forms.ModelForm):
    def __init__(self, qs=None, *args, **kwargs):
        super(ProjectSettings, self).__init__(*args, **kwargs)
        self.fields['members'].widget = forms.CheckboxSelectMultiple()
    class Meta:
        model = Project
        fields = ('summary', 'members')

views.py:

def update_project(request, project_slug):
    project = None
    if project_slug:
        project = get_object_or_404(Project, name=project_slug) # somehow get your project object  

    if request.method == 'POST':
        form = ProjectSettings(request.POST, instance=project)
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            project.save()
            return HttpResponseRedirect('/projects/' + project.slug + '/')
    else:
        form = ProjectSettings(instance=project)

    return render_to_response('update_project.html', {'form': form}, context_instance=RequestContext(request))
like image 67
dting Avatar answered Jan 22 '23 19:01

dting