class Foo(models.Model):
name = models.CharField(max_length=20)
bar = models.ForeignKey(Branch, null=True)
class Bar(models.Model):
name = models.CharField(max_length=20)
When I do:
x = Foo.objects.get(bar_id=2)
I get:
*** FieldError: Cannot resolve keyword 'bar_id' into field. Choices are: bar, id, name
Shouldn't bar_id use the foreign key id directly to look for the matching Foo?
I understand I can query the db first to get the Bar object and then use it in the get statement, but should I waste a query on this if the id is already in the Foo table?
(I'm using an old django and python for a legacy project: django 1.4 python 2.7.2)
It's x = Foo.objects.get(bar__id=2) with double underscore.
django doc.
x = Foo.objects.get(bar__id=2)
Even though the SQL table is using bar_id as the name of the column for the ForeignKey, you need to use bar__id to traverse the FK.
Hopefully, Django will optimize this and don't do a real join to execute the query:
>>> print MyModel.objects.filter(bar__id=1).values('pk').query
SELECT "myapp_mymodel"."id" FROM "myapp_bar" WHERE "myapp_mymodel"."bar_id" = 1
# Let's compare this with a real joining... (assuming Bar has a `name` field)
>>> print MyModel.objects.filter(bar__name='Thing').values('pk').query
SELECT "myapp_mymodel"."id" FROM "myapp_bar" INNER JOIN "myapp_bar" ON (
"myapp_mymodel"."bar_id" = "myapp_bar"."id"
) WHERE "myapp_bar"."name" = Thing
Related
I am a newbie to django and was reading about select_related. I understand that whenever a foreign key is accessed django executes an additional query. But when I checked with DEBUG log in my code, it seems django executes two queries no matter if the foreign key is accessed or not. Can someone explain this behaviour ?
class Person(models.Model):
# ...
name = models.CharField(max_length=250)
class Book(models.Model):
# ...
author = models.ForeignKey(Person, on_delete=models.CASCADE)
As per doc
# Without select_related()...
b = Book.objects.get(id=4) # Executes a query.
p = b.author #Executes a query.
But with the get() it executes two queries
b = Book.objects.get(id=4) # Executes two queries (one for books one for author).
First of all, you need to call select select_related:
ids = [1,2,3,4]
query = Book.objects.filter(id__in=ids).select_related('author')
notice that I did that using the filter method and not the get method.
the reason is that select/prefetch related doesn't work with the get method.
if you still want only one object with select related you should do:
book = Book.objects.filter(id=4).select_related('author')[0]
author = book.author
or do:
book = Book.objects.select_related('author').get(id=4)
author = book.author
if you want to do it for multiple objects and get all the authors:
ids = [1,2,3,4]
query = Book.objects.filter(id__in=ids).select_related('author')
authors_in_query = [book.author for book in query]
`SELECT id,name,(case WHEN (status<>"COM" AND ( SELECT COUNT(*) FROM BAC WHERE (BAC.status<>"AM" AND CURDATE() > BAC.deadline AND BAC.act_id=Act.id) )>0 )THEN 6) as status FROM ACT)
Models
class ACT(models.Model):
name = models.CharField(max_length=80)
class BAC(models.Model):
status = models.CharField(max_length=5)
act = models.ForeignKey(ACT,on_delete=models.CASCADE())
`I am facing the problem of converting a MySQL query to Django ORM. I am trying to assign a value to a field when the count of some values in another field increases than a certain value.
Since I don't know your exact models I am just creating a random example. I hope this helps.
class Boo(models.Model):
name = models.CharField(max_length=32)
class Hoo(models.Model):
boo = models.ForeignKey(Boo)
name = models.CharField(max_length=32)
Hoo.objects.filter(name='boohoo').annotate(count=Count(Case(When(boo__name='boo', then=1))))
What this code will do is for every Hoo object with name=boohoo it will annotate a count field which will be the Count of their related Boo class, when boo__name='boo'
I'm learning Django.
I have two models as follows:
class IdentificationAddress(models.Model):
id_ident_address = models.AutoField(primary_key=True)
ident = models.ForeignKey('Ident', models.DO_NOTHING, db_column='ident')
address = models.TextField()
time = models.DateTimeField()
class Meta:
managed = False
db_table = 'identification_address'
class IdentC(models.Model):
id_ident = models.AutoField(primary_key=True)
ident = models.TextField(unique=True)
name = models.TextField()
class Meta:
managed = False
db_table = 'ident'
I am using the Django debugger:
python manage.py shell
and import the model
from invent.models import IdentC as identity
from invent.models import IdentificationAddress as ident_Address
I can access the name of a specific identity
indentity.objects.filter(ident='00LD').values('name')
it returns
<QuerySet [{'name': u'Joss'}]>
Then I can access information of a specific address:
ident_Address.objects.filter(address='LOC23').values().last()
it returns
{'times': u'{"2017-07-16"}', u'ident_id': u'00LD', 'address': u'LOC23', 'id_ident_address': 217}
but I get an error when I try to use any identity
ident_Address.objects.filter(ident='00LD').values()
or
ident_Address.objects.filter(ident_id='00LD').values()
error:
ValueError: invalid literal for int() with base 10: '00LD'
If I try this in postgres
SELECT * FROM identification_address WHERE ident= '00LD'
I don't have any problem.
I appreciate any help.
Thank you in advance
NOTE: For the sake of clarity, I've changed the name of the Ident model to IdentC
You try to match '00LD' with the field id of Ident.
Or in your first example you wrote :
indentity.objects.filter(ident='00LD').values('name')
'00LD' match with ident field of Ident
So you can filter like this :
ident_Address.objects.filter(ident__ident='00LD').values()
First ident represent your model, and the second one, the field of Ident.
If you want to filter ident_Address by Ident name :
ident_Address.objects.filter(ident__name='Joss').values()
In Django, when you want to access some field in ForeignKey table, you should use __ (Double underscore). For your case, it will be
ident_Address.objects.filter(ident__id_ident='00LD')
Thanks guys.
Sorry #Exprator I'm learning how to use the answer field, as well.
So I started debugging how Django queries are translated to SQL queries. Then I found the solution:
ident_Address.objects.filter(ident__id_ident__iexact='00LD').values()
Where first ident means field ident in IdentificationAddress model and id_ident field in identC model
Trying to run a complicated query in Django over Postgresql.
These are my models:
class Link(models.Model):
short_key = models.CharField(primary_key=True, max_length=8, unique=True, blank=True)
long_url = models.CharField(max_length=150)
class Stats_links_ads(models.Model):
link_id = models.ForeignKey(Link, related_name='link_viewed', primary_key=True)
ad_id = models.ForeignKey(Ad, related_name='ad_viewed')
views = models.PositiveIntegerField()
clicks = models.PositiveIntegerField()
I want to run using the Django ORM a query which will translate into something like so:
select a.link_id, sum(a.clicks), sum (a.views), (select long_url from links_link b where b.short_key = a.link_id_id)
from links_stats_links_ads a
group by a.link_id_id;
If i exclude the long_url field that I need I can run this code and it will work:
Stats_links_Ads.objects.all().values('link_id').annotate(Sum('views'), Sum('clicks'))
I don't know how to add the subquery in the select statement.
Thanks
You can see the raw sql behind your queries using the query attribute of Queryset.
For example, look at the sql behind my first answer using select_related, it's clear the generated sql doesn't behave as expected and accessing the long_url will result in additional queries.
Take 2
You can follow relationships using double underscore notation like this
qs = Stats_links_ads.objects
.values('link_id', 'link_id__long_url')
.annotate(Sum('views'), Sum('clicks'))
str(qs.query)
'SELECT
"stackoverflow_stats_links_ads"."link_id_id",
"stackoverflow_link"."long_url",
SUM("stackoverflow_stats_links_ads"."clicks") AS "clicks__sum",
SUM("stackoverflow_stats_links_ads"."views") AS "views__sum"
FROM "stackoverflow_stats_links_ads"
INNER JOIN "stackoverflow_link"
ON ("stackoverflow_stats_links_ads"."link_id_id" = "stackoverflow_link"."short_key")
GROUP BY
"stackoverflow_stats_links_ads"."link_id_id",
"stackoverflow_link"."long_url"'
I'm not working with any data, so I haven't verified it, but the sql looks right.
Take 1
Does not work
Can't you use .select_related? [docs]
qs = Stats_links_Ads.objects.select_related('link')
.values('link_id').annotate(Sum('views'), Sum('clicks'))
str(qs.query)
'SELECT
"stackoverflow_stats_links_ads"."link_id_id",
SUM("stackoverflow_stats_links_ads"."clicks") AS "clicks__sum",
SUM("stackoverflow_stats_links_ads"."views") AS "views__sum"
FROM "stackoverflow_stats_links_ads"
GROUP BY "stackoverflow_stats_links_ads"."link_id_id"'
I have the following models:
class Foo(models.Model):
pass
class Bar(models.Model):
foo = models.ForeignKey(Foo)
is_successful = models.BooleanField()
I would like to get all foo objects with an annotation if all of the bar objects associated with foo object have is_successful as True
So far my queryset is:
foos = Foo.objects.all().annotate(all_successful=Min('bar__is_successful'))
The idea for the all_successful annotation is that if the minimum value of all is_successful rows is 1, then all of them must be True (assuming 0 is False and 1 is True). So knowing that I can use the queryset like so:
foo = foos[0]
if foo.all_successful == 1:
print 'All bars are successful'
else:
print 'Not all bars are successful'
This works great in sqlite however it fails in PostgreSQL because PostgreSQL can't execute MIN aggregate on a boolean column. I guess this works in sqlite because sqlite treats bools as integers hence it can execute the aggregate.
My question is how can I make this queryset work in PostgreSQL without converting my is_successful field to an IntegerField?
Thanx
I know this is an old question, but I ran up against this recently. Django v1.8 now has built in support for case/when, so you can use the ORM instead of hacking around with custom SQL.
https://docs.djangoproject.com/en/1.8/ref/models/conditional-expressions/#case
Foo.objects.annotate(
all_successful=Case(
When(bar__is_successful=False, then=False),
When(bar__is_successful=True, then=True),
default=False,
output_field=BooleanField()
))
I haven't tried this out, but something similar worked for me on a recent project.
FOR DJANGO <= 1.7: to get an annotation I think you can simply use Extra
foos = Foo.objects.extra(select={'all_successful': 'CASE WHEN COUNT(b.foo) > 0 THEN 0 ELSE 1 END FROM yourapp_bar as b WHERE b.is_successful = false and b.foo = yourapp_foo.id' })
if your system is running Django 1.8+ please follow Dav3xor answer.
being inspired by https://docs.djangoproject.com/en/dev/topics/db/managers/ I suggest to use a custom manager for Bar class instead of annotation
class BarManager(models.Manager):
def get_all_successful_foos_ids(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute("""
SELECT foo, COUNT(*)
FROM yourapp_bar
GROUP BY 1
WHERE is_successful = true""") # <-- you have to write the correct table name here
result_list = []
for row in cursor.fetchall():
if row[1] > 0:
result_list.append(row[0])
return result_list
class Bar(models.Model):
foo = models.ForeignKey(Foo)
is_successful = models.BooleanField()
objects = BarManager() # here I'm changing the default manager
then, in your code:
foos = foo.objects.filter(id__in=Bar.objects.get_all_successful_foos_ids())