Django query based on another query results - django

I have 4 models in my simplified design
class modelA(models.Model):
name = models.CharField()
class modelsUser(model.Model):
username = models.CharField()
class bridge(models.Model):
user = models.ForeignKey(modelUser, on_delete=models.CASCADE, related_name='bridges')
modelA = models.ForeignKey(modelA, on_delete=models.CASCADE, related_name='bridges')
class subModelA(models.Model):
modelA = models.ForeignKey(modelA, on_delete=models.CASCADE, related_name='subModelAs')
value = models.IntegerField()
class subModelB(models.Model):
modelA = models.ForeignKey(modelA, on_delete=models.CASCADE, related_name='subModelBs')
text = models.TextField()
What I am trying to to is to get all subModelBs and subModelAs that are for modelAs for which given modelUser have bridge.
I've started with this:
user = modelUser.objects.get(pk=1)
bridges = user.bridges.all()
What I've been thinking is something like this:
subModelBs = subModelB.objects.filter(modelA__in=bridges__modelA)
but unfortunately it doesn't work because of error that __modelA is not defined.
Is there any proper way to do this?

Find first the modelAs and then do two other queries:
modelAs = bridge.objects.filter(user__pk=1).values_list('modelA', flat=True)
subModelAs = subModelA.object.filter(modelA__in=modelAs)
subModelBs = subModelB.object.filter(modelA__in=modelAs)

A good question first of all!
Tried reproducing on my system, the following worked for me:
user = modelUser.objects.get(pk=1)
bridges = user.bridges.all()
subModelAs = subModelA.objects.filter(
modelA_id__in=[x.modelA_id for x in list(bridges)]
)
And similarly for subModelBs. Hope this helps you well.

Related

How to Use annotate function to in django for below condition?

class Surface(models.Model):
name = models.CharField(max_length=100)
surface_class = models.ManyToManyField(SurfaceClass, default=None)
class SurfaceGeometry(models.Model):
surface = models.ForeignKey(Surface, on_delete=models.DO_NOTHING)
geometry_parameter = models.ForeignKey(SurfaceGeometryParameters, on_delete=models.CASCADE)
value = models.FloatField()
class SurfaceGeometryParameters(models.Model):
name = models.CharField(max_length=30, unique=True)
Surface.objects.prefetch_related('surface_class',Prefetch('surfacecorrelationcontroller_set'),Prefetch('surfacegeometry_set')).annotate(height=?).order_by('surface_class__name','-height')
I want to take height(value) from SurfaceGeomentry model where Height is name of geometry parameter from SurfaceGeometryParameters models for Surface.
I can get a height from SurfaceGeometry like this.
SurfaceGeometry.objects.get(surface__id = 1, geometry_parameter__name__iexact= 'Height')
where surfcace__id's value 1 should come from parent query.
How I can achieve the this?
Final Answer of this question after too many attempt is
surface_height_query = SurfaceGeometry.objects.filter(surface=OuterRef('pk'),geometry_parameter__name__iexact='Height') \
.values_list('value', flat=True)
return Surface.objects.annotate(height=Subquery(surface_height_query)).order_by('surface_class__name','height')
Django Documentation for the same

Is there any possible solution for getting more than one value inside function in django?

I am creating a blog application using Django and I am also very much new to django.
This is the models I created
class categories(models.Model):
Title = models.CharField(max_length=40, default='GST')
class Blog(models.Model):
User = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,null=True,blank=True)
Date = models.DateTimeField(default=datetime.now)
Blog_title = models.CharField(max_length=255)
likes = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='likes',blank=True)
Description = RichTextUploadingField(blank=True, null=True,config_name='special')
Blog_image = models.ImageField(upload_to='blog_image', null=True, blank=True)
Category = models.ForeignKey(categories,on_delete=models.CASCADE,related_name='blogs')
I was wondering How to count the total no of blog present under a particular category?
I want to track a specific count rate for all Categories...
Done something like this in my model
def categories_count(self):
for a in categories.objects.all():
categories_count = Blog.objects.filter(Category__Title=a.Title).count()
return categories_count
But it is returning only one value...Can anyone suggest me with some suitable codes to resolve this...
Thank you
You can get a list of tuples of category title and blog count with the following query:
categories.objects.annotate(blog_count=Count('Categories')).values_list('Title', 'blog_count')

Query a many to many field of referenced table using ORM

