how to fetch data from many to many field objects? - django

I am getting stuck in many to many field objects in my model i want to retrieve the price of product from the order model objects but while fetching it gives sometime many related manager error or query set error
models.py
from django.db import models
from django.contrib import admin
from django.contrib.auth.models import User
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
slug = models.SlugField(max_length=100, db_index=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
stock = models.PositiveIntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Order(models.Model):
user = models.ForeignKey(User,on_delete= models.SET_NULL,null = True)
product = models.ManyToManyField('Order.Product')
is_ordered = models.BooleanField(default =False)
date_ordered = models.DateTimeField(auto_now = True,null = True)
views.py
def get_product_price(request):
if request.method=="GET":
user=User.objects.get(username = "hemant")
orders = user.order_set.all()
order = orders[0]
price = order.product.price
return HttpResponse(price)

When you are getting value from ManyToManyField, you need to do it like this:
products = order.product.all()
If you want to get the prices, you can do it like this:
products = order.product.all()
for product in products:
print(product.price)
If you want to return prices as a http response, then you can use values_list() to get list of prices from queryset. Like this
import json
views.py
def get_product_price(request):
if request.method=="GET":
user=User.objects.get(username = "hemant")
orders = user.order_set.all()
order = orders[0]
price = list(order.product.all().values_list('price', flat=True))
return HttpResponse(json.dumps(price))

Related

django serializer error: images_data = self.context['request'].FILES KeyError: 'request'

models.py
#
from django.db import models
from user.models import User
from chat.models import TradeChatRoom, AuctionChatRoom
class Goods(models.Model):
class Meta:
db_table = 'Goods'
ordering = ['-created_at'] # 일단 추가해뒀습니다
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sell_goods')
buyer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='buy_goods', null=True)
trade_room = models.ForeignKey(TradeChatRoom, on_delete=models.CASCADE)
auction_room = models.ForeignKey(AuctionChatRoom, on_delete=models.CASCADE)
title = models.CharField(max_length=256)
content = models.TextField()
category = models.CharField(max_length=32)
status = models.BooleanField(null=True)
predict_price = models.IntegerField()
start_price = models.IntegerField()
high_price = models.IntegerField(null=True)
start_date = models.DateField(null = True)
start_time = models.DateTimeField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
like = models.ManyToManyField(User, related_name='like_goods', null=True)
class GoodsImage(models.Model):
class Meta:
db_table = "GoodsImage"
goods = models.ForeignKey(Goods, on_delete=models.CASCADE)
image = models.ImageField(upload_to='goods/')
serializer.py
from rest_framework import serializers
from .models import Goods,GoodsImage
class GoodImageSerializer(serializers.ModelSerializer):
image = serializers.ImageField(use_url=True)
def get_image(self, obj):
image = obj.goods_set.all()
return GoodsPostSerializer(instance=image, many = True, context = self.context)
class Meta:
model = GoodsImage
field =('image',)
class GoodsPostSerializer(serializers.ModelSerializer):
image = GoodImageSerializer(many=True, read_only = True)
class Meta:
model = Goods
fields = (
'seller', 'buyer','auction_room','title','content',
'category','status','predict_price','start_price','high_price',
'trade_room','start_date','start_time','created_at','like','image',
)
read_only_fields = ("seller",)
def create(self, validated_data):
goods = Goods.objects.create(**validated_data)
images_data = self.context['request'].FILES
for image_date in images_data.getlist('image'):
GoodsImage.objects.create(goods = goods, image = image_date)
return goods
error
images_data = self.context['request'].FILES
KeyError: 'request'
I want to save multiple images, but I keep getting an error. I don't know what to do anymore.
I searched for a method and followed it, but it seems that I am the only one who gets an error.
Please help if you know how to solve this problem.
And I want to know if it is correct to put it in a list like "image":["12.jpeg,"13.jpeg] when inserting multiple images through postman.
It's hard not being able to solve this problem. please help me if you know the answer
Change GoodImageSerializer calling this:
GoodImageSerializer(instance=images, many = True, context={'request': request})
Then change your GoodsPostSerializer's create method like this:
def get_image(self, obj):
image = obj.goods_set.all()
request = self.context['request']
return GoodsPostSerializer(instance=image, many = True, context={'request': request})

How to use foreign key field's attribute for another model field

