my input query is
query = "select * from tab1 left join tab2 on tab2.patient_id =tab1.patient_id ,tab3 left join tab4 on tab4.patient_id =tab3.patient_id"
data = model_name.objects.raw(query)
How do you retrieve values from a RawQuerySet?
The result obtained by making raw queries using raw method of Manager generates instances similar to instances generated using get or filter method. To get a field simply do obj_name.attr.
For eg:
class Tab(models.Model):
field1 = models.BooleanField()
field2 = models.PositiveIntegerField()
query = "select * from app_name_tab"
objs = Tab.objects.raw(query)
for obj in objs:
print obj.field1, obj.field2
For more info, refer to https://docs.djangoproject.com/en/dev/topics/db/sql/
Related
I have three models
class A(models.Model):
field1 = models.IntegerField()
class B(models.Model):
id_a = models.ForeignKey(A,on_delete=models.CASCADE)
field1 = models.IntegerField()
field2 = models.IntegerField()
class C(models.Model):
id_a = models.ForeignKey(A,on_delete=models.CASCADE)
field1 = models.IntegerField()
field2 = models.IntegerField()
I want to write a request that looks like this: SELECT * FROM B,C,A WHERE B.id_a=C.id_a WHERE A.id_a=2 and display all the attributes of the two tablesHere is what I tried to do:
a_id_att = 1
data = B.objects.filter(id_a=C.objects.filter(id_a=a_id_att)[0])
It does not work. How to write the join and make to display all the attributes of the tables?
The SQL statement that you wrote seems strange.
SELECT * FROM B, C, A
WHERE B.id_a = C.id_a
AND A.id_a = 2
It seems that you want a single row from A and then all related rows from B and C, which your SQL query does NOT achieve.
Did you mean something like this:
SELECT * FROM B, C, A
WHERE A.id = 2
AND B.id_a = A.id
AND C.id_a = A.id
You can achieve something like that in Django using prefetch_related(), which builds a query so that the related rows are also loaded into memory in the first query and not in subsequent queries.
# this will return a queryset with a single element, or empty
qs = A.objects.prefetch_related('b_set', 'c_set').filter(id=2)
for elem in qs: # here the single DB query is made
print(elem.field1) # A.field1
for det in elem.b_set.all():
print(det.field1) # B.field1, does NOT make another DB query
print(det.field2) # B.field2, does NOT make another DB query
for det in elem.c_set.all():
print(det.field1) # C.field1, does NOT make another DB query
print(det.field2) # C.field2, does NOT make another DB query
Note: I use b_set here because that is the default for the ForeignKey field; this changes if the field would specify a different related_name.
Does this address and solve your issue?
I need to use raw sql in my django project. I'm using count command and then I associated it with as command like "the_count" but i got an error. The error like this, the_count does not exist.
And my my code here,
# First Model
class AModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_lenth=100)
# Second Model
class BModel(models.Model):
a_model = models.ForeignKey(AModel, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
query = 'SELECT "app_AModel"."id",
(SELECT COUNT(*) FROM "app_BModel" INNER JOIN ON ("app_BModel"."user_id" = "app_AModel"."user_id") WHERE ("app_BModel"."a_model_id" = "app_AModel"."id")) AS "the_count"
FROM "app_AModel"
WHERE ("the_count" = 0)'
BModel.objects.raw(query)
Thank you for yours helps...
From postgreSQL documentation
An output column's name can be used to refer to the column's value in
ORDER BY and GROUP BY clauses, but not in the WHERE or HAVING clauses;
there you must write out the expression instead.
Also you probably want to use GROUP BY and HAVING on my_app table instead so something in a line of following:
SELECT a_model_id, COUNT(*) from app_BModel GROUP BY a_model_id HAVING count(*) > 0
I'm using Django, Python 3.7, and PostgreSQL 9.5. I have these models:
class Article(models.Model):
...
label = models.TextField(default='', null=True)
class Label(models.Model):
name = models.CharField(max_length=200)
I want to write a Django query that retrieves all the articles whose label contains a name from the Labels table. In PostGres, I can structure my query like so:
select a.* from myapp_article a join myapp_label l on a.label ilike '%' || l.name || '%';
but I have no idea how to pull this off in Django on account of the "on" clause and "ilike". How do I pull this off?
If you've to do a case insensitive search on Article's label for matching names, then you can use regex and pass it a flat list of all the label names like so:
Article.objects.filter(label__iregex=r'(' + '|'.join(Label.objects.all().values_list('name', flat=True)) + ')')
What the above query does is, it makes a flat list of labels:
['label1' , 'label2', 'label3']
and then the string is joined like this:
'(label1|label2|label3)'
and a similar SQL query is used:
SELECT * from FROM "app_article" WHERE "app_article"."label" ~* (label1|label2|label3)
Otherwise, for case sensitive approach, you can use this:
names_list = Label.objects.all().values_list('name', flat=True)
Article.objects.filter(label__in=names_list)
This wouldn't translate into same SQL query, but would yield the same results, using an inner query.
inner_query = Label.objects.annotate(article_label=OuterRef('label')).filter(article_label__icontains=F('name'))
articles = Article.objects.annotate(labels=Subquery(inner_query.values('name')[:1])).filter(labels__isnull=False)
This should roughly should translate to this:
select a.* from myapp_article a where exists (select l.* from myapp_label l where a.label ilike '%' || l.name || '%')
But due to a current issue in Django regarding using OuterRef's in annotations, this approach doesn't work. We need to use a workaround suggested here until the issue is fixed to make this query work, like this:
Define a custom expression first
class RawCol(Expression):
def __init__(self, model, field_name):
field = model._meta.get_field(field_name)
self.table = model._meta.db_table
self.column = field.column
super().__init__(output_field=CharField())
def as_sql(self, compiler, connection):
sql = f'"{self.table}"."{self.column}"'
return sql, []
Then build your query using this expression
articles = Article.objects.all().annotate(
labels=Subquery(
Label.objects.all().annotate(
article_label=RawCol(Article, 'label')
).filter(article_label__icontains=F('name')).values('name')[:1]
)
).filter(labels__isnull=False)
This should return instances of Article model whose label field contain a value from the name field of Label model
In your class Article you will have to declare label as foreignkey to class Label
class Article(models.Model):
...
label = models.ForeignKey(Label, default='', on_delete=models.CASCADE)
And then you can access it.
i have two tables like categories and items in models items has foreign key of categories,here we have 4 categories and 12 items where each category has 3 items how to write a query set to get query set to get items with same category
i know how to write Sql query (select * from category where category_id =1;). how to write it in d'jango query set.
You can achieve it by using Django Queries:
Item.objects.filter(category__id=1)
As mentioned in the Kamil's answer, you could use filters or if you want to use SQL query as is, you could also use raw queries. An example (taken from official docs) -
class Person(models.Model):
first_name = models.CharField(...)
last_name = models.CharField(...)
birth_date = models.DateField(...)
And querying it would be -
# querying with SQL raw query
for p in Person.objects.raw('SELECT * FROM myapp_person'):
And in your case -
# assuming your query is correct
Item.objects.raw('select * from category where myapp_category_id = 1')
I have 2 Django models, ModelA with an ArrayField that is used to store a large list of primary key values (possibly 50k+ list)
class ModelA(models.Model):
pk_values = ArrayField(models.IntegerField())
class CustomManager(manager.Manager):
def get_for_index(self, index_id):
qs = self.get_queryset()
obj = ModelA.objects.get(pk=index_id)
return qs.filter(id__in=obj.pk_values)
class ModelB(models.Model):
# [...] some fields
objects = CustomManager()
This works:
qs = ModelB.objects.get_for_index(index_id=1)
However, this would be super slow where "pk_values" is a large list.
So I tried doing raw SQL queries:
class CustomManager(manager.Manager):
def get_for_index(self, index_id):
qs = self.get_queryset()
sql = "SELECT * FROM myapp_model_b JOIN myapp_model_a ON myapp_model_b.id = ANY(myapp_model_a.pk_values) WHERE myapp_model_a.id = '%s'" % index_id
return qs.raw(sql)
But this returns a django.db.models.query.RawQuerySet instance.
But with this, I cant do things like queryset.values() afterwards.
How can I convert this to a normal Django queryset?
Is there a better way of doing this?
Docs:
ArrayField https://docs.djangoproject.com/en/2.0/ref/contrib/postgres/fields/#arrayfield
Custom Manager https://docs.djangoproject.com/en/2.0/topics/db/managers/#custom-managers-and-model-inheritance
Raw queries https://docs.djangoproject.com/en/2.0/topics/db/sql/#performing-raw-sql-queries
You can use a RawSQL expression:
ModelB.objects.filter(id__in=RawSQL(
'SELECT unnest(a.pk_values) FROM app_modela a WHERE a.id = %s',
[index_id]
))
Alternatively you can reproduce the exact query you have in your question with extra():
ModelB.objects.extra(
tables=['foo_modela'],
where=[
'"app_modelb"."id" = ANY("app_modela"."pk_values")',
'"app_modela"."id" = %s',
],
params=[index_id],
)
Update: I got something working using .extra()
class CustomManager(manager.Manager):
def get_for_index(self, index_id):
qs = self.get_queryset()
sql = "myapp_model_b.id IN (SELECT UNNEST(myapp_model_a.pk_values) FROM myapp_model_a WHERE myapp_model_a.id='%s')" % index_id
return qs.extra(where=[sql])
Docs: https://docs.djangoproject.com/en/2.0/ref/models/querysets/#django.db.models.query.QuerySet.extra