KareemElashmawy
KareemElashmawy

Reputation: 251

Django foreign key select field empty

I've been working on a small django website as I learn django. According to the documentation when you create a form class with a meta class that points at a model with foreign key fields, it'll render those fields as select inputs.

In my application I have 3 models, client test, and record where record carries two foreign keys, each of whom point to client and test respectively

Models.py

class Client(models.Model):
    first = models.CharField(max_length=264)
    last = models.CharField(max_length=264)
    DOB = models.DateField()

    def __str__(self):
        return self.first + " " + self.last


class Test(models.Model):
    test = models.CharField(max_length=264)
    fee = models.DecimalField(max_digits=12, decimal_places=2)

    def __str__(self):
        return self.test


class Record(models.Model):
    client = models.ForeignKey(Client, on_delete=models.CASCADE)
    test = models.ForeignKey(Test, on_delete=models.CASCADE)
    date = models.DateField()

    def __str__(self):
        return str(self.date) + " " + str(self.test) + " for " + str(self.client)

Form.py

class NewLabRecord(forms.ModelForm):
    client = forms.ChoiceField(
        label='Client ID',
        widget=forms.Select(
            attrs={'class': 'form-control'}))
    test = forms.ChoiceField(
        label='Test ID',
        widget=forms.Select(
            attrs={'class': 'form-control'}))
    date = forms.DateField(
        label='Test Date',
        widget=forms.DateInput(
            attrs={'class': 'form-control'}))

    class Meta:
        model = models.Record
        fields = '__all__'

I render NewLabRecord at the top of my index view for records. The idea is to create a record and redirect back to the page (therefore seeing it in the list of records). Presently, I'm emulating class-based-views and not actually implementing it because I have not learned it yet. Nevertheless, this pattern does work for my client and test (the code is nearly identical).

Views.py

class LabRecord:
    @staticmethod
    def index(request):
        new_record_form = forms.NewLabRecord
        records = models.Record.objects.order_by('date')
        print(records)
        context = {
            'records': records,
            'new_record_form': new_record_form
        }
        return render(request, "layouts/lab/record/index.html", context=context)

layouts/lab/record/index.html

<div class="collapse" id="createLabRecord">
    {% include 'components/lab/record/create.html' %}
</div>

components/lab/record/create.html

<!DOCTYPE html>
{% block content %}
  <div class="card col-sm" style="">
    <form class="form" method="post" action="{% url 'lab:create lab record' %}">
      {{ new_record_form }}
      {% csrf_token %}
      <input type="submit" value="submit">
    </form>
  </div>
{% endblock %}

Now, when I go to the url for this view, /lab/records/, the view renders two select fields and an input for the date; however, the select fields are empty! Note: I have 9 clients and 4 tests in the database!

Why is the view generating empty select fields for the foreign key fields?

Upvotes: 1

Views: 1686

Answers (1)

Alan Hoover
Alan Hoover

Reputation: 1450

In your view, you need to query your Client and Test models and put those lists into your context to make them available to your form/template.

    records = models.Record.objects.order_by('date')
    clients = models.Client.objects.all()
    tests = models.Test.objects.all()
    context = {
        'records': records,
        'new_record_form': new_record_form,
        'clients' : clients,
        'tests' : tests,
    }

I have not learned the forms portion of django yet to tell you if there is something else to connect the lists to the input select fields.

Edit: It looks like the following in your form should accomplish the desired:

class NewLabRecord(forms.ModelForm):
    client = forms.ModelChoiceField(models.Client.objects.all())
    test = forms.ModelChoiceField(models.Test.objects.all())
    date = forms.DateField(
        label='Test Date',
        widget=forms.DateInput(
            attrs={'class': 'form-control'}))

    class Meta:
        model = models.Record
        fields = '__all__'

And I don't think that the changes to your view are then necessary.

Upvotes: 2

Related Questions