Django ORM query with multiple inner join - django

I want to be able to do queries involving multiple inner joins using Django ORM, here's my model (showing only relevant fields)
class PuntoMedida(models.Model):
id_punto_medida = models.AutoField(primary_key=True,db_column='id_punto_medida')
nombre = models.CharField(max_length=100, blank=False,db_column='nombre')
class Meta:
db_table = 'punto_medida'
class Instalacion(models.Model):
id_instalacion = models.AutoField(primary_key=True,db_column='id_instalacion')
activo = models.CharField(max_length=1, blank=False,default='1',db_column='activo')
usuarios=models.ManyToManyField(User,through='InstalacionUsuario')
class Meta:
db_table = 'instalacion'
class InstanciaInstalacion(models.Model):
id_instancia_instalacion = models.AutoField(primary_key=True,db_column='id_instancia_instalacion')
id_instalacion = models.ForeignKey(Instalacion, blank=False, null=False,db_column='id_instalacion')
puntos_medida=models.ManyToManyField('PuntoMedida',through='InstInstalacionPuntoMedida')
activo = models.CharField(max_length=1, blank=True,default='1',db_column='activo')
class Meta:
db_table = 'instancia_instalacion'
class InstInstalacionPuntoMedida(models.Model):
id= models.AutoField(primary_key=True,db_column='id')
id_instancia_instalacion = models.ForeignKey(InstanciaInstalacion,db_column='id_instancia_instalacion', blank=False, null=False)
id_punto_medida = models.ForeignKey('PuntoMedida',db_column='id_punto_medida', blank=False, null=False)
class Meta:
unique_together = ('id_instancia_instalacion','id_punto_medida')
db_table = 'instancia_instalacion_punto_medida'
class InstalacionUsuario(models.Model):
id= models.AutoField(primary_key=True,db_column='id')
id_instalacion = models.ForeignKey(Instalacion,db_column='id_instalacion', blank=False, null=False)
id_usuario = models.ForeignKey(User,db_column='id_usuario', blank=False, null=False)
class Meta:
unique_together = ('id_instalacion','id_usuario')
db_table = 'instalacion_usuario'
I want to obtain all the objects PuntoMedida related to all the objects Instalacion that a certain user can see. Until now, I've been able to do this using a raw sql like this one:
SELECT a.id_punto_medida, a.nombre
FROM punto_medida a
INNER JOIN instancia_instalacion_punto_medida b ON
a.id_punto_medida=b.id_punto_medida
INNER JOIN instancia_instalacion c ON
b.id_instancia_instalacion=c.id_instancia_instalacion
INNER JOIN instalacion d ON
c.id_instalacion=d.id_instalacion
INNER JOIN instalacion_usuario f ON
d.id_instalacion=f.id_instalacion
AND c.activo='1'
AND d.activo='1'
and f.id_usuario=7
However, I am looking forward to stop using raw queries and only use Django ORM just in case I need to change my database in the future (currently using PostgreSQL, might migrate to Oracle). I've been trying to create the equivalent ORM query with no success. Any ORM expert out there than can help me with this syntax or give me an example of where to start?Thanks in advance...

Can you try this:
PuntoMedida.objects.filter(
instanciainstalacion__activo='1',
instanciainstalacion__id_instalacion__activo='1',
instanciainstalacion__id_instalacion__instalacionusuario_set__id_usuario=7,
)

Related

How to create complex multi model join query set with Django ORM

