Sorted list of two query sets - django

class TagSynonym(models.Model):
source_tag_name = models.CharField(max_length=255, unique=True)
target_tag = models.ForeignKey(Tag, related_name='tag_synonyms', null=True)
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
used_count = models.PositiveIntegerField(default=0)
class Meta:
ordering = ('-used_count', 'name')
For a given query q:
I want all the tags which contain the query in its name or in the related synonym's source_tag_name.
I also want the sort order to be preserved by ordering.
I have a query like the following
tags_by_name = tag_all.filter(name__contains=q)
tags_by_synonyms = tag_all.select_prefetched('tag_synonyms').filter(tag_synonyms__source_tag_name__contains=q).distinct()
tags = tags_by_name | tags_by_synonyms
tags = tags.distinct()
I'm not sure if the above code is correct.
Are there better way to do this?

You can do this with Django Q object:
from django.db.models import Q
qs = Tag.objects.select_prefetched('tag_synonyms').filter(
Q(name__contains=q) | Q(tag_synonyms__source_tag_name__contains=q)) \
.distinct()

Related

Django : Create custom object list in the view and pass it to template to loop over

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.

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

Is it possible to combine multiple values_list() in Django?

As the title suggests, I have multiple sets of queries that each return a values list. I then use the values list to filter another queryset. At the moment I can only do this second step one queryset at a time. Is it possible to combine my initial values lists into one super long list? I'm trying to create an activity/news feed like feature.
views.py:
cookie_ids = Cookie.objects.filter(board__pk=self.kwargs['pk']).values_list('id',
flat=True)
sugar_ids = Sugar.objects.filter(board__pk=self.kwargs['pk']).values_list('id',
flat=True)
**then:
context['cookie_actions'] = Action.objects.filter(target_id__in=cookie_ids)
context['sugar_actions'] = Action.objects.filter(target_id__in=sugar_ids)
Edit: I think this is the only model that might matter
Models.py:
class Action(models.Model):
user = models.ForeignKey(User,
related_name='actions',
db_index=True)
verb = models.CharField(max_length=255)
target_ct = models.ForeignKey(ContentType,
blank=True,
null=True,
related_name='target_obj')
target_id = models.PositiveIntegerField(null=True,
blank=True,
db_index=True)
target = GenericForeignKey('target_ct', 'target_id')
created = models.DateTimeField(auto_now_add=True,
db_index=True)
class Meta:
ordering = ('-created',)
You can use chain to combine your querysets
from itertools import chain
cookie_ids = Cookie.objects.filter(board__pk=self.kwargs['pk']).values_list('id',flat=True)
sugar_ids = Sugar.objects.filter(board__pk=self.kwargs['pk']).values_list('id',flat=True)
ids_list = chain(cookie_ids, sugar_ids)
context['total_actions'] = Action.objects.filter(target_id__in=ids_list)
I think this is what you want
cookie_ids=Cookie.objects.filter(board__pk=self.kwargs['pk']).values_list('id',flat=True)
sugar_ids=Sugar.objects.filter(board__pk=self.kwargs['pk']).values_list('id',Ôflat=True)
ids_list = list(cookie_ids) + list(sugar_ids)
context['total_actions'] = Action.objects.filter(target_id__in=ids_list)
Using union
cookie_ids = Cookie.objects.filter(board__pk=self.kwargs['pk']).values_list('id',
flat=True)
sugar_ids = Sugar.objects.filter(board__pk=self.kwargs['pk']).values_list('id',
flat=True)
target_ids = cookie_ids.union(sugar_ids)
My References:
Django QuerySet union link

How to filter values on foreign key field editing?

I have a three simple models:
class Task(models.Model):
code = models.CharField(max_length=200, unique=True)
tags = models.ManyToManyField(Tag, blank=True)
class Session(models.Model):
tasks = models.ManyToManyField(Task, through='TaskInSession')
and
class TaskInSession(models.Model):
session = models.ForeignKey(Session, on_delete=models.CASCADE)
task = models.ForeignKey(Task, on_delete=models.CASCADE)
count = models.IntegerField()
For session editing I have:
class SessionAdmin(admin.ModelAdmin):
inlines = [TaskInSessionInline,]
exclude = ('tasks', )
Is it possible to add tasks filterting by tag possibility, for easy task selection on session editing?
you should have in models.py your Tag class defined. Maybe something like this:
class Tag(models.Model):
Name = models.CharField(max_length=200, unique=True)
Then in your views.py you get the desired Tag. For instance:
desired_tag = Tag.objects.get(Name='some_name') # you could as well get the desired tagby the pk attribute.
Finally, Get all the tasks associated with that Tag. Example:
filtered_tasks = Tasks.objects.filter(tags=desired_tag)
The query you are looking would be filtered_tasks.

How to add fields together automatically

I'm going to build up an assets management system. Every property has an unique property-code combine with department-code and type-code.
from django.db import models
class Department(models.Model):
# department_code like rm01
department_code = models.CharField(max_length=4, unique=True)
name = models.CharField(max_length=100)
class Type(models.Model):
# type_code like fe03
type_code = models.CharField(max_length=4, unique=True)
name = models.CharField(max_length=100)
class Property(models.Model):
# property_code like rm01fe037767
property_code = models.CharField(max_length=12, unique=True)
name = models.CharField(max_length=100)
department = models.ForeignKey(Department)
type = models.ForeignKey(Type)
How to do that? OR is there another way to achieve the aim?
Yes but not automatically.
In the normal case: when you want to add new Propery ~> User should pick other 2 model right?
dep_id = ?
type_id= ?
p = Property (id = dep_id+type_id, name = "blabla"...)
p.save()
And you should define the PK (default is int field, auto incre ~> to charfield) and input it by yourself
And normally: we don't do that...
We use: ForeignKey like:
class Item(models.Model):
people = models.ForeignKey(People)
name = models.CharField(max_length=40)
value = models.IntegerField(default=0)
def __str__(self):
return self.name