Django - filtering of objects by parameters - django

There is a catalog of products, the products have properties ... want to filter products by the properties.
class Product(models.Model):
name = models.CharField(verbose_name="Название",max_length=255)
description = models.TextField(verbose_name="Описание")
category = models.ManyToManyField(Category,verbose_name=("Категория"))
class Product_Attribute(models.Model):
product = models.ForeignKey(Product)
option = models.ForeignKey(Attribute_Option)
value = models.CharField(verbose_name=("Value"), max_length=255)
class Attribute_Option(models.Model):
description = models.CharField(verbose_name=("Description"), max_length=100)
name = models.SlugField(verbose_name=("Attribute name"), max_length=100)
I want to filter for two or more attribute

Your original question is extremely ambiguous as to what properties you wish to filter the Product objects on. If you want all Product objects that relate to a specific Attribute_Option.description value, you could use:
attribute_options = Attribute_Option.objects.get(description='foo')
product_attributes = Product_Attribute.objects.select_related('Product').filter(option__in=attribute_options)
results = [p.product for p in product_attributes]
If you simply want all Product objects that have both a specific name and a specific description, you could use:
Product.objects.filter(name='foo', description='bar')
I really have no idea which properties you are trying to filter on though. If you specify that in your question, you might be able to get an answer that matches your specific use-case.

Related

Query intermediate through fields in django

I have a simple Relation model, where a user can follow a tag just like stackoverflow.
class Relation(models.Model):
user = AutoOneToOneField(User)
follows_tag = models.ManyToManyField(Tag, blank=True, null=True, through='TagRelation')
class TagRelation(models.Model):
user = models.ForeignKey(Relation, on_delete=models.CASCADE)
following_tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
pub_date = models.DateTimeField(default=timezone.now)
class Meta:
unique_together = ['user', 'following_tag']
Now, to get the results of all the tags a user is following:
kakar = CustomUser.objects.get(email="kakar#gmail.com")
tags_following = kakar.relation.follows_tag.all()
This is fine.
But, to access intermediate fields I have to go through a big list of other queries. Suppose I want to display when the user started following a tag, I will have to do something like this:
kakar = CustomUser.objects.get(email="kakar#gmail.com")
kakar_relation = Relation.objects.get(user=kakar)
t1 = kakar.relation.follows_tag.all()[0]
kakar_t1_relation = TagRelation.objects.get(user=kakar_relation, following_tag=t1)
kakar_t1_relation.pub_date
As you can see, just to get the date I have to go through so much query. Is this the only way to get intermediate values, or this can be optimized? Also, I am not sure if this model design is the way to go, so if you have any recomendation or advice I would be very grateful. Thank you.
You need to use Double underscore i.e. ( __ ) for ForeignKey lookup,
Like this :
user_tags = TagRelation.objects.filter(user__user__email="kakar#gmail.com").values("following_tag__name", "pub_date")
If you need the name of the tag, you can use following_tag__name in the query and if you need id you can use following_tag__id.
And for that you need to iterate through the result of above query set, like this:
for items in user_tags:
print items['following_tag__name']
print items['pub_date']
One more thing,The key word values will return a list of dictionaries and you can iterate it through above method and if you are using values_list in the place of values, it will return a list of tuples. Read further from here .

Django Accessing the attributes of an M2M field by querying the related table

I have a table which looks like this
class Person(User):
"""
This model represents person's personal and
professional details.
"""
attribute1 = models.CharField(max_length=100)
attribute2 = models.TextField()
attribute3 = models.TextField()
attribute4 = models.ForeignKey(Receptionist)
referred_attribute1 = models.ManyToManyField(Hobby)
class Hobby(models.Model):
name = models.CharField(max_length=100)
Use case:
A Person P1 has Hobbies h1,h2,h3(defined[as 3 separate entries] in table Hobby).
Now I want to retrieve a Person object with all the attributes and properties that it has including Hobbies. For that I'm executing the following :
Person.objects.values("attribute1",
"attribute2",
"referred_attribute1").get(attribute3="p1's attribute")
What I want is :
{'attribute1':'p1_attribute1',
'attribute2':'p1_attribute2',
'referred_attribute':['h1','h2','h3']}
and what I get is:
[{'attribute1':'p1_attribute1',
'attribute2':'p1_attribute2',
'referred_attribute':'h1'},
{'attribute1':'p1_attribute1',
'attribute2':'p1_attribute2',
'referred_attribute':'h2'},
{'attribute1':'p1_attribute1',
'attribute2':'p1_attribute2',
'referred_attribute':'h3'}]
Is there a way to get my desired result directly from a queryset? As I do not want to manually re-arrange the above result!
Do this instead:
p1 = Person.objects.get(attribute3="p1's attribute")
p1 is the instance of the person, you can then retrieve all your attributes from it. eg:
p1_attribute1 = p1.attribute1
p1_hobbies = p1.referred_attribute1.all()
Now you can rearrange that data however you like, for example, making a list of the hobbies as follows:
hobby_list = p1_hobbies.values_list('name', flat=True)
I would rename referred_attribute1 to hobbies for code readability
Something like this should work in this case:
[{'attribute1': res.attribute1, 'attribute2': res.attribute2,
'referred_attribute1': [a.name for a in res.referred_attribute1.all()]}
for res in Person.objects.prefetch_related('referred_attribute1').filter(attribute3="p1's attribute")]
Also see Django's Prefetch-related

Access foreign key objects in Django