I want to create a query set with multiple tables. I cannot find any resource that helps
Beside some small 2 table join examples, I cannot find any resource that is helpful for this, not in stackoverflow, the Django docs or even in the ORM Cookbook.
Below is firstly the SQL I want to recreate followed by the models classes, simplified for the purpose of this question. They have in fact a LOT more fields.
SELECT doc_uid,
docd_filename,
doct_name,
docd_media_url
FROM vugd_detail,
vug_virtual_user_group,
vugdoc_vug_docs,
doc_documents,
docd_detail,
doct_type
WHERE vugd_vug_uid = vug_uid
AND vugdoc_vug_uid = vug_uid
AND vugdoc_doc_uid = doc_uid
AND docd_doc_uid = doc_uid
AND doct_uid = doc_doct_uid
AND vugd_status = 1
AND docd_status = 1
AND NOW() BETWEEN vugd_start_date AND vugd_end_date
AND NOW() BETWEEN docd_start_date AND docd_end_date
AND vug_uid = {{Some ID}};
class VugVirtualUserGroup(models.Model):
objects = None
vug_uid = models.AutoField(primary_key=True)
vug_name = models.CharField(unique=True, max_length=30)
vug_short_code = models.CharField(max_length=6)
vug_created_on = models.DateTimeField()
vug_created_by = models.CharField(max_length=30)
class Meta:
managed = False
db_table = 'vug_virtual_user_group'
app_label = 'main'
class VugdDetail(models.Model):
objects = None
vugd_uid = models.AutoField(primary_key=True)
vugd_vug_uid = models.ForeignKey(VugVirtualUserGroup, models.DO_NOTHING, db_column='vugd_vug_uid')
vugd_long_name = models.CharField(max_length=50)
vugd_status = models.IntegerField()
vugd_start_date = models.DateTimeField()
vugd_end_date = models.DateTimeField()
class Meta:
managed = False
db_table = 'vugd_detail'
app_label = 'main'
class VugdocVugDocs(models.Model):
onjects = None
vugdoc_uid = models.AutoField(primary_key=True)
vugdoc_vug_uid = models.ForeignKey(VugVirtualUserGroup, models.DO_NOTHING, db_column='vugdoc_vug_uid')
vugdoc_doc_uid = models.ForeignKey(DocDocuments, models.DO_NOTHING, db_column='vugdoc_doc_uid')
class Meta:
managed = False
db_table = 'vugdoc_vug_docs'
app_label = 'main'
class DocdDetail(models.Model):
objects = None
docd_uid = models.AutoField(primary_key=True)
docd_doc_uid = models.ForeignKey(DocDocuments, models.DO_NOTHING, db_column='docd_doc_uid')
docd_filename = models.CharField(max_length=200)
docd_media_url = models.CharField(max_length=300)
docd_status = models.IntegerField()
docd_start_date = models.DateTimeField()
docd_end_date = models.DateTimeField()
class Meta:
managed = False
db_table = 'docd_detail'
app_label = 'main'
class DoctType(models.Model):
objects = None
doct_uid = models.AutoField(primary_key=True)
doct_vug_uid = models.ForeignKey('VugVirtualUserGroup', models.DO_NOTHING, db_column='doct_vug_uid')
doct_name = models.CharField(max_length=30)
doct_start_date = models.DateTimeField()
doct_end_date = models.DateTimeField()
class Meta:
managed = False
db_table = 'doct_type'
app_label = 'main'
class DocDocuments(models.Model):
object = None
doc_uid = models.AutoField(primary_key=True)
doc_doct_uid = models.ForeignKey('DoctType', models.DO_NOTHING, db_column='doc_doct_uid')
class Meta:
managed = False
db_table = 'doc_documents'
app_label = 'main'
As one can see the actual query is not so complicated.
The goal here is just to get the list of valid(active, not deleted) files avilable for a specific VUG(Simple user group), if the group has a valid status.
So if there is any Django ORM experts who can help with what is really a common SQL type that should be should be possible to be converted to a Django ORM script, your help will sincerely be appreciated.
The Django way to query multiple related tables is to chain them using dot notation.
A sudo code example might help point you in the right direction.
vug_virtual_user_group = VugVirtualUserGroup.objects.get(vug_uid="Some ID")
print(f'vug_virtual_user_group {vug_virtual_user_group}')
A lookup using the foreign key to get all related objects
vugdoc_vugdocs = vug_virtual_user_group.vugdocvugdocs_set.all()
print(f'vugdoc_vugdocs {vugdoc_vugdocs}')
This example assumes that the default modelmanger objects is not overridden by objects = None.

Nested Query / not directly related field in Django

If the models are as follows,
class Subject(BaseModel):
name = models.CharField(db_column='name', max_length=200, blank=False, null=False, unique=True)
class Meta:
managed = True
db_table = 'Subject'
class Topic(BaseModel):
name = models.CharField(db_column='name', max_length=200, blank=False, null=False, unique=True)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE, null=False, related_name='subject_topic')
class Meta:
managed = True
db_table = 'Topic'
class Question(BaseModel):
topic = models.ForeignKey(Topic, on_delete=models.CASCADE, null=False, related_name='question_topic')
class Meta:
managed = True
db_table = 'Question'
How can I make a query Question for a subject.
questions = Question.objects.filter(topic_in=Topic.objects.get(subject=subject).only('id').all())
but it's not working. Any help would be really great help.
Your current "inner queryset" won't give you multiple values, it only returns 1.
Topic.objects.get(subject=subject).only('id').all()
You are using .objects.get() which returns a model instance, so for example .only() or .all() on that won't work unless your model has such a defined function.
Furthermore you don't really need 2 querysets, you can get your result with only one:
my_subject_ids = [...] # these are the ids of Subject models you want
Question.objects.filter(topic__subject_id__in=my_subject_ids)
You can also query for Subject.name, just use the following for this: topic__subject__name__in=my_list.
If you want to query for a specific Subject-instance you can use topic__subject=my_obj.
You also might wanna take a look at the docs for more information on that.

Stop Django from autofilling manytomany fields in admin

