Django override __init__ form method using ModelForm

Viewed 165

I have a foreign key (zone_set) as a choice field in a form. It should only display current project's zone_set . As you can see, It is not the case since it's displaying a zone_set: I belong to an other project, I should not be displayed here which does not belong to the current project.

enter image description here

Here is my form but it doesn't work.

class ODMatrixForm(forms.ModelForm):

    class Meta:
        model = ODMatrix
        # fields = '__all__'
        exclude = ('size', 'locked',)

    def __init__(self, current_project=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if current_project:
            queryset = ZoneSet.objects.filter(project=current_project)
            self.fields['zone_set'].queryset = queryset

The view creating the ODMatrix

def create_od_matrix(request, pk):
    """A roadnetwork depends on a project. It
     must be created inside the project"""
    
    current_project = Project.objects.get(id=pk)
    form = ODMatrixForm(initial={'project': current_project})
    if request.method == 'POST':
        print(request)
        od_matrix = ODMatrix(project=current_project)
        # form = ODMatrixForm(request.POST, instance=od_marix)
        form = ODMatrixForm(data=request.POST, instance=od_matrix)
        if form.is_valid():
            form.save()
            messages.success(request, "OD matrix cessfully created")
            return redirect('od_matrix_details', od_matrix.pk)

    context = {
        'project': current_project,
        'form': form}
    return render(request, 'form.html', context)
1 Answers

You created the __init__ as constructor of the Meta class, this should be a constructor of the ODMatrixForm, so:

class ODMatrixForm(forms.ModelForm):
    
    #       🖟 part of ODMatrixForm, not Meta
    def __init__(self, current_project=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if current_project:
            self.fields['zone_set'].queryset = ZoneSet.objects.filter(
                project=current_project
            )
    
    class Meta:
        model = ODMatrix
        # fields = '__all__'
        exclude = ('size', 'locked',)

In the view, you will need to pass the current_project when you construct an ODMatrixForm object:

from django.shortcuts import get_object_or_404

def create_od_matrix(request, pk):
    current_project = get_object_or_404(Project, pk=pk)
    if request.method == 'POST':
        form = ODMatrixForm(current_project, request.POST, request.FILES)
        if form.is_valid():
            form.instance.project = current_project
            form.save()
            messages.success(request, 'OD matrix cessfully created')
            return redirect('od_matrix_details', od_matrix.pk)
    else:
        form = ODMatrixForm(current_project=current_project)
    context = {
        'project': current_project,
        'form': form
    }
    return render(request, 'form.html', context)

Note: It is often better to use get_object_or_404(…) [Django-doc], then to use .get(…) [Django-doc] directly. In case the object does not exists, for example because the user altered the URL themselves, the get_object_or_404(…) will result in returning a HTTP 404 Not Found response, whereas using .get(…) will result in a HTTP 500 Server Error.

Related