I have two models:
class Item(models.Model):
name = models.CharField()
class Param(models.Model):
product = models.ForeignKey(Item, related_name="param")
height = models.IntegerField()
price = models.IntegerField()
I want is to annotate item with average price, but only keep params with height > 60.
items = Item.objects.filter(param__height__gt=60).annotate(price=Avg('param__price'))
That's where I have problems. I want to fetch filtered Param objects from above query.
Is it possible to do?
I know that there is workaround:
for item in items:
item.params = item.param.filter(height__gt=60)
But there are a lot of additional queries.
So, my question whether can I access filtered param objects from items?
items = Item.objects.select_related('param').filter(param__height__gt=60).annotate(price=Avg('param__price')).all()
class Artist():
blah = models.TextField()
class Album()
blah = models.ForeignKey(blah)
the key is to be shaed in the class like this^
Also for example the key can be x,y points, time, user, or whatever you want!
https://github.com/Ry10p/django-Plugis/blob/master/courses/models.py
example line 52
-Cheers

Embed product-variance logic into Django models

I wonder how I would model my Products model to auto-create (and that the admin-App would also understand it) variants of a Product based on it's variant-parts.
My Products have;
Colors
Sizes
and can probably get more features in the future.
How would I model my Product class to generate all variants of the Product?
Say I would create a new Product in Colors Red Blue Green and in Sizes XS S M L XL.
class Product(models.Model):
name = models.CharField(max_length=200)
class Color(models.Model):
product = models.ForeignKey(Product)
name = models.CharField(max_length=200)
class Size(models.Model):
product = models.ForeignKey(Product)
name = models.CharField(max_length=200)
class FutureVariant(models.Model):
product = models.ForeignKey(Product)
name = models.CharField(max_length=200)
# etc.
Now when I would need a smart method that when I would auto-create all color-size-[FUTURE VARIANT] for that product.
So I would tell Django;
Create new Product
In the colors Red Blue Green
In the sizes XS S M L XL
And the Product class would go and produce Products with all possible combinations in the products_product table.
I'm almost sure that this has design flaws. But I'm just curious how to put this logic in the ORM, and not to write weird procedural code, which would probably go against the DRY principal.
In Database logic I would think of something like this;
PRODUCTS
- id
- name
PRODUCTS_VARIANTS_COLORS
- id
- name
- html_code
PRODUCTS_VARIANTS_SIZES
- id
- name
PRODUCTS_VARIANTS_TABLES
- table_name
- table_id
PRODUCTS_VARIANTS
- product_id
- variant_table
- variant_id
This way I could make endless variant tables, as long as I would register them in my PRODUCTS_VARIANTS_TABLES and store their name as relevant. PRODUCTS_VARIANTS would hold all the the variants of the product, including combinations of them all. I am also aiming to have a selection-phase where the user can chose (in a HTML checkbox-list) which variants it does and doesn't want.
The problem (I think) is that this would not really comply with a logic in the ORM.
I don't know if you are asking about alternatives or just looking to make your way work, but what about splitting a product from it's attributes?
So instead of having separate models for attributes, you just have an Attribute model. This way you are future-proofing your database so you can easily add more attributes (like if you have products with a height and width instead of just color or size).
class AttributeBase(models.Model):
label = models.CharField(max_length=255) # e.g. color, size, shape, etc.
...
class Attribute(models.Model):
base = models.ForeignKey('AttributeBase', related_name='attributes')
value = models.CharField(max_length=255) # e.g. red, L, round, etc.
internal_value = models.CharField(max_length=255, null=True, blank=True) # other values you may need e.g. #ff0000, etc.
...
class ProductAttribute(Attribute):
product = models.ForeignKey('Product', related_name='attributes')
It now becomes very easy to create all attributes for a product...
class Product(models.Model):
...
def add_all_attributes(self):
for attribute in Attribute.objects.all():
self.attributes.add(attribute)
now when you use product.add_all_attributes() that product will contain every attribute. AND you can even make it add attributes of a certain AttributeBase
def add_all_attributes_for_base(self, label):
base = AttributeBase.objects.get(label=label)
for attribute in base.attributes.all():
self.attributes.add(attribute)
You could write something as:
class Product(models.Model):
#classmethod
def create_variants(cls):
# compute all possible combinations
combinations = ...
for combination in combinations:
Product.objects.create(**combination)
Creating all the combinations would indeed happen through registering the possible variants and their possible values.
Note that ORM is there to help you map Django objects to database records, it doesn't help you with producing the database records (read: Django models) that you wish to save.

Django Query Set in Deep with exclude

i have three classes
Product have many Descriptions and each model have many stores
what i want to do
select all products but store.qty value > 0
I've tried
pr = Product.objects.all().exclude(Product__Product_description__qty > 0)
how can i do that ?
class Product
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
class Product_description
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
product = models.ForeignKey(Product)
class Store
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
desc = models.ForeignKey(Product_description)
qty = models.IntegerField()
pr = Product.objects.filter(Product_description__qty__lte = 0)
Or if you really must use exclude:
pr = Product.objects.exclude(Product_description__qty__gt = 0)
all() is not necessary in either case; you just end up building an untriggered proxy that goes into building the filter/exclude queryset afterward. It wastes memory and CPU, but otherwise does nothing. Only the .delete() operator requires a working all() queryset, but it's a special case designed explicitly to avoid the accidental destruction of datasets.
The Django Queryset API documentation is very readable.
Django convention is to name your class ProductDescription.
This seems like a backward hierarchy. Why would stores have "product descriptions?" Isn't that metadata on the product itself, and what you care about is that the stores have a certain quantity of product? Or are these product variants, i.e you want to find all the products for which stores have at least one green or blue or orange one? Something tells me that your project needs a careful re-think.