Django ORM lookup syntax - python

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.

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)

Reduce the headache on database when using ModelChoiceField

please I need your help how to reduce the database call when using ModelChoiceField as it requires a queryset and I have to use it three times separately with a model that is recursively foreign key on itself, the code is below:
ModelForm code in the init function
self.fields['category'] = forms.ModelChoiceField(queryset=queryset)
self.fields['super_category'] = forms.ModelChoiceField(queryset=)
self.fields['product_type'] = forms.ModelChoiceField(queryset=)
the model class:
class Category(ProjectBaseModel, AuditLogMixin):
parent_id = models.ForeignKey('self', related_name='children', blank=True, null=True, on_delete=models.CASCADE,verbose_name=_('Parent'))
what i tried to do is collect all ids of the desired categories in array and make only one filter queryset with them like the following:
category = auction.category
super_category = category.parent_id
product_type = super_category.parent_id
ids= [category.id,super_category.id,product_type.id]
queryset = Category.objects.filter(id__in=ids)
How to proceed with that solution

Get '_set' for a many-to-many relationship with a through

I've got a user model like this:
class Person(AbstractUser):
id = models.AutoField(primary_key=True)
...(additional attributes taken out for brevity)...
kids = models.ManyToManyField(
'Person', through='Relationship', related_name='parents')
and a Relationship model that looks like this:
class Relationship(models.Model):
parent_id = models.IntegerField()
kid_id = models.IntegerField()
class Meta:
unique_together = ('parent_id', 'kid_id')
I'm trying to figure out the best way to get a set of the kids related to a particular parent (who would be the ones logged in).
I've got something like this:
user = Person.objects.get(id=request.user.id)
print(user.relationship_set.all())
But that gives me an error 'Person' object has no attribute 'relationship_set'
How best would I accomplish this?
I ended up going a slightly different route and using this filter:
Person.objects.filter(id__in=[ r.kid_id for r in Relationship.objects.filter( parent_id=person_id ) ]

Reverse lookup 'QuerySet' object has no attribute

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)

Categories