Reventlow
Reventlow

Reputation: 167

Django how to generate a filtered queryset

I have got 2 models. Asset and Asset_Type. In my asset_type detail view i would like to list all assets of that asset type. I think I have to use models.Asset.queryset().filter() but i can't get it to work.

On my template I would like to loop though the list with a 'for' (example: object in list) and print the values like this {{ object.name }}

models.py

    class Asset(models.Model):
    
        # Relationships
        room = models.ForeignKey("asset_app.Room", on_delete=models.SET_NULL, blank=True, null=True)
        model_hardware = models.ForeignKey("asset_app.Model_hardware", on_delete=models.SET_NULL, blank=True, null=True)
    
        # Fields
        name = models.CharField(max_length=30)
        serial = models.CharField(max_length=30, unique=True, blank=True, null=True, default=None)
        mac_address = models.CharField(max_length=30, null=True, blank=True)
        purchased_date = models.DateField(null=True, blank=True)
        may_be_loaned = models.BooleanField(default=False, blank=True, null=True)
        notes = models.TextField(max_length=448, null=True, blank=True)
        ip = models.CharField(max_length=90, null=True, blank=True)
        created = models.DateTimeField(auto_now_add=True, editable=False)
        last_updated = models.DateTimeField(auto_now=True, editable=False)
    
        class Meta:
            ordering = ["name"]
    
        def __str__(self):
            return str(self.name)
    
        def get_absolute_url(self):
            return reverse("asset_app_asset_detail", args=(self.pk,))
    
        def get_update_url(self):
            return reverse("asset_app_asset_update", args=(self.pk,))
    
    
    class Asset_type(models.Model):
    
        # Fields
        name = models.CharField(max_length=30)
        last_updated = models.DateTimeField(auto_now=True, editable=False)
        created = models.DateTimeField(auto_now_add=True, editable=False)
        notes = models.TextField(max_length=448, null=True, blank=True)
    
        class Meta:
            ordering = ["name"]
    
        def __str__(self):
            return str(self.name)
    
        def get_absolute_url(self):
            return reverse("asset_app_asset_type_detail", args=(self.pk,))
    
        def get_update_url(self):
            return reverse("asset_app_asset_type_update", args=(self.pk,))

class Model_hardware(models.Model):

    # Relationships
    asset_type = models.ForeignKey("asset_app.Asset_type", on_delete=models.SET_NULL, blank=True, null=True)
    brand = models.ForeignKey("asset_app.Brand", on_delete=models.SET_NULL, blank=True, null=True)

    # Fields
    name = models.CharField(max_length=30)
    notes = models.TextField(max_length=448, null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True, editable=False)
    last_updated = models.DateTimeField(auto_now=True, editable=False)

    class Meta:
        ordering = ["name"]

    def __str__(self):
        return str(self.name) + " :: " + str(self.brand.name) + " :: " + self.asset_type.name

    def get_absolute_url(self):
        return reverse("asset_app_model_hardware_detail", args=(self.pk,))

    def get_update_url(self):
        return reverse("asset_app_model_hardware_update", args=(self.pk,))

views.py

class Asset_typeDetailView(generic.DetailView):
    model = models.Asset_type
    form_class = forms.Asset_typeForm

Upvotes: 1

Views: 125

Answers (1)

Abdul Aziz Barkat
Abdul Aziz Barkat

Reputation: 21812

You can simply iterate over the related objects in the template by using the default related name which is the model name in lowercase with _set appended. So asset_type.model_hardware_set.all() will give you all Model_hardware instances related to Asset_type and similarly for model_hardware.asset_set.all():

{% for model_hardware object.model_hardware_set.all %}
    {% for asset in model_hardware.asset_set.all %}
        {{ asset.name }}
    {% endfor %}
{% endfor %}

But this can become slow, since we run into the N + 1 problem that is for each model hardware we will be making queries to get it's assets. We can use prefetch_related_objects on your model instance to prefetch all the related objects (in fewer queries) this and make it faster:

from django.db.models import prefetch_related_objects
from django.views.generic import DetailView


class YourDetailView(DetailView):
    model = Asset_type
    template_name = '<your_template_name>.html'
    
    def get_object(self, queryset=None):
        obj = super().get_object(queryset=queryset)
        prefetch_related_objects([obj], 'model_hardware__asset')
        return obj

Note: Class names in python should ideally be in PascalCase not Some_case (Don't think there is any such convention as you make here), hence ModelHardware instead of Model_hardware and AssetType instead of Asset_type would be better names.

Upvotes: 1

Related Questions