Django reverse lookup foreign keys with multiple conditions - django

I am trying to do a reverse lookup in Django with multiple conditions, hopefully, my question does not duplicate with other questions. Let me explain:
I have two models:
Model A:
class A(models.Model):
title = models.CharField(max_length=200)
... other fields
Model B:
class B(models.Model):
a = models.ForeignKey(A)
label = models.CharField(max_length=200)
value = models.CharField(max_length=200)
... other fields
How can I get A that:
1) has a B with label = name, value=John
2) and, has a B with label =age, value =20
I tried the following, it does not work:
A.objects.filter(b__label="name", b__value="John", b__label="age", b__value=20)

You can use Q object:
from django.db.models import Q
A.objects.filter(
(Q(b__label="name") & Q(b__value="John")) | (Q(b__label="age") & Q(b__value=20))
)

Related

How to Sum a value with row duplicates in the Django orm query

Goal
I am looking for a way to get a sum of values
Models
class ModelX(models.Model):
name = models.CharField(...)
class ModelA(models.Model):
value = models.IntegerField()
class ModelB(models.Model):
modela = models.ForeignKey(ModelA)
modelxs = models.ManyToManyField(ModelX, through='ModelC')
class ModelC(models.Model):
modelb = models.ForeignKey(ModelB)
modelx = models.ForeignKey(ModelX)
modelxx = models.ForeignKey(ModelX, related_name='modelcs')
What I've tried
Modelx.objects.values('name').annotate(
total=Sum('modelcs__modelb__modela__value')
).values('total')
And
sum_qs = (
ModelA
.objects
.filter(modelb_set__modelc_set__modelxx=OuterRef('id'))
.values('modelb_set__modelc_set__modelxx_id')
.order_by()
.distinct()
.values('value')
)
qs = ModelX.objects.annotate(
total=Subquery(sum_qs.values('value')))
The Problem
ModelC can contain multiple rows with the same modelxx relation. This will cause duplicates, and cause the sum of 'value' to get duplicated
Example
Below are the example models where I would expect to see the 10 as stated in the expected results up top: edit
foo = ModelX.objects.create(name='Foo')
bar = ModelX.objects.create(name='Bar')
baz = ModelX.objects.create(name='Baz')
modela1 = ModelA.objects.create(value=10)
modela2 = ModelA.objects.create(value=10)
modelb1 = ModelB.objects.create(modela=modela1)
modelb2 = ModelB.objects.create(modela=modela2)
modelc1 = ModelC.objects.create(modelb=modelb1, modelx=foo, modelxx=bar)
modelc2 = ModelC.objects.create(modelb=modelb1, modelx=bar, modelxx=bar) # This would be a cause a duplicate as a relation to ModelX through modelxx
modelc3 = ModelC.objects.create(modelb=modelb1, modelx=baz, modelxx=baz)
modelc4 = ModelC.objects.create(modelb=modelb2, modelx=bar, modelxx=bar)
Expected Results
EDIT {'Bar': 20, 'Baz': 10}
This can also be as a QuerySet of ModelX to serialize

Django many to many relationship. How to get all rows which primary key doesn't exist on another table?

I've 3 models A, B and C. they are as follows:
class A(models.Model):
name=models.TextFeild()
class B(models.Model):
name=models.TextFeild()
class C(models.Model):
a=models.models.ForeignKey(A, on_delete=models.CASCADE, related_name='as')
b=models.models.ForeignKey(B, on_delete=models.CASCADE, related_name='bs')
I want to get all B's which doesn't exist in C with A.name = 'jhon'
I've tried
taken_tests = C.objects.filter(a.name='jhon')
queryset = B.objects.filter().exclude(pk__in=taken_tests)
But this gives me all B's.
Try to use related_name in exclude:
B.objects.exclude(bs__a__name='jhon')
Can you use ManyToManyField?
So the code will be:
a1 = A(name='a1')
a1.save()
a2 = A(name='a2')
a2.save()
b1 = B(name='b1')
b1.save()
b1.a.add(a1)
b2 = B(name='b2')
b2.save()
b3 = B(name='b3')
b3.save()
b3.a.add(a2)
ba = B.objects.filter(a=a1)
bna = B.objects.exclude(a=a1)
If you need some attributes on the relationship table you can loot at ManyToManyField.through

Regarding ManyToManyFields in Django