I have two models in different apps like so:
class Account(models.Model):
"""
Class to store fiat account information of a companies bank account
"""
number = models.CharField(max_length=100)
currency = models.ForeignKey(FiatCurrency, on_delete=models.CASCADE)
owner = models.ForeignKey(Company, on_delete=models.CASCADE)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.number
class FiatTransaction(models.Model):
"""
Class to store Transactions made between escrow and operative white-listed fiat accounts
"""
debit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='debit_account')
credit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='credit_account')
executed_on = models.DateTimeField(auto_now_add=True)
amount = models.FloatField()
currency = debit_account.currency
is_processed = models.BooleanField(default=False)
fee = models.FloatField()
memo = models.CharField(max_length=250)
def __str__(self):
return F"Transferred {self.amount} from {self.debit_account} to {self.credit_account} at {self.executed_on}"
Now the field currency of model FiatTransaction doesn't seem to work the way I intend it to do. It raises
AttributeError: 'ForeignKey' object has no attribute 'currency'
# Source model
class FiatCurrency(models.Model):
"""
A model to store Fiat Currencies offered by Finchin to
include into cash-pools.
"""
ISO_Code = models.CharField(max_length=3)
name = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
Why's that and how to make this work?
You can make a #property that will determine the currency of that object with:
class FiatTransaction(models.Model):
debit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='debit_account')
credit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='credit_account')
executed_on = models.DateTimeField(auto_now_add=True)
amount = models.FloatField()
is_processed = models.BooleanField(default=False)
fee = models.FloatField()
memo = models.CharField(max_length=250)
#property
def currency(self):
return self.debit_account.currency
This can however be inefficient if you have to do this for a lot of FiatTransactions.
In that case it might be better to remove the currency property, and annotate the QuerySet with:
from django.db.models import F
FiatTransaction.objects.annotate(currency=F('debit_account__currency'))
The FiatTransactions that arise from this will have an extra attribute named .currency that will contain the .currency of the .debit_account.
If you need this often, you can make use of a Manager that will automatically annotate when you access FiatTransaction.objects:
from django.db.models import F
class FiatTransactionManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).annotate(
currency=F('debit_account__currency')
)
class FiatTransaction(models.Model):
# …
objects = FiatTransactionManager()

Total Price Django

How can i get "total" price of items of OrderItem in cart model from these models down below? I tried doing something in views but I get attribute error that QuerySet' object has no attribute 'total'.
views.py
def cart(request):
cart = Cart.objects.filter(order_user=request.user)
order_items = OrderItem.objects.filter(cart__in=cart)
total = 0
for i in order_items:
total = i.quantity * i.item.price + cart.total
cart.update(total=total)
models.py
class OrderItem(models.Model):
cart = models.ForeignKey('Cart', on_delete=CASCADE, null=True)
item = models.ForeignKey(Item, on_delete=CASCADE, null=True)
quantity = models.IntegerField(default=1)
class Item(Visits, models.Model):
title = models.CharField(max_length=150)
price = models.IntegerField(default=1000)
image = models.ImageField(upload_to='pictures', default='static/images/man.png')
description = models.TextField(default="Item")
visits = models.IntegerField(default=0)
class Cart(models.Model):
order_user = models.OneToOneField(User, on_delete=CASCADE)
ordered = models.BooleanField(default=False)
total = models.IntegerField(default=0, help_text="100 = 1EUR")
order_items = models.ManyToManyField(Item, related_name='carts', through=OrderItem )
Just aggregate the total of ModelField total of the queryset like so
Total = Cart.objects.all().aggregate('total')
# Filtered in your case
Total = Cart.objects.filter(order_user=request.user).aggregate('total')
Apply filtering as necessary.
Also I suggest to have a good read here
You can retrieve the cart information for current users via the OrderItem model itself.
Check how annotate works
from django.db.models import Count
order_items = (OrderItem.objects.filter(cart__order_user=request.user)
.annotate(total=Count("quantity")*(item__price) + cart__total)
)

Django raw query giving same result on all models

