I spent over two days looking in docs and internet and cannont find solution. I have models:
class Invoice(models.Model):
(...)
class Product(models.Model):
(...)
class InvoicedItems(models.Model):
invoice = models.ForeignKey(Invoice, on_delete=CASCADE)
article = models.ForeignKey(Product, on_delete=CASCADE)
How to get list of all invoices containing one product? I want to make search engine.
I tried to define in InvoicedItems:
def get_invoices_by_article(article):
inv_obj = InvoicedItems.objects.filter(article=article)
inv = inv_obj.invoice
return inv
But all the time I get error:
'QuerySet' object has no attribute 'invoice'
I know that I am close but I need your help. Thanks in advance!
Your problem is this: 'QuerySet' object has no attribute 'invoice'
When you do any .filter()
call it always returns a QuerySet Obj.. or a fancy list/array
Outputting in python manage.py shell
demonstrates this:
inv_obj = InvoicedItems.objects.filter(article=article_obj)
print(inv_obj)
# <QuerySet [<Article Object (1)>]>
# ^ Not an Article Object
So your method will work with some minor changes (and a variable name change)
def get_invoices_by_article(article):
inv_list = InvoicedItems.objects.filter(article=article)
inv = inv_list.first().invoice
return inv
but! this raises 2 major issues:
Example Solutions:
def get_invoices_by_article(article):
inv_list = InvoicedItems.objects.filter(article=article)
count = inv_list.count()
if count == 0:
return None
if count > 1:
# return list of invoices
return [i.invoice for i in inv_list]
# implied: is 1
# return single invoice
return inv_list.first().invoice
# Maybe it's easier to ALWAYS return a List, so it's always a single type!
# empty or not!
def get_invoices_by_article(article):
return [i.invoice for i in InvoicedItems.objects.filter(article=article)]
After you figure out which path you want to take with the method, I'd recommend making it into a Model Manager function instead.
Current way:
invoice_item = InvoicedItems.objects.all().first()
my_return = invoice_item.get_invoices_by_article(article_obj)
You must fetch an InvoiceItem Obj before you can use it -> Extra work!
Model Manager way:
my_return = InvoicedItems.objects.get_invoices_by_article(article_obj)
You cut out that db hit!
from django.db import models
class InvoicedItemsManager(models.Manager):
def get_invoices_by_article(article):
return [i.invoice for i in InvoicedItems.objects.filter(article=article)]
class InvoicedItems(models.Model):
invoice = models.ForeignKey(Invoice, on_delete=CASCADE)
article = models.ForeignKey(Product, on_delete=CASCADE)
objects = InvoicedItemsManager()
Simple as that! Now you can add any number of methods you want for InvoicedItems.objects.
But maybe a Model Method or a Manager Method is overkill- idk! It's up to you