Filter fields queryset in Django ModelChoiceField with external reference keys

130 Views Asked by At

The Trim inherited the Car and the TrimType, and the TrimType inherited the Car.

When creating Trim on a specific Car detail page, I want to filter the queryset of trimType field by the id of the Car.

from All trimType select options

to trimType selection options for Car1

I referred to How to use the request in a ModelForm in Django.

My templates/brand/trim_update.html is:

# trim_update.html

{% extends 'base.html' %}
{% block content %}
<div class="container">
    <h5 class="my-3 border-bottom pb-2">New Trim</h5>
    <form method="post" class="post-form my-3" enctype="multipart/form-data">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit" class="btn btn-primary">Save</button>
    </form>
</div>
{% endblock %}

and brand/models.py is:

# models.py

class Car(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=200, null=False, blank=False)
    image = models.ImageField(default=None, null=True, upload_to='car/%Y/%m/%d/')
    order = models.IntegerField(default=0)

    def __str__(self):
        return self.name

class TrimType(models.Model):
    car = models.ForeignKey(Car, on_delete=models.SET_NULL, blank=True, null=True)
    typeName = models.CharField(max_length=50, blank=False, ull=False)

    def __str__(self):
        return self.typeName

class Trim(models.Model):
    id = models.AutoField(primary_key=True)
    order = models.IntegerField(default=0)
    trimType = models.ForeignKey(TrimType, on_delete=models.SET_NULL, null=True, blank=True)
    car = models.ForeignKey('Car', on_delete=models.SET_NULL, null=True)
    name = models.CharField(max_length=256, default='')
    price = models.IntegerField(default=0)
    image = models.ImageField(default=None, blank=True, null=True, upload_to='trim/%Y/%m/%d/')

    def __str__(self):
        if self is not None and self.car is not None:
            return self.car.name +'-'+self.name
        else: return ''

and brand/views/base_views.py is:

# base_views.py

def trim_create(request, car_id):
    if request.method == 'POST':
    car = get_object_or_404(Car, pk=car_id)
        form = TrimForm(request.POST, request.FILES, request=request)
        form.fields['trimType'].queryset = TrimType.objects.filter(car_id=car_id)
        if form.is_valid():
            trim = form.save(commit=False)
            trim.car = car
            trim.image = form.cleaned_data['image']
            trim.save()
            return redirect('brand:car_detail', car_id=car_id)
    else:
        form = TrimForm()

    context = {'form': form}
    return render(request, 'brand/trim_update.html', context)

and brand/forms/CarForms.py is:

# CarForms.py

class TrimForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super(TrimForm, self).__init__(*args, **kwargs)
        self.fields['trimType'].queryset = TrimType.objects.filter(car=self.request.car)

    class Meta:
        model = Trim
        fields = ('id', 'name', 'price', 'order', 'trimType', 'image')
        image = forms.ImageField()

    name = forms.CharField(required=True)
    trimType = forms.ModelChoiceField(queryset=TrimType.objects.all())
    price = forms.IntegerField(required=True)
    order = forms.IntegerField(required=True)

But, The following error occurs in CarForms.py:

self.request = kwargs.pop('request')

KeyError: 'request'

1

There are 1 best solutions below

0
Kholdarbekov On

I think you don't need to filter TrimType queryset in your TrimForm, because you are doing exact same thing in your trim_create view. Try this:

# base_views.py

def trim_create(request, car_id):
    if request.method == 'POST':
    car = get_object_or_404(Car, pk=car_id)
        form = TrimForm(request.POST, request.FILES, request=request)
        form.fields['trimType'].queryset = TrimType.objects.filter(car__id=car_id)
        if form.is_valid():
            trim = form.save(commit=False)
            trim.car = car
            trim.image = form.cleaned_data['image']
            trim.save()
            return redirect('brand:car_detail', car_id=car_id)
    else:
        form = TrimForm()

    context = {'form': form}
    return render(request, 'brand/trim_update.html', context)



# CarForms.py

class TrimForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(TrimForm, self).__init__(*args, **kwargs)

    class Meta:
        model = Trim
        fields = ('id', 'name', 'price', 'order', 'trimType', 'image')
        image = forms.ImageField()

    name = forms.CharField(required=True)
    trimType = forms.ModelChoiceField(queryset=TrimType.objects.all())
    price = forms.IntegerField(required=True)
    order = forms.IntegerField(required=True)