I have 3 models Product, Photo, and ProductLikeDilike. I am performing left outer join on all the 3 models. First I am joining Product with Photo and then the resultant table(temp) I am joining with ProductLikeDilike. Below is the raw sql.
Note: olx is the name of django app.
data = Product.objects.raw('select * from (select
olx_product.id,olx_product.name,olx_photo.file,olx_photo.cover_photo_flag
from olx_product left outer join olx_photo on
(olx_product.id=olx_photo.reference_id_id) where
olx_photo.cover_photo_flag="yes" or olx_photo.cover_photo_flag is null) as
temp left outer join olx_productlikedislike on
(temp.id=olx_productlikedislike.product_id_id and
olx_productlikedislike.product_liked_by_id_id=2)')
for x in data:
print(x.name)
What I want to understand that when I use any of the above 3 models to run the raw sql why I am getting the same result i.e.
When I do
data = Product.objects.raw('select *.....')
for x in data:
print(x.name)
or
data = Photo.objects.raw('select *......')
for x in data:
print(x.name)
or
data = ProductLikeDislike.raw('select *.....')
for x in data:
print(x.name)
I am getting the same result. Why?
Please help me to understand this.
Below is the models.py file
from django.db import models
from django.urls import reverse
from django.dispatch import receiver
from django.contrib.auth.models import User
class Product(models.Model):
category = models.ForeignKey(Category ,on_delete=models.CASCADE)
name = models.CharField(max_length = 200, db_index = True)
slug = models.SlugField(max_length = 200, db_index = True)
description = models.TextField(blank = True)
price = models.DecimalField(max_digits = 10, decimal_places = 2 )#Not used FloatField to avoid rounding issues
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
contact= models.BigIntegerField(default=None,blank=True, null=True)
created_by = models.CharField(max_length = 200, default=None,blank=True, null=True)
uploaded_by_id = models.IntegerField(default=0)
status = models.IntegerField(default=0) # 0-->Active,1-->Inactive
mark_as_sold = models.IntegerField(default=0) # 0-->not sold,1-->sold
def get_absolute_url(self):
return reverse('olx:edit_product', kwargs={'pk': self.pk})
class Meta:
ordering = ('-created',)
index_together = (('id','slug'),)# we want to query product by id and slug using together index to improve performance
def __str__(self):
return self.name
class Photo(models.Model):
reference_id = models.ForeignKey(Product, null=True,on_delete=models.CASCADE)
photo_type = models.CharField(max_length = 70, db_index = True)
file = models.FileField(upload_to='photos/',default='NoImage.jpg')
cover_photo_flag = models.CharField(default=0,max_length = 5, db_index = True)
uploaded_at = models.DateTimeField(auto_now_add=True)
uploaded_by_id = models.IntegerField(default=0)
status = models.IntegerField(default=0) # 0-->Active,1-->Inactive
class Meta:
ordering = ('-uploaded_at',)
class ProductLikeDislike(models.Model):
product_id = models.ForeignKey(Product,models.SET_DEFAULT,default=0)
product_liked_by_id = models.ForeignKey(User,models.SET_DEFAULT,default=0)
status = models.BooleanField(default=False)
And Please also show me how to write it in pure Django way if possible?
I am getting the same result. Why? Please help me to understand this.
Because .raw(..) [Django-doc] just takes a raw query and executes it. The model from which the raw is performed is irrelevant.
We can generate a query that looks like:
from django.db.models import Q
Product.objects.filter(
Q(photo__photo_flag__isnull=True) | Q(photo__photo_flag='yes'),
Q(likedislike__product_liked_by_id_id=2)
)
So here we accept all Products for which a related Photo object has a flag that is NULL (this also happens in case the JOIN does not yield any flags), or the photo_flag is 'yes'). Furthermore there should be a Likedislike object where the liked_by_id_id is 2.
Note that usually a ForeignKey [Django-doc] has no _id suffix, or id_ prefix. It is also a bit "odd" that you set a default=0 for this, especially since most databases only assign strictly positive values as primary keys, and it makes no sense to inherently prefer 0 over another object anyway.
Something like this:
user_i_care_about = User.objects.get(username='user2')
productlikedislike_set = models.Prefetch('productlikedislike_set',
ProductLikeDislike.objects.select_related('product_liked_by') \
.filter(product_liked_by=user_i_care_about) \
.order_by('id'))
photo_set = models.Prefetch('photo_set', Photo.objects.all()) # this is here incase you need to a select_related()
products = Product.objects.prefetch_related(photo_set, productlikedislike_set) \
.filter(models.Q(photo__cover_photo_flag='yes') | models.Q(photo__isnull=True)) \
.filter(productlikedislike__product_liked_by=user_i_care_about)
Then you can use:
for product in products:
for pic in product.photo_set.all():
print(x.file.name)
# every product here WILL be liked by the user
if your models look something like this:
class Product(models.Model):
# category = models.ForeignKey(Category, on_delete=models.CASCADE) # TODO: uncomment, didnt want to model this out
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, db_index=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2) # Not used FloatField to avoid rounding issues # this is correct, no need to explain this, anyonw that works with django, gets this.
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
contact = models.BigIntegerField(default=None,blank=True, null=True)
created_by = models.CharField(max_length=200, default=None, blank=True, null=True)
uploaded_by_id = models.IntegerField(default=0) # TODO: use ForeignKey(User) here!!!
status = models.IntegerField(default=0) # 0-->Active,1-->Inactive # TODO: learn to use `choices`
mark_as_sold = models.IntegerField(default=0) # 0-->not sold,1-->sold # TODO: there is something called `BooleanField` use it!
class Meta:
ordering = ('-created',)
index_together = (('id', 'slug'),) # we want to query product by id and slug using together index to improve performance
def get_absolute_url(self):
return reverse('olx:edit_product', kwargs={'pk': self.pk})
def __str__(self):
return self.name
class Photo(models.Model):
product = models.ForeignKey(Product, null=True,on_delete=models.CASCADE, db_column='reference_id')
photo_type = models.CharField(max_length=70, db_index=True)
file = models.FileField(upload_to='photos/', default='NoImage.jpg')
cover_photo_flag = models.CharField(default=0, max_length=5, db_index=True) # TODO: learn to use `choices`, and you use "yes" / "no" -- and the default is 0 -- FIX THIS!!
uploaded_at = models.DateTimeField(auto_now_add=True)
uploaded_by_id = models.IntegerField(default=0) # TODO: use ForeignKey(User) here!!!
status = models.IntegerField(default=0) # 0-->Active,1-->Inactive # TODO: learn to use `choices` -- perhaps just call this "is_active" and make it a bool
class Meta:
ordering = ('-uploaded_at',)
class ProductLikeDislike(models.Model):
product = models.ForeignKey(Product, models.SET_DEFAULT, default=0) # TODO: default=0?? this is pretty bad. models.ForeignKey(Product, models.SET_NULL, null=True) is much better
product_liked_by = models.ForeignKey(User, models.SET_DEFAULT, default=0, db_column='product_liked_by_id') # TODO: default=0?? this is pretty bad. models.ForeignKey(ForeignKey, models.SET_NULL, null=True) is much better
status = models.BooleanField(default=False) # TODO: rename, bad name. try something like "liked" / "disliked" OR go with IntegerField(choices=((0, 'Liked'), (1, 'Disliked')) if you have more than 2 values.
A full example WITH tests can be seen here: https://gist.github.com/kingbuzzman/05ed095d8f48c3904e217e56235af54a

