Reputation:
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
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