My models are as follows:
class AppUser(models.Model):
id = models.AutoField(primary_key=True)
user = models.OneToOneField(User)
states = models.ManyToManyField(State)
class ABC(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
email = models.EmailField()
app_user = models.ForeignKey(AppUser, null=True, blank=True)
I want to query my database for list of objects present in ABC model and I want to filter it according to the list of States.
I am trying something like this:
ABC.objects.filter(app_user__states__in = state_list).values('id','name')
But this is not working. Can I even access a many to many field like this or do I need to create a custom through table.
Yes, you can.
For queryset:
ABC.objects.filter(app_user__states__in = [1,2]).values('id', 'name')
you'll get sql like this:
>>> print(ABC.objects.filter(app_user__states__in = [1,2]).values('id', 'name').query)
SELECT "test_abc"."id", "test_abc"."name"
FROM "test_abc"
INNER JOIN "test_appuser" ON ("test_abc"."app_user_id" = "test_appuser"."id")
INNER JOIN "test_appuser_states" ON ("test_appuser"."id" = "test_appuser_states"."appuser_id")
WHERE "test_appuser_states"."state_id" IN (1, 2);
Looks fine. Maybe it doesn't work as you expected?

Django: way of getting objects?

I have following structure of models
For the model of 'Campaigns' I'm going to write down functions that would be called in a template: ads_count
My solution was:
class Campaigns(AlphaModel):
Project = models.ForeignKey('Projects', on_delete=models.CASCADE, related_name='projects')
......
def ads_count(self):
c = 0
gr = AdGroups.objects.filter(Campaign=self).all()
for g in gr:
c += g.ads_count()
return c
......
class AdGroups(AlphaModel):
Campaign = models.ForeignKey('Campaigns', on_delete=models.CASCADE, related_name='campaigns')
server_id = models.PositiveIntegerField()
......
def ads_count(self):
return Ads.objects.filter(AdGroup=self).count()
......
class Ads(AlphaModel):
AdGroup = models.ForeignKey('AdGroups', on_delete=models.CASCADE, related_name='adgroups_a')
server_id = models.PositiveIntegerField()
......
However this was throws an error "Cannot resolve keyword into field"
Is there any simple and direct way of counting objects of 'ads' that belongs to particular object of 'campaigns' ?
Thanks in advance!
First of all, it is general convention to use CamelCase only for class definitions, and lower case in all your field names. This makes it much easier to follow the code and distinguish between classes and objects. I would rewrite your models as follows:
class Campaigns(AlphaModel):
project = models.ForeignKey('Projects', on_delete=models.CASCADE, related_name='projects')
class AdGroups(AlphaModel):
campaign = models.ForeignKey('Campaigns', on_delete=models.CASCADE, related_name='campaigns')
server_id = models.PositiveIntegerField()
class Ads(AlphaModel):
ad_group = models.ForeignKey('AdGroups', on_delete=models.CASCADE, related_name='adgroups_a')
server_id = models.PositiveIntegerField()
Now, to get the count of Ads associated with a Campaign, you can do this query:
class Campaigns(AlphaModel):
...
def ads_count(self):
return Ads.objects.filter(ad_group__campaign=self).count(distinct=True)
(using your existing field names this would be):
Ads.objects.filter(AdGroup__Campaign=self).count(distinct=True)

Django substract two fields from related model

With this models:
class Vine(models.Model):
autor = models.ForeignKey(Viner,related_name='autor')
titulo = models.CharField(max_length=450)
estado = models.CharField(choices=ESTADOS_VINE, max_length=30)
objects = models.Manager()
custom_object = managers.VineManager()
and the model for the votes
class Voto(models.Model):
user = models.ForeignKey(MyUser)
submit_date = models.DateTimeField(auto_now_add=True)
vine = models.ForeignKey(Vine)
valoracion = models.BooleanField(default=False)
and the class for the Favorites (This is working fine yet)
class Favorito(models.Model):
date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='favoritos')
I have this 'query' in Django.
vines = Vine.custom_object.filter(estado=2).\
annotate(favoritosCount=Count('favoritos', distinct=True)).\
filter(voto__valoracion=False).annotate(disLikesCount=Count('voto', distinct=True))\
.annotate(likesCount=Count('voto', distinct=True)).filter(voto__valoracion=True)
But the second filter is not working because of the first.
Basically what I want is to get the sum of 'positive votes' - 'negative votes' as a field and order by it.
Could anyone please help me?
Thank you in advance
AFAIK you can't do that query with the ORM. You might be able to do it with a raw query.
I think It's easier if you add a count field to your Vine model and order by it. Then update that count field every time there's a new Voto.
Something like this:
from django.db.models import F
class Vine(models.Model):
...
votos = models.IntegerField()
class Meta:
ordering = ('votos',)
class Voto(models.Model):
...
def save(self):
"""When saving new Voto instance, update related Vine."""
if not self.pk:
new_vote = 1 if self.valoracion else -1
self.vine.update(votos=F('votos') + new_vote)
return super(Voto, self).save()
PS: If you want to know more about that F expression.