Filtering Django models by user & object

I'm learning Django with a dummy example but having difficulty in understanding how to correctly filter my Django models by an authorised user in my views.
In my view I want to list the transactions associated with a users portfolio. The code below runs but when trying to access the result of 't' I get the error:
'ValueError: The QuerySet value for an exact lookup must be limited to one result using slicing.'
Any help would be much appreciated, thanks.
if request.user.is_authenticated:
# Get model data
pf = Portfolio.objects.filter(user=request.user)
t = Transaction.objects.filter(pf=pf)
My model is as below:
from django.db import models
from django.contrib.auth.models import User
class Portfolio(models.Model):
# Portfolio has one user associated with it
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100, default='-')
def __str__(self):
return self.name
class Transaction(models.Model):
# Transaction has one equity associated with it
equity = models.ForeignKey('Equity', on_delete=models.CASCADE, null=True)
# Transaction has one portfolio associated with it
pf = models.ForeignKey('Portfolio', on_delete=models.CASCADE)
BUY = 'BUY'
SELL = 'SELL'
BUY_OR_SELL = (
(BUY, 'BUY'),
(SELL, 'SELL'),
)
action = models.CharField(choices=BUY_OR_SELL, default=BUY, max_length=5)
num = models.FloatField(default=1)
price = models.FloatField(default=0)
date = models.DateField('date')
fee = models.FloatField(default=0)
def __str__(self):
return f'{self.equity}, {self.num}x{self.price}, {self.date:%d %b %Y}'
class Equity(models.Model):
class Meta:
verbose_name_plural = "Equities"
CUR_EUR = 'EUR'
CUR_GBP = 'GBP'
CUR_USD = 'USD'
CURRENCY_CHOICES = (
(CUR_EUR, 'EUR'),
(CUR_GBP, 'GBP'),
(CUR_USD, 'USD'),
)
symbol = models.CharField(max_length=20, default='-')
exchange = models.CharField(max_length=100, default='-')
currency = models.CharField(max_length=15, choices=CURRENCY_CHOICES, default=CUR_USD)
def __str__(self):
return self.symbol
Many thanks!
pf is here a collection of Portfolio objects, so you can query it with the __in lookup [Django-doc]:
Transaction.objects.filter(pf__in=pf)
Or if you are not interested in the Porfolio objects itself, you can make a query like:
Transaction.objects.filter(pf__user=request.user)
The query below will result in a query like:
SELECT transaction.*
FROM transaction
JOIN portfolio ON transaction.pf_id = portfolio.id
WHERE porfolio.user_id = 123
(with 123 the id of the request.user)