Reverse lookup 'QuerySet' object has no attribute - python

I have the following Models where I define a trucking company and their insurance company.
Models.py:
class Truckers(models.Model):
DOT_Number = models.IntegerField(primary_key=True)
Address = models.CharField( max_length=200)
class CaliIns_FK(models.Model):
DOTNmb = models.ForeignKey(Truckers, on_delete=models.CASCADE)
CoName = models.CharField(max_length=20)
There are many truckers and not as many insurance companies. I am trying to get a list of every Trucker that has insurance.
I tried the following as per django:
truck = Truckers.objects.all()
filtered = truck.caliinsfk_set.filter(truckers__isnull=True)
and
filtered = truck.caliins_fk_set.filter(truckers__isnull=True)
getting error:
AttributeError: 'QuerySet' object has no attribute 'caliinsfk_set'

Given you want Truckers objects with no related CaliIns_FK, you can .filter() your Trucks:
Truckers.objects.filter(caliins_fk__isnull=True)
or given the ForeignKey has a related_name, it should be:
Truckers.objects.filter(myrelatedname__isnull=True)
(with myrelatedname the value in related_name for the ForeignKey)
Here we thus obtain a QuerySet for which there is no related CaliIns_FK object.
This results in a query with a LEFT OUTER JOIN so if the "set" of related CaliIns_FKs is empty, it will have a row with NULL:
SELECT truckers.*
FROM truckers
LEFT OUTER JOIN caliins_fk ON truckers.DOT_Number = caliins_fk.DOTNmb_id
WHERE caliins_fk.id IS NULL
Note that one typically does not uses plural names for models, or the name of a primary key in a foreign key. According to PEP-8, attributes also are written in lowercase and with underscores. A more Django-ish approach would be:
class Trucker(models.Model):
dot_number = models.IntegerField(primary_key=True)
address = models.CharField( max_length=200)
CaliInsurance(models.Model):
trucker = models.ForeignKey(Trucker, on_delete=models.CASCADE)
coname = models.CharField(max_length=20)
In that case the query thus looks like:
Trucker.objects.filter(caliinsurance__isnull=True)

Related

Not able to access related model data using foreign key in Django

models.py
class products(models.Model):
name = models.CharField(max_length=100)
sku = models.CharField(max_length=50)
vendor = models.CharField(max_length=50)
brand = models.CharField(max_length=50)
price = models.FloatField()
product_status = models.BooleanField()
quantity = models.IntegerField()
def __str__(self):
return self.name
# categories
class categories(models.Model):
category_name = models.CharField(max_length=50)
parent_id = models.IntegerField()
# product categories
class product_categories(models.Model):
product = models.ForeignKey(products, on_delete=models.CASCADE)
category = models.ForeignKey(categories, on_delete=models.CASCADE)
def __str__(self):
return self.category
I can access 'category' table data(inside django shell) using
data = products.objects.all()
data.values('product_categories__category__category_name')
output: <QuerySet [{'product_categories__category__category_name': 'xxxx'}}]>
If I put this(inside django shell)
data.product_categories.category
output: 'QuerySet' object has no attribute 'product_categories'
How do I get a queryset(can be passed to html) which includes data from "categories" table along with the data of "products" table
There are a couple of issues happening here. First, data is a queryset, which is kind of like a list of objects, even though here there's just one object in the list. What you want is to get an attribute off of the item in the list, so you need something like a data.first() to get to that object before you start dotting into its attributes.
Secondly, the way Django handles reverse FK relationships requires that you refer to the FK by the standard name of, in your case, product_categories_set, OR you set your own related_name attribute on the FK. Something like:
# product categories
class product_categories(models.Model):
product = models.ForeignKey(products, on_delete=models.CASCADE, related_name='product_categories')
category = models.ForeignKey(categories, on_delete=models.CASCADE, related_name='product_categories')
def __str__(self):
return self.category
so that you can refer to your product_categories model from both the product and categories using just data.product_categories.
Thirdly, when accessing a reverse FK relationship, just like in point (1) above, you will get a related manager, from which you can get a queryset of items. Thus, to get the category name, you need to indicate which item you want the category name for. Assuming it's just the first item for everything, it would look something like:
data = products.objects.all()
product_category = data.product_categories.all()
category_name = product_category.category.category_name
Of course once you have more data, you'll not always want to just pick the first item, so you'll need to add filtering logic into the query to make sure you get the item you're looking for.
ETA, I do agree with the comment by Jorge above - a MTM would make this a bit simpler and would, in essence, create your product_categories table for you.

django filter get parent to child

i am using django(3.1.5). and i am trying to get parent model to child model by filter query
i have model like -
class Product(models.Model):
product_name = models.CharField(max_length=255, unique=True)
is_feature = models.BooleanField(default=False)
is_approved = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
class ProductGalleryImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
product_gallery_image = models.FileField(upload_to='path')
is_feature = models.BooleanField(default=False)
i am getting data from SELECT * FROM products_product AS pp INNER JOIN products_productgalleryimage AS ppgi ON ppgi.product_id = pp.id WHERE ppgi.is_feature=1 AND pp.is_feature=1 AND is_approved=1 ORDER BY pp.created_at LIMIT 4 mysql query.
so how can i get data like this query in django filter query
Firstly you can add related_name to ProductGalleryImage for better query support like this
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='product_images')
Then your query should be like this
products=Product.objects.filter(is_approved=True, is_feature=True, product_images__is_feature=True).order_by('created_at')[:4]
You can simply loop over the other related model like so:
for product_gallery_image in product_instance.productgalleryimage_set.all():
print(product_gallery_image.product_gallery_image)
The productgalleryimage_set here is simply the related model name in lowercase with _set appended. You can change this by setting the related_name attribute on the foreign key.
Note: This will perform a query to fetch each of the product_gallery_image objects of some product instance.
If you want to get the first object only:
product_gallery_image = product_instance.productgalleryimage_set.first()
If you want to perform a join as in your example which will perform only one query you can use select_related (this will only work in forward direction for reverse direction look at prefetch_related):
product_gallery_images = ProductGalleryImage.objects.all().select_related('product')
for product_gallery_image in product_gallery_images:
print(product_gallery_image.product.product_name)
print(product_gallery_image.product_gallery_image)

