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

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.

Related

Compare two different classes based on their values

I'm trying to compare two variables based on their actual values but it's just not working and I think it's because they are from different classes.
Here's an example :
models = Model_info.objects.all()
m = 'X-POWER 3'
for model in models:
if m == model:
check = 'accepted'
print(check)
break
else:
check = 'rejected'
print(check)
print(f'final resulte is {check}')
So Knowing that the value of variable m does exist in the queryset models and :
type(m) = < class 'str' >
type(model) = < class 'application.models.Model_info' >
is there any method to compare the value of two variables no matter what class they belong to.
You cannot make a comparison with a queryset, you need to do this with a field name of your choosing.
Here's an example:
Assuming ModelInfo had a field like name, all you have to do is
compare the string m with the model instance's field(assuming the field name is also a string), i.e:
m = 'X-POWER 3'
for model in models:
if m == model.name:
check = 'accepted'
... # continuation of your code

Django/Postgres: Choose from DISTINCT ON sets

I have these models:
class Product(Model):
...
class Scanning(Model):
product = ForeignKey(..)
datetime = DateTimeField(...)
...
I'm trying to get one scanning for each product where the scanning is a latest one from product.scanning.all() set.
s1 = (product1,01.01.1000)
s2 = (product2,01.01.1200)
s3 = (product1,01.01.1900)
s4 = (product2,01.01.1988)
s5 = (product3,01.01.2015)
s6 = (product3,01.01.1970)
would return <s4,s3,s5>
Scanning.objects.filter(product__user=u,product__active=True).distinct('product_id').order_by('datetime')
Raises exception:
ProgrammingError: SELECT DISTINCT ON expressions must match initial
ORDER BY expressions LINE 1: SELECT DISTINCT ON
("productapp_scanning"."product_id") "pro...
^
How to make it work?
With postgres, you cannot do distinct on a field, unless it's also what you sort by:
Scanning.objects.filter(product__user=u,product__active=True).distinct('product_id').order_by('product_id', 'datetime')
If that's not good enough, one solution is to make a double query like this:
q1 = Scanning.objects.filter(product__user=u,product__active=True).values('product_id').distinct().annotate(x=Max('id'))
q2 = Scanning.objects.filter(id__in=[i["x"] for i in q1])

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

What is the difference between these two Django Q-based queries:

Assuming a simple model like this:
class Item(models.Model):
name = models.CharField(max_length=10)
class Relation(models.Model):
item = models.ForeignKey(Item)
weight = models.IntegerField()
And a couple of Q objects like this:
some = Q(relation__x__gt=3)
others = Q(relation__x=7)
What is the semantic difference between:
first = Item.objects.filter(some, ~others)
and
second = Item.objects.filter(some).exclude(others)
NOTE: Querying through the relation appears to be different than querying a single simple object. The SQL generated for the two above queries is different.
Here's the generated SQL for first:
SELECT "item_item"."id", "item_item"."name"
FROM "item_item"
INNER JOIN "item_relation"
ON ("item_item"."id" = "item_relation"."item_id")
WHERE ("item_relation"."weight" > 3
AND NOT ("item_item"."id" IN
(SELECT U1."item_id"
FROM "item_relation" U1
WHERE (U1."weight" = 7 AND U1."item_id" IS NOT NULL))))
And the SQL for second:
SELECT "item_item"."id", "item_item"."name"
FROM "item_item"
INNER JOIN "item_relation"
ON ("item_item"."id" = "item_relation"."item_id")
INNER JOIN "item_relation" T3
ON ("item_item"."id" = T3."item_id")
WHERE ("item_relation"."weight" > 3 AND NOT (T3."weight" = 7 ))

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)