Search code examples
djangodjango-modelsdjango-tables2

Queryset from two models for a single table


I have a list of parts (model 1) with prices (model 2). I want to display them in a django-tables2 table to get the historic prices of a part in a table:

models.py:

class Parts(models.Model):
    name = models.CharField('Name', max_length=120, unique=True)

class Prices(models.Model):
    price = models.DecimalField("Price", decimal_places=2, max_digits=8)
    date = models.DateTimeField(default=timezone.now)
    part = models.ForeignKey(Parts, on_delete=models.CASCADE)

tables.py:

class PriceHistoryTable(django_tables2.Table):
    price = django_tables2.Column(accessor="prices_list", verbose_name="Price",)
    date = django_tables2.Column(accessor="dates_list", verbose_name="Date",)

    class Meta:
        model = Parts
        sequence = ("date",  "price",)

I tried to create the tables from two lists as I thought the docs would suggest here (list of dicts) using these methods in the models:

def dates_list(self):
    return [{"date": d.date} for d in Prices.objects.filter(part_id = self.id)]

def prices_list(self):
    return [{"price": p.price} for p in Prices.objects.filter(part_id = self.id)]

But then I end up with a table in django-tables2 that contains the complete list of dates and prices in just one row.

How would a method look that creates a queryset for the prices and one for the dates, so I can use it for django-tables2?

edit for the solution:

views.py

class PartTable(SingleTableView):
    model = Parts
    template_name = "gap/parts_detail.html"
    table_class = PartPriceHistoryTable   
    queryset = Parts.objects.annotate(date=F("prices__date"),
                                      price=F("prices__price")).order_by('price', 'date')

tables.py:

class PriceHistoryTable(django_tables2.Table):
    price = django_tables2.Column(accessor="price", verbose_name="Price",)
    date = django_tables2.Column(accessor="date", verbose_name="Date",)

    class Meta:
        model = Parts
        sequence = ("date",  "price",)  

models.py stays as above


Solution

  • You can try to annotate the value of date and price with queryset using F() expression, and access it through accessor(or not, just define the field name same as annotated variable) in the table. For example:

    # table class
    class PriceHistoryTable(django_tables2.Table):
        price = django_tables2.Column(accessor="price", verbose_name="Price",)
        date = django_tables2.Column(accessor="date", verbose_name="Date",)
    
        class Meta:
            model = Parts
            sequence = ("date",  "price",)
    
    # example implementation
    from django.db.models import F
    
    queryset = Parts.objects.annotate(date=F('prices__date'), price=F('price__price')).order_by('parts', 'date')
    table = PriceHistoryTable(queryset)
    
    for item in table.as_values():
        print(item)
    
    # view
    
    class PartTable(SingleTableView):
        template_name = "gap/parts_detail.html"
        table_class = PartPriceHistoryTable
        table_pagination = {"per_page": 10}    
        queryset = Parts.objects.annotate(date=F("prices__date"), price=F("prices__price")).order_by('price', 'date')