Getting rid of duplicates when sorting by 2 related fields in Django

I'm having difficulties sorting data from a stock management database in a Django query. The data has to be ordered by 2 related fields, and this results in duplicates. I understand that this is expected behavior, but I can't seem to find an elegant way to get rid of those duplicates.
models.py:
class Attribute(models.Model):
name = models.CharField(max_length=100)
class ProductType(models.Model):
name = models.CharField(max_length=100)
attributes = models.ManyToManyField(Attribute, through='ProductTypesAttributes')
class ProductTypesAttributes(models.Model):
product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE)
attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
class AttributePossibleValues(models.Model):
attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
sorting_value = models.IntegerField(null=True)
class Meta:
ordering = ('sorting_value', )
class Product(models.Model):
product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE)
attributes = models.ManyToManyField(Attribute, through='ProductAttributes')
price = models.DecimalField(max_digits=8, decimal_places=2)
class ProductAttributes(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
value = models.ForeignKey(AttributePossibleValues, on_delete=models.CASCADE, null=True)
The query which is returning duplicates (views.py):
product_type = ProductType.objects.get(pk=data['product_type_id'])
queryset = Product.objects.filter(product_type=product_type)
queryset = queryset.order_by('productattributes__attribute__name','productattributes__value__sorting_value')
For example:
A specific red sweater made out of wool, is represented by a Product instance. This instance is linked to 2 Attribute instances (color and material) through a many-to-many intermediary table. This intermediary table also links the attributes to the correct values for these attributes (red and wool).
Now I want to generate a list with the prices of all sweaters. I want this list to be sorted. I want to see the cotton sweaters first, and then the sweaters made out of wool. And those cotton and wool sweaters should also be sorted by their color, so the user sees prices of the blue sweaters before those of the red sweaters.
So I use the query above, and each sweater is shown twice. This is expected: I'm trying to order a single object with respect to two different values (material and color). But I need each sweater to be shown only once.
I've tried:
I found similar questions (such as this one) in which the issue was fixed by annotating the queryset and then sorting by the annotated value. But I can't think of something to annotate my query with, that would solve my problem.
I can't use .distinct() because I'm using a mySQL database.
I've worked around this issue by manually filtering out duplicate pks. But I'm still interested in a more elegant solution.

Django ORM lookup syntax

I have a model Fruit, and fruit has orchards.
class Fruit():
orchards = models.ManyToManyField('Orchard', blank=True)
Each orchard belongs to a farm:
class Orchard():
farm = models.ForeignKey('Farm', verbose_name='Farm', null=True, blank=True, on_delete=models.PROTECT)
Every fruit is a seedling
class Seedling():
fruit = models.ForeignKey('Fruit', editable=False, on_delete=models.CASCADE)
Here is my attempt:
queryset = Seedling.objects.all().filter(fruit__orchards__in__farm=farm_id)
This gets me an error django.core.exceptions.FieldError: Related Field got invalid lookup: in
Anyone able to clear up my query? Much appreciated
You filter with:
queryset = Seedling.objects.filter(fruit__orchards__farm=farm_id)
so without the __in lookup [Django]. If you filter over a one-to-many relation, or a many-to-many relation, you filter existentially over one of the related elements.

How to create django database model that "knows" what kind of category it is?

In Django, I have the following models.py
class Product(RandomPrimaryIdModel):
title = models.CharField(max_length=20, blank=True, null=True)
price = models.CharField(max_length=20, blank=True, null=True)
condition = models.CharField(max_length=20, blank=True, null=True)
class Mattress(Product):
length = models.CharField(max_length=50)
size = models.CharField(max_length=5)
class Pillow(Product):
shape= models.CharField(max_length=50)
comfort= models.CharField(max_length=5)
The idea is that there's a "product" model and several "product_type" models. I'm trying to create a database scheme that relates the two. The end goal is so that when I given access to a primary id for an object whose product_type is unknown, I can simply query/filter that object to find out what the product_type is of the object.
I know that sounds a bit confusing, but how would I go about implementing the correct way? The current scheme (the one above) is not the correct solution I believe.
According to the docs on multi-table inheritance you can reference the lowercase name of the model. In your case to find out the "product type" you'd do something like:
product = Product.objects.get(id=12)
try:
mattress = product.mattress
is_mattress = True
except Mattress.DoesNotExist:
is_mattress = False
You could abstract this out to a helper method that would do the tests for you and return the type as a string or enum of some sort.
If you have a reference to an object, can't you use something like:
p = Product.objects.get(id=1)
class_of_p = str(p.__class__)
and then parse the resulting string
"<class 'whatever.models.Pillow'>"
to find what you need? Apologies if I'm missing something.

Categories