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
Related
I am trying to adapt the Viewflow Dynamicsplit example
The objective is to split approvals of an Order based on OrderLines and to assign them to the appropriate approver. It seems that this should be possible as there seems to be a possibility described in this answer - Assign user to tasks automatically (Viewflow, Django)
The issue is that whenever I try to grab the current order pk in the DynamicSplitActivation - the queryset comes back empty. Not sure where I am going wrong. The queryset works fine if I set the pk manually, but as soon as I try to use a dynamic variable it stops working.
I have listed the flow and models etc. at the end of the post, however, I am guessing the issue is quite basic. Any help would be appreciated!
nodes.py
...
class DynamicSplitActivation(AbstractGateActivation):
def calculate_next(self):
self._order = self.flow_task._order_callback(self.process)
order_id = self._order.pk
# order_id = 28
order_lines = models.OrderLine.objects.filter(order_id=order_id)
project_lines = project_models.ProjectLine.objects.filter(orderline__in=order_lines)
users = User.objects.filter(projectline__in=project_lines).distinct()
self._approvers = users
...
class DynamicSplit(...):
...
def __init__(self, callback):
super(DynamicSplit, self).__init__()
self._order_callback = callback
self._ifnone_next_node = None
...
flows.py
...
approval_split = (
nodes.DynamicSplit(lambda act: act.order)
.IfNone(this.end)
.Next(this.split_approval_budget)
)
models.py
...
class ProjectLine(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
description = models.CharField(max_length=50)
budget_holder = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True)
class Order(models.Model):
description = models.CharField(max_length=30)
class OrderLine(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
project_line = models.ForeignKey(project_models.ProjectLine, on_delete=models.SET_NULL, null=True, blank=False)
product = models.ForeignKey(catalogue_models.Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField()
budgetholder_approved = models.BooleanField(null=True)
class OrderProccess(Process):
order = models.ForeignKey(Order, blank=True, null=True, on_delete=models.CASCADE)
...
# Here is my models
This is my CustmerBuySell model DB designed.
class CustomerBuySell(models.Model):
customer = models.ForeignKey(CustomerAdd, on_delete=models.CASCADE)
customer_buy_sell_debit = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
customer_buy_sell_credit = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
description = models.CharField(max_length=250, blank=True)
date = models.DateField()
sms = models.BooleanField(default=False)
picture = models.ImageField(upload_to='customer_buy_sell_pics', default='images.png')
created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
updated_at = models.DateTimeField(auto_now=True, blank=True, null=True)
def __str__(self):
return self.customer.customer_name
class Meta:
verbose_name = "Customer BuySell"
verbose_name_plural = "Customer BuySell"
# Here, is my View.
This is the class-based APIView, which I have used. And try to use the aggregate query in this view.
class DailyCustomerBuySellAPIView(APIView):
def get(self, request):
customer_buy_sell = CustomerBuySell.objects.extra(select={'day': 'date( date )'}).values('day').order_by(
'date__date').annotate(available=Count('date__date'))
serializer = CustomerBuySellSerializer(customer_buy_sell, many=True)
return Response({"customer_buy_sell": serializer.data})
# And, finally here are my Serializers
I have no idea what's the problem! Please help me.
class CustomerBuySellSerializer(serializers.ModelSerializer):
# customer = CustomerAddSerializer()
class Meta:
model = CustomerBuySell
fields = '__all__'
def to_representation(self, instance):
representation = super(CustomerBuySellSerializer, self).to_representation(instance)
if instance.customer is not None:
customer_name = instance.customer.customer_name
previous_due = instance.customer.previous_due
representation['custo`enter code here`mer_name'] = customer_name
representation['previous_due'] = previous_due
return representation
There are many problems with your approach. Let me mention each of them one by one:
First of all remove date__date from your APIVIew
Before:
customer_buy_sell = CustomerBuySell.objects.extra(select={'day': 'date( date )'}).values('day').order_by(
'date__date').annotate(available=Count('date__date'))
Instead, write it as:
from django.db.models.functions import Extract
customer_buy_sell = CustomerBuySell.objects.annotate(day=Extract('date','day')).values('day').order_by('day')
if you need a count of the days then you can try
customer_buy_sell_count = customer_buy_sell.count()
Another thing that you are doing wrong is you pass a dict to serializer as you are already using values that return a dictionary of days and not object of CustomerBuySell so you do not need to pass it to serializer otherwise you have to do it according to your need.
In CustomerBuySellSerializer you are using a model serializer with __all__ while you are passing an extra fields day that is not part of it.
So in short there are so many syntax issues with your Django and Django Rest Framework.Great way to fix such issues is to set with an experience programmer so that he can improve the flow of the code. Later on you can focus on logic.
I suppose it is just a typo: Change date__date to date
i have two models ImageShoot and Image.
models.py:
class ImageShoot(models.Model):
name = models.CharField(max_length=100)
# image = models.URLField(name=None)
created_at = models.TimeField(auto_now_add=True)
def __str__(self):
return self.name
class Image(models.Model):
license_type = (
('Royalty-Free','Royalty-Free'),
('Rights-Managed','Rights-Managed')
)
image_number = models.CharField(default=random_image_number,max_length=12)
title = models.CharField(max_length = 100)
image = models.ImageField(upload_to = 'home/tboss/Desktop/image' , default = 'home/tboss/Desktop/image/logo.png')
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.CASCADE)
shoot = models.ForeignKey(ImageShoot, on_delete=models.CASCADE)
image_keyword = models.TextField(max_length=1000)
credit = models.CharField(max_length=150, null=True)
location = models.CharField(max_length=100, null=True)
license_type = models.CharField(max_length=20,choices=license_type, default='')
uploaded_at = models.TimeField(auto_now_add=True)
def __str__(self):
return self.title
admin.py:
class Imageset(admin.ModelAdmin):
associated_images = ImageShoot.image_set.all()
return associated_images
admin.site.register(Image)
admin.site.register(ImageShoot,Imageset)
what i am trying to achieve that when i create a image it should show on imageshoot also like when i create image in shoot 1. this image should show on shoot 1.
i am not sure which field should i add on imageshoot.
Use Reverse lookup
In Your views you can get all the images associated with the ImageShoot Obj using set.Try this in your shell after briefly going through the docs
associated_images = ImageShoot.image_set.all()
You can also use django orm methods for querysets like filter, count, etc.
Edit:
To display related images you can use this in admin:
#admin.register(ImageShoot)
class Imageset(admin.ModelAdmin):
list_display = ('name', 'created_at', 'associated_images')
def associated_images(self, obj):
return obj.image_set.all() #<----edit
associated_images.admin_order_field = 'imageshoot_image'
I want to create a custom object list in the view and pass it to the template. In the template I want to loop over the list and display the information.
My models are
class CustomUser(AbstractUser):
def __str__(self):
return self.email
class Post(models.Model):
author = models.ForeignKey(CustomUser,on_delete=models.CASCADE,)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
post_url = models.URLField(max_length = 200, blank = True)
slug = models.SlugField(unique=True, blank=True)
class subscription(models.Model):
creator = models.ForeignKey(CustomUser,default=None, null=True,on_delete=models.CASCADE,related_name='creator',)
booster = models.ForeignKey(CustomUser,default=None, null=True,on_delete=models.CASCADE,related_name='booster')
sub_value = models.FloatField(blank = True)
sub_id = models.TextField(blank = True)
status = models.BooleanField(default=False)
dateSubscribed = models.DateTimeField(default=timezone.now)
dateSubscriptionEnded = models.DateTimeField(default=timezone.now)
paymentCount = models.FloatField(default= 0)
I want to filter objects from subscription model like below
subs = subscription.objects.filter(booster = request.user)
Then find creators in the above subs object list and for each creator get the name, numbers Posts, and number of Subscribers. Add this to custom list and pass it to the template to loop over and display the information in the template. Can someone help me how to create this custom list. Thanks!
Ok so here are the basics minus the subscribers because I don't see the relation clearly. This is how to parse the name and the number of posts. \
my_list = []
for sub in subs:
name = sub.creator.name
auth_id = sub.creator.id
posts = Post.objects.filter(author=auth_id)
num_of_posts = len(posts)
my_list.append({
'name':name,
'post_count': num_of_posts,
})
then you would pass mylist thru the template context.
It is a common mistake to name the related_name=… parameter [Django-doc] to the same value as the name of the field. The related_name parameter however is the name of the reverse relation Django will automatically add. So here it means a relation to access for example the related subscription objects of a given CustomUser.
Therefore it makes more sense to rename these, for example like:
class Subscription(models.Model):
creator = models.ForeignKey(
CustomUser,
default=None,
null=True,
on_delete=models.CASCADE,
related_name='created_subscriptions'
)
booster = models.ForeignKey(
CustomUser,
default=None,
null=True,
on_delete=models.CASCADE,
related_name='boosted_subscriptions'
)
sub_value = models.FloatField(blank=True)
sub_id = models.TextField(blank =True)
status = models.BooleanField(default=False)
dateSubscribed = models.DateTimeField(default=timezone.now)
dateSubscriptionEnded = models.DateTimeField(default=timezone.now)
paymentCount = models.FloatField(default=0)
Next we can make a query where:
from django.db.models import Count
CustomUser.objects.filter(
created_subscriptions__booster=request.user
).annotate(
number_of_posts=Count('post', distinct=True)
)
This is a QuerySet of CustomUsers where each CustomUser that arises from this QuerySet has an extra attribute .number_of_posts that contains the number of posts. You thus can iterate over the queryset directly in the template.
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))