My models:
class Product(models.Model):
name = models.CharField(max_length=125, blank=False, null=False)
description = models.CharField(max_length=255, blank=True)
abbreviation = models.CharField(max_length=15, blank=True)
category = models.ForeignKey('inventory.Category',
on_delete=models.SET_NULL, null=True, blank=True)
metric = models.CharField(max_length=15, blank=True, null=True)
class Category(models.Model):
name = models.CharField(max_length=55, blank=False, null=False)
abbreviation = models.CharField(max_length=15, blank=True)
parent_category = models.ForeignKey('inventory.Category',
on_delete=models.SET_NULL, null=True, blank=True)
I want to recover all products:
products = Product.objects.all()
That's ok. I get all the products with success.
But I want that the products returned came with the ForeignKey attribute as an textual field from the referenced model, and no the raw integer that references it.
The field in question would be the Category.name.
At present my return is:
{model: product, pk:1, fields: {..., category: 1} }, { ....... } ...
What I Want:
{model: product, pk:1, fields: {..., category: 'Categoria 1 '}
I need the set pre-populated with the related model info, cause I'll return a JSON string as result for the service caller.
My attempt to return JSON:
products = Product.objects.all()
serialized_products = serializers.serialize('json', products)
return JsonResponse({'result': 'success', 'data':serialized_products}) #return all
How can I do this? The most simple and elegant form...
The only thing I can think of is use values_list to return whatever fields you want:
products = Product.objects.all().values('name',
'description',
'category__name',
'category__id',
'category__abbreviation')
return JsonResponse(products)
It might be a little less convenient then serializer, but I don't think serializer is good at handling foreigne key relations because what if you have deeper relationships that you need to include in the result?
Related
I have two models (Product & Category) which every product has a linked category.
I have installed DjangoFilterBackend which the hope of filtering on the category field to return a list of products in that category.
However, whenever I send the query in Postman. I receive the error Select a valid choice. That choice is not one of the available choices..
I have tried filtering on another field in my product model (name for an example) and that works fine. So i'm not sure if i'm missing something for category to work.
Product/View.py:
class ProductView(ListAPIView):
serializer_class = ProductSerializer
queryset = Product.objects.all()
filter_backends = [DjangoFilterBackend]
filterset_fields = ('category', 'name')
Products/Models.py:
class Product(models.Model):
name = models.CharField(max_length=250, unique=True, blank=False)
photo = models.ImageField(upload_to=product_photo_path)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
quantity = models.IntegerField()
description = models.TextField(blank=False)
price = models.DecimalField(max_digits=6, decimal_places=2)
in_stock = models.BooleanField(default=False)
trending = models.BooleanField(default=False)
def __str__(self):
return self.name
Products/serializers.py
class ProductSerializer(serializers.ModelSerializer):
category = serializers.CharField(source='category.name', read_only=True)
class Meta:
model = Product
fields = ('category', 'name', 'photo', 'quantity', 'description', 'price', 'in_stock', 'trending')
The query I am using is a GET request to:
http://127.0.0.1:8000/api/products?category=xxxx - I am sending no payload. The response I am receiving is a `400 Bad Request` and the exact error is:
{
"category": [
"Select a valid choice. That choice is not one of the available choices."
]
}
Ah-ha!
I changed the model to:
class Product(models.Model):
name = models.CharField(max_length=250, unique=True, blank=False)
photo = models.ImageField(upload_to=product_photo_path)
**category = models.ForeignKey(Category, to_field='name', on_delete=models.CASCADE)**
quantity = models.IntegerField()
description = models.TextField(blank=False)
price = models.DecimalField(max_digits=6, decimal_places=2)
in_stock = models.BooleanField(default=False)
trending = models.BooleanField(default=False)
And now it works!
Well, I am not sure, but try to filter on field category_id, this field is created automatically for FK fields
Just in case somebody will ever need the answer, to be able to use the name of a foreign field to filter instead of the primary key use a double underscore i.e category__name in this case. Note that name in this case is the field of the foreign model that you want to filter with and you can replace it with your field accordingly.
I have two models Category and Product
class Category(models.Model):
name = models.CharField(max_length=200,
db_index=True)
slug = models.SlugField(max_length=200,
unique=True,)
description = models.CharField(max_length=50,
blank=True)
class Product(models.Model):
category = models.ForeignKey(Category,
related_name='products',
on_delete=models.CASCADE)
name = models.CharField(max_length=200,
db_index=True)
description = models.TextField(blank=True)
slug = models.SlugField(max_length=200, db_index=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
featured = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
the wanted behavior is to return all categories with and each category should include 10 products
I tried to return all categories without limiting the products returned objects then I used the slice filter in the templates, but I am not sure if this is efficient for big scale and I am not sure if Django will lazy-load the products.
right now the view is as follows
def product_list(request):
featured_products = Product.objects.filter(featured=True).all()
categories = Category.objects.all()
return render(request,
template_name='home/home.html',
context={'categories': categories,
'featured_products': featured_products})
is the way I am using is efficient or I should limit the products when querying the categories using Category.objects.all()?
Yes, that's an usual approach. Django's querysets are lazy and database won't be accessed until you slice the result in your template and that slicing would go - kind of - to the level of database.
I'm struggling getting the right query for my project. Here is an example of my model :
from django.db import models
class Brand(models.Model):
name = models.CharField(max_length=30)
handle = models.SlugField(max_length=30, unique=True, null=True, blank=True)
def __unicode__(self):
return self.name
class Product(models.Model):
product_id = models.SlugField(unique=True)
brand = models.ForeignKey(Brand, on_delete=models.SET_NULL, blank=True, null=True)
collections = models.ManyToManyField('Collection')
def __unicode__(self):
return self.product_id
I am trying to get all the Brand_name based on the value of the Product.collection
Eg: Shoes is a Product.collection, I want to get all the Brand_name for collection - shoes
I tried __ method also. Somehow it is not working.
You should try something like this:
Brand.objects.product_set.filter(collections__name="Shoes").values('brand__name')
Brand.objects.filter(
product__collection=product.collection
).values_list('name', flat=True).distinct()
product is instance of Product.
Add a related_name to your foreign key like below,
brand = models.ForeignKey(Brand, on_delete=models.SET_NULL, blank=True,
null=True, related_name='products')
And then you can try below query and it'll return you a list of all the Brand.name which has product__collections_name='Shoes'.
Brand.objects.filter(products__collections__name='Shoes').values_list('name', flat=True)
I'm struggling getting the right query for my project. Here is an example of my model :
from django.db import models
class Product_Category(models.Model):
name = models.CharField(max_length=30, unique=True)
handle = models.SlugField(max_length=30, unique=True, null=True, blank=True)
collection = models.CharField(max_length=30, null=True)
def __unicode__(self):
return self.name
class Product(models.Model):
product_id = models.SlugField(unique=True)
name = models.CharField(max_length=100)
collections = models.ManyToManyField('Collection')
category = models.ForeignKey(Product_Category, on_delete=models.SET_NULL, blank=True, null=True)
def __unicode__(self):
return self.product_id
I am trying to get all the product_id based on the value of the Product_Category.collection.
Eg: books is a Product_category.collection, I want to get all the products for books collection.
I tried __ method also. Somehow it is not working.
The double underscore is the way to go.
books = Product.objects.filter(category__collection='books')
I know there are a bunch of questions addressing this issue, but I haven't solved it out yet. I'm using DRF for the first time and I'm working with nested serializers. My Restaurant serializer points to a Category serializer via a slug related field as it shows below
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = (
'name',
'description'
)
class RestaurantSerializer(serializers.HyperlinkedModelSerializer):
category = serializers.SlugRelatedField(
many=True,
read_only=False,
queryset=Category.objects.all(),
slug_field='name'
)
class Meta:
model = Restaurant
fields = (
'id',
'name',
'description',
'website',
'twitter',
'facebook',
'instagram',
'category'
)
I send all the necessary data to my endpoint to create a new Restaurant via jquery but I keep getting "Cannot assign "[< Category: Italian food >]": "Restaurant.category" must be a "Category" instance."
I understand I need to assign a Category object to Restaurant's category, although I don't know how to access my queryset to extract the object that matters.
Any advices on this?
Edit: This is the data I'm sending from jquery to my endpoint
{"name":"Restaurant","email":"restaurant#gmail.com","password":"1234","category":["Italian food"],"description":"Description test"}
Edit # 2 See the model definitions below
class Restaurant(models.Model):
name = models.CharField(max_length=80, null=False)
description = models.TextField(max_length=300, null=False)
email = models.EmailField(max_length=80, null=True)
password = models.CharField(max_length=60, null=False)
website = models.URLField(max_length=80, null=True)
twitter = models.CharField(max_length=60, null=True, blank=True)
facebook = models.CharField(max_length=60, null=True, blank=True)
instagram = models.CharField(max_length=60, null=True, blank=True)
category = models.ForeignKey('Category')
def __unicode__(self):
return self.name
class Category(models.Model):
name = models.CharField(max_length=80, null=False)
description = models.TextField(max_length=100, null=False)
def __unicode__(self):
return self.name
You have a ForeignKey from Restaurant to Category. That means there is only one Category for each Restaurant. But you are sending a list of category slugs, and you have many=True in the definition of the SlugRelatedField.
Your data should just be {..."category": "Italian food"...}, and you should remove the many=True.