I have the following models:
class Color(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
def __str__(self):
return self.name
class Flower(models.Model):
flower_number = models.PositiveSmallIntegerField(
default=1,blank=True, null=True)
petal_color = models.ManyToManyField(Color,blank=True, related_name="%(app_label)s_%(class)s_petal",
related_query_name="%(app_label)s_%(class)s")
petal_outer_color = models.ManyToManyField(Color,blank=True, related_name="%(app_label)s_%(class)s_petal_outer",
related_query_name="%(app_label)s_%(class)s")
class Meta:
abstract = True
class Plant(Flower):
name = models.CharField(max_length=50, null=False, blank=False, unique=True)
On the Admin I just have:
admin.site.register(Plant)
When I go into the Django admin and fill out either of the manytomany petal_color or petal_outer_color with data the other manytomany field automatically gets filled when it saves. How do I stop this from happening? Nothing shows up as an error and I tried going back and deleting and re-entering data but it still happens
Try using symmetrical=False in the ManyToManyField, that might be causing the issue here as you have two M2M fields going to the same model.
Read up on symmetrical in the Django docs.
Do something like this
class Flower(models.Model):
flower_number = models.PositiveSmallIntegerField(
default=1,blank=True, null=True)
petal_color = models.ManyToManyField(Color,blank=True, symmetrical=False related_name="%(app_label)s_%(class)s_petal",
related_query_name="%(app_label)s_%(class)s")
petal_outer_color = models.ManyToManyField(Color,blank=True, symmetrical=False, related_name="%(app_label)s_%(class)s_petal_outer",
related_query_name="%(app_label)s_%(class)s")
class Meta:
abstract = True
By default, the value of symmetrical is True for Many to Many Field which is a bi-directional relationship.
The ManyToManyField is assumed to be symmetrical – that is, if I am your friend, then you are my friend.

How to reduces number of sqls generating Django restful services

I need help to improve API performance. Below is my approach also i have tried to do other combination of fields with select_related or prefetch_related but still i am getting like 400sqls.
model.py
class VisVisits(models.Model):
visit_id = models.IntegerField(primary_key=True)
null=True)
class Meta:
managed = False
db_table = 'vis_visits'
def __str__(self):
return str(self.visit_id)
class VisVisitData(models.Model):
vdata_id = models.IntegerField(primary_key=True)
app_local_id = models.IntegerField(blank=True, null=True)
visit = models.ForeignKey('VisVisits', models.DO_NOTHING, blank=True, null=True, related_name='data')
class Meta:
managed = False
db_table = 'vis_visit_data'
def __str__(self):
return str(self.vdata_id)
Serializer
class VisVisitDataSerializer(serializers.ModelSerializer):
class Meta:
model = VisVisitData
field = '__all__'
class VisVisitsSerializer(serializers.ModelSerializer):
data = VisVisitDataSerializer(many=True)
class Meta:
model = VisVisits
fields = ('visit_id','data')
views.py
visit_data = VisVisits.objects.filter(is_valid=1,user_id=u).prefetch_related('school_program__school')
visit_data_serializer = VisVisitsSerializer(visit_data,context={'request':request},many=True)
To reduce the number of queries you must prefetch the data that you serialize in the VisVisitsSerializer, i.e. data. In this serializer you don't include school_program so it's not necessary prefetch this. You can use the django debug toolbar (https://django-debug-toolbar.readthedocs.io/en/latest/) to inspect the repeated queries and find out what to put in select_releted/prefetch_related

don't want inner joins on django models

i've migrated an existing database that i would like to work with django. i have the following models:
class Device(models.Model):
class Meta:
db_table = u'DEVICES'
managed=False
id = models.CharField(primary_key=True, max_length=22, db_column='DEVICE_ID')
name = models.CharField(max_length=40, db_column='DEVICE_NAME')
status = models.CharField(max_length=10, db_column='STATUS')
class DevicePort(models.Model):
class Meta:
db_table = u'DEVICE_PORT'
managed=False
id = models.CharField( primary_key=True, max_length=22, db_column='DEVICE_PORT_ID')
device = models.OneToOneField(Device, db_column='DEVICE_ID')
type = models.CharField( max_length=22, db_column='PORT_TYPE_ID')
port_num = models.CharField( max_length=30, db_column='DEVICE_PORT_NUM')
class IP(models.Model):
class Meta:
db_table = u'IP_NODE'
managed=False
ip_address = models.CharField(primary_key=True, max_length=15, db_column='IP_NODE_NO')
hostname = models.CharField(max_length=40, db_column='IP_HOST')
port = models.OneToOneField(DevicePort, db_column='DEVICE_PORT_ID')
status = models.CharField(max_length=50, db_column='IP_NODE_STATUS')
i want a list of Devices and their relational IP.ip_address and IP.hostnames. In SQL, i would do something like:
SELECT UNIQUE
d.device_name device
FROM
IP_NODE c,
DEVICES d,
DEVICE_PORT e
WHERE
c.ip_node_no = b.ip_node_no
AND c.device_port_id = e.device_port_id
AND e.device_id = d.device_id
AND d.device_name LIKE 'something';
How do i do this with my django models?
So instead of an inner join you want...and implicit join? Is there something I'm missing here because an implicit join is slower than an explicit one most all cases.
[http://postgresql.1045698.n5.nabble.com/explicit-JOIN-faster-than-implicit-td1920335.html][1]
I can't see any reason you would want to do this, but as stated above you can certainty get this by writing your own queries with raw, or even connection.cursor. Maybe even could pull it off with extra(tables=[...]), though I'm not positive as I've never used tables like this.