user585936
user585936

Reputation:

Linking information in views with particular records in the HTML page

I have three models:

Cars (containing records of the cars):

name = models.CharField(max_length=255)

Sample records:

Features (containing a list with different features that may be of interest):

name = models.CharField(max_length=255)
primary_feature = models.BooleanField()

Sample records:

CarFeatures (containing the features and their values):

car = models.ForeignKey(Car, on_delete=models.CASCADE)
feature = models.ForeignKey(Feature, on_delete=models.CASCADE)
value = models.CharField(max_length=255)

Sample records:

Car   | Feature | Value
==========================
Car A | color   | green
Car A | owner   | Alice
Car B | color   | blue
Car C | windows | 6

You may wonder about the structure of these models but I'm simplifying here. So let's assume these models make sense.

Now the question is: I want to pull up a list with all cars, and I then want to show particular features (those that are primary features) in the table. So my end goal is to get this:

Car   |   Color   |    Owner
=============================
Car A | green     | Alice
Car B | blue      | unknown
Car C | unknown   | unknown

It is important to note that not all features will be available for all cars.

The question is: if I want to get a list with all cars, and only particular features, what is the right way to go about it?

In the view I have this:

cars = Car.objects.all()
features = Feature.objects.filter(primary_feature=True)
car_features = CarFeature.objects.filter(feature__primary_feature=True)

On the HTML page I then do this:

<table>
<tr>
 <th>Car</th>
{% foreach feature in features %}
 <th>{{ feature.name }}</th>
{% endforeach %}
</tr>

{% foreach car in cars %}
  <tr>
    <td>{{ car.name }}</td>
    {% foreach feature in features %}
      <td>??? How do I get the right value here??</td>
    {% endforeach %}
  </tr>
{% endforeach %}
</table>

This is what I get:

Car   |   Color   |    Owner
=============================
Car A | ???       | ???
Car B | ???       | ???
Car C | ???       | ???

How should I approach this?

Upvotes: 0

Views: 63

Answers (1)

markwalker_
markwalker_

Reputation: 12869

If you've not come across it yet, you may find the ListView useful for displaying objects in a table. For example you could do;

CarsList(ListView):
    model = Car

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['features'] = Feature.objects.filter(
            primary_feature=True
        )
        return context

Then in your template;

<table>
    <tr>
        <th>Car</th>
        {% for feature in features %}
            <th>{{ feature.name }}</th>
        {% endfor %}
    </tr>

{% for car in object_list %}
    <tr>
        <td>{{ car.name }}</td>
        {% for car_feature in car.carfeatures_set.all %}
            <td>{{ car_feature.feature }}</td>
        {% endfor %}
    </tr>
{% endfor %}
</table>

Now you've asked how to get all cars for a particular feature. That would involve a reverse lookup on the CarFeature objects which relate to the Feature you want.

For example;

special_feature = Features.objects.get(name='Special')

car_features = special_feature.carfeatures_set.all()

# Then all cars with `special_feature` is
special_cars = car_features.values('car')

Or in a template;

{% for car_feature in car_features %}
    {{ car_feature.car.name }}
{% endfor %}

You might find it beneficial to also read through the Many to One relationship docs; https://docs.djangoproject.com/en/2.0/topics/db/examples/many_to_one/

To show all the cars, with associated primary features you'd need to query the CarFeatures table similar to;

def get_context_data(self, **kwargs):
    context = super(CarList, self).get_context_data(**kwargs)
    cars = list()
    for obj in Car.objects.all():
        cars.append(
            (obj.name,
             obj.carfeatures_set.filter(feature__primary_feature=True))
        )
    context['cars'] = cars
    return context

Which in the template would look like;

<h1>Cars</h1>
<ul>
{% for car, carfeatures in cars %}
    <li>
        {{ car }}
        <ul>
            {% for carfeature in carfeatures %}
                <li>{{ carfeature.feature.name }}</li>
            {% endfor %}
        </ul>
    </li>
{% endfor %}
</ul>

Upvotes: 0

Related Questions