djangoformsdjango-forms

Get URL pk in django forms.py


I have this URL

path('private/productores/<pk>', views.Productor_Private.as_view()),

Views.py

class Productor_Private(generic.DetailView):
    model = Productor
    template_name = 'firstpage/productor_private.html'

    def get(self, request, pk):
        form = RepartoForm()
        return render(request, self.template_name, {'form': form})

    def post(self, request, pk):
        form = RepartoForm(request.POST)

        if form.is_valid():
            return render(request, self.template_name, args)

I want to retrieve the pk from the URL to use it as a filter inside the forms.py, to do something like this:

class RepartoForm(forms.Form):
    productos = forms.ModelMultipleChoiceField(queryset=Producto.objects.filter(productor=pk))

So in other words, I need to check what the current user's "productor" id is in order to only retrieve the "productos" that belong to this "productor"


Solution

  • You will need to "patch" the form constructor, and manually set the queryset in the __init__ function:

    class RepartoForm(forms.Form):
        productos = forms.ModelMultipleChoiceField(queryset=Producto.objects.all())
    
        def __init__(self, *args, productor_pk=None, **kwargs):
            super(forms.Form, self).__init__(*args, **kwargs)
            if productor_pk is not None:
                self.fields['productos'].queryset = Producto.objects.filter(
                    productor=productor_pk
                )

    Or for older versions of Python that does not implement more advanced parameter unpacking, one can implement it like:

    class RepartoForm(forms.Form):
        productos = forms.ModelMultipleChoiceField(queryset=Producto.objects.all())
    
        def __init__(self, *args, **kwargs):
            productor_pk = kwargs.pop('productor_pk', None)
            super(forms.Form, self).__init__(*args, **kwargs)
            if productor_pk is not None:
                self.fields['productos'].queryset = Producto.objects.filter(
                    productor=productor_pk
                )

    In case no product_pk is given, the queryset are all the Productos (in case you do not want that, you can alter the form, and for example by default use an empty QuerySet like Producto.objects.none()).

    Then in the view, you can construct the form with a named productor_pk parameter:

    class Productor_Private(generic.DetailView):
        model = Productor
        template_name = 'firstpage/productor_private.html'
    
        def get(self, request, pk):
            form = RepartoForm(productor_pk=pk)
            return render(request, self.template_name, {'form': form})
    
        def post(self, request, pk):
            form = RepartoForm(request.POST, productor_pk=pk)
    
            if form.is_valid():
                return render(request, self.template_name, args)

    Note: you also need to cover the case where the form is invalid: right now post will return None for that, but you should return a HTTP response for all codepaths.