Suppose I make a model:
class A(models.Model):
col = models.ManyToManyField(B)
Now after migration, this will create a table A_B with fields id,A_id,B_id.
Now if I want to access the id of the table A_B, how to do that? And how to update the B_id of this table?
If you have an object of A:
A_obj.col.all()
This will return all the A_B objects related to that A
for ab in A_obj.col.all():
print ab.id
This will print all the ids of the table A_B.
for ab in A_obj.col.all():
ab.B_id = new id
ab.save()
This will update your B_id with new id.
You are looking for Through Relationships
class A(models.Model):
col = models.ManyToManyField(B, through='C')
class C(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
//any other extra field
Then you can filter or query C table.
For example, to print all C instances id:
for c in C.objects.all():
print c.id

(Django) filtering objects in view

I have 2 classes in model
class A(models.Model):
name = models.CharField(max_length = 20)
class B(models.Model):
a = models.ForeignKey(A)
and I want to filter objects of B which does not have 'a' not has name of "exclude".
I tried
objects = B.objects.exclude(a.name == "exclude")
in my view, but it does not work.
How can I do this?
This will work:
objects = B.objects.exclude(a__name="exclude")
objects = B.objects.exclude(a__name="exclude")
or
from django.db.models import Q
objects = B.objects.filter(~Q(a__name="exclude"))
but the former one is good enough..

Django ORM equivalent for this SQL..calculated field derived from related table

I have the following model structure below:
class Master(models.Model):
name = models.CharField(max_length=50)
mounting_height = models.DecimalField(max_digits=10,decimal_places=2)
class MLog(models.Model):
date = models.DateField(db_index=True)
time = models.TimeField(db_index=True)
sensor_reading = models.IntegerField()
m_master = models.ForeignKey(Master)
The goal is to produce a queryset that returns all the fields from MLog plus a calculated field (item_height) based on the related data in Master
using Django's raw sql:
querySet = MLog.objects.raw('''
SELECT a.id,
date,
time,
sensor_reading,
mounting_height,
(sensor_reading - mounting_height) as item_height
FROM db_mlog a JOIN db_master b
ON a.m_master_id = b.id
''')
How do I code this using Django's ORM?
I can think of two ways to go about this without relying on raw(). The first is pretty much the same as what #tylerl suggested. Something like this:
class Master(models.Model):
name = models.CharField(max_length=50)
mounting_height = models.DecimalField(max_digits=10,decimal_places=2)
class MLog(models.Model):
date = models.DateField(db_index=True)
time = models.TimeField(db_index=True)
sensor_reading = models.IntegerField()
m_master = models.ForeignKey(Master)
def _get_item_height(self):
return self.sensor_reading - self.m_master.mounting_height
item_height = property(_get_item_height)
In this case I am defining a custom (derived) property for MLog called item_height. This property is calculated as the difference of the sensor_reading of an instance and the mounting_height of its related master instance. More on property here.
You can then do something like this:
In [4]: q = MLog.objects.all()
In [5]: q[0]
Out[5]: <MLog: 2010-09-11 8>
In [6]: q[0].item_height
Out[6]: Decimal('-2.00')
The second way to do this is to use the extra() method and have the database do the calculation for you.
In [14]: q = MLog.objects.select_related().extra(select =
{'item_height': 'sensor_reading - mounting_height'})
In [16]: q[0]
Out[16]: <MLog: 2010-09-11 8>
In [17]: q[0].item_height
Out[17]: Decimal('-2.00')
You'll note the use of select_related(). Without this the Master table will not be joined with the query and you will get an error.
I always do the calculations in the app rather than in the DB.
class Thing(models.Model):
foo = models.IntegerField()
bar = models.IntegerField()
#Property
def diff():
def fget(self):
return self.foo - self.bar
def fset(self,value):
self.bar = self.foo - value
Then you can manipulate it just as you would any other field, and it does whatever you defined with the underlying data. For example:
obj = Thing.objects.all()[0]
print(obj.diff) # prints .foo - .bar
obj.diff = 4 # sets .bar to .foo - 4
Property, by the way, is just a standard property decorator, in this case coded as follows (I don't remember where it came from):
def Property(function):
keys = 'fget', 'fset', 'fdel'
func_locals = {'doc':function.__doc__}
def probeFunc(frame, event, arg):
if event == 'return':
locals = frame.f_locals
func_locals.update(dict((k,locals.get(k)) for k in keys))
sys.settrace(None)
return probeFunc
sys.settrace(probeFunc)
function()
return property(**func_locals)