Regarding ManyToManyFields in Django - 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

Related

django order by aggregate value from non-related table

I have three models, Accrual and Member, and the common field of these two models is register_no, but this field is not a foreign key
class Accrual(models.Model):
register_no = models.PositiveIntegerField(verbose_name=_('Register No'))
amount=models.DecimalField(decimal_places=2, max_digits=17, verbose_name=_('Total Amount'))
class Member(models.Model):
register_no = models.PositiveIntegerField(unique=True, verbose_name=_('Register No'))
class Driver(models.Model):
register_no = models.PositiveIntegerField(unique=True, verbose_name=_('Register No'))
I want to list the debt each member/driver has. It can be done with #property;
class Member(models.Model):
register_no = models.PositiveIntegerField(unique=True, verbose_name=_('Register No'))
#property
def debt(self):
ret_val = Accrual.objects.filter(register_no=self.register_no).aggregate(
debt=Sum('amount'))
debt = ret_val.get('debt', 0)
return debt if debt else 0
but I can't use order_by this way. I want to sort each member by debt. How can I solve this problem?
You can use a subquery to add all the related accruals based on the register_no:
from django.db.models import OuterRef, Subquery, Sum
accruals = Accrual.objects.filter(
register_no=OuterRef('register_no')
).values('register_no').annotate(debt=Sum('amount'))
Member.objects.annotate(
debt=Subquery(accruals.values('debt'))
).order_by('debt')
which produces this sql:
SELECT "id", "register_no", (
SELECT SUM(U0."amount") AS "debt"
FROM "accrual" U0
WHERE U0."register_no" = ("register_no")
GROUP BY U0."register_no"
) AS "debt"
FROM "member"
ORDER BY "debt" ASC

Django reverse lookup foreign keys with multiple conditions

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))
)

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

Join with more than one, specific one-to-many rows

I have to join one row from table Type1 with two rows from table Type2. There are example models of database:
class Type1(models.Model):
name = models.CharField(max_length=300)
class Type2(models.Model):
foreign = models.ForeignKey(Type1)
name = models.CharField(max_length=50)
value = models.IntegerField()
In SQL, query looks like this:
SELECT A.name, B.value, C.value
FROM Type1 A, Type2 B, Type2 C
WHERE B.foreign = C.foreign = A.id
AND B.name = _VAR1_ AND C.name = _VAR2_
How to do that query using Django methods (not raw SQL!)?
Not sure if I understand your question correctly, and know very little SQL to understand the SQL code.
Interpretation 1 is you want a reverse relationship query and a Q object for the OR part. See 'lookups that span relationship' and 'Complex lookups with Q objects' in this page.
Ex.
Type1.objects.filter(Q(type2__name = '_VAR1_' | type2__name = '_VAR2_'))
This return all rows of model Type1 that has an associated Type2 row with value VAR1 or VAR2 for name.
Interpretation 2: you want to define the relationship.
Well, that's easier. You have a Type1 row and want to associate two other rows with this one.
Query the Type1 row and save it to a variable.
t1 = Type1.object.get(id = X)
Then create type2 objects using t1 object in the foreign key:
t2 = Type2(foreign = t1, name = 'whatever', value = 'value')
t2.save()
and the same for the other object
t3 = Type2(foreign = t1, name = 'whatever2', value = 'value2')
t3.save()
Let me know if I misinterpreted the question.
Cheers.

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)