I am new in django. I want to create a query in django I tried with select_related but I don't know how to insert the second part of the condition: AND model1.qty >= model2.items
I've tried:
Model1.objects.select_related('model2).filter(model1.qty__gte=?)
But it's not working properly.
Below is the SQL query which I want to implement with django queryset:
SELECT model1.name,model2.name WHERE model1.id=model2.model1.id AND model1.qty >= model2.items
My models:
class Article(models.Model):
date_crea = models.DateTimeField('Créer le', auto_now_add=True)
designation = models.TextField('designation', max_length=500)
seuil = models.IntegerField('Seuil d\'alerte')
class Stock(models.Model):
date_crea = models.DateTimeField('Créer le', auto_now_add=True)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
qte_reel = models.IntegerField('stock reel',default=0)
You use an F expression to refer to the value of a field in the database. There have to be relations to follow from the current object to the field on the object that you want to compare against.
I'm not clear how the question relates to the posted models, but an F expression can follow a foreign key so
Stock.objects.filter( qte_reel__gte = F( 'article__seuil' ))
would work, if those fields are the ones you want to compare.
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]
I have 2 models which i wanna join
class CollectionBook(models.Model):
collection = models.ForeignKey('Collection')
book = models.ForeignKey('Book')
class Meta:
unique_together = (('collection', 'book'))
class Book(models.Model):
main_author = models.ForeignKey('Author', verbose_name=u'Main author')
publisher = models.ForeignKey('Publisher', verbose_name=u'Publisher')
title = models.CharField(unique=False, max_length=255, verbose_name=u'Title')
text = models.TextField(verbose_name=u'Book text', max_length=523000)
I tried to do it this way
book = Book.objects.extra(
select = {'id':'mainapp_collectionbook.book_id'})
book.query.join((None,'mainapp_collectionbook',None,None))
connection = (
Book._meta.db_table,
CollectionBook.book.field,
)
book.query.join(connection, promote=True)
But it didn't work out and gave me error
Could you offer me another pythonic solutions of this problem or improve my way of doing it, I don't wanna write sql query, I hope that there are better django orm functions for this
Taking the clarification from the comment:
I have another table "Collection". I want select books where collection to which book belongs is in allowed collection list. I generate collection list before this query
First, replace your explicit CollectionBook table with a ManyToManyField on either Book or Collection. For this example I'll assume it's on Book, since that keeps the syntax clearer and the Collection model isn't shown.
class Book(models.Model):
main_author = models.ForeignKey('Author', verbose_name=u'Main author')
publisher = models.ForeignKey('Publisher', verbose_name=u'Publisher')
title = models.CharField(unique=False, max_length=255, verbose_name=u'Title')
text = models.TextField(verbose_name=u'Book text', max_length=523000)
collection = models.ManyToManyField('Collection')
Then you can use __ syntax to follow relationships:
Books.objects.filter(collection__in=SOME_LIST_OF_COLLECTIONS).distinct()
If you need additional information on the book/collection relation, EG a date collected or something, specify a through argument to the ManyToManyField.
if I understand correctly, you can try it, but don't forget to change the YOU_CONDITION
allow_collections = CollectionBook.objects.filter(YOU_CONDITION)
books_pks = allow_collections.values_list('book__pk', flat=true)
Book.objects.filter(pk__in=books_pks)
I have a simple Relation model, where a user can follow a tag just like stackoverflow.
class Relation(models.Model):
user = AutoOneToOneField(User)
follows_tag = models.ManyToManyField(Tag, blank=True, null=True, through='TagRelation')
class TagRelation(models.Model):
user = models.ForeignKey(Relation, on_delete=models.CASCADE)
following_tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
pub_date = models.DateTimeField(default=timezone.now)
class Meta:
unique_together = ['user', 'following_tag']
Now, to get the results of all the tags a user is following:
kakar = CustomUser.objects.get(email="kakar#gmail.com")
tags_following = kakar.relation.follows_tag.all()
This is fine.
But, to access intermediate fields I have to go through a big list of other queries. Suppose I want to display when the user started following a tag, I will have to do something like this:
kakar = CustomUser.objects.get(email="kakar#gmail.com")
kakar_relation = Relation.objects.get(user=kakar)
t1 = kakar.relation.follows_tag.all()[0]
kakar_t1_relation = TagRelation.objects.get(user=kakar_relation, following_tag=t1)
kakar_t1_relation.pub_date
As you can see, just to get the date I have to go through so much query. Is this the only way to get intermediate values, or this can be optimized? Also, I am not sure if this model design is the way to go, so if you have any recomendation or advice I would be very grateful. Thank you.
You need to use Double underscore i.e. ( __ ) for ForeignKey lookup,
Like this :
user_tags = TagRelation.objects.filter(user__user__email="kakar#gmail.com").values("following_tag__name", "pub_date")
If you need the name of the tag, you can use following_tag__name in the query and if you need id you can use following_tag__id.
And for that you need to iterate through the result of above query set, like this:
for items in user_tags:
print items['following_tag__name']
print items['pub_date']
One more thing,The key word values will return a list of dictionaries and you can iterate it through above method and if you are using values_list in the place of values, it will return a list of tuples. Read further from here .
I have been using Django for a couple of years now but I am struggling today with adding a HAVING constraint to a GROUP BY.
My queryset is the following:
crm_models.Contact.objects\
.filter(dealercontact__dealer__pk__in=(265,),
dealercontact__activity='gardening',
date_data_collected__gte=datetime.date(2012,10,1),
date_data_collected__lt=datetime.date(2013,10,1))\
.annotate(nb_rels=Count('dealercontact'))
which gives me the following MySQL query:
SELECT *
FROM `contact`
LEFT OUTER JOIN `dealer_contact` ON (`contact`.`id_contact` = `dealer_contact`.`id_contact`)
WHERE (`dealer_contact`.`active` = True
AND `dealer_contact`.`activity` = 'gardening'
AND `contact`.`date_data_collected` >= '2012-10-01'
AND `contact`.`date_data_collected` < '2013-10-01'
AND `dealer_contact`.`id_dealer` IN (265))
GROUP BY `contact`.`id_contact`
ORDER BY NULL;
I would get exactly what I need with this HAVING constraint:
HAVING SUM(IF(`dealer_contact`.`type`='customer', 1, 0)) = 0
How can I get this fixed with a Django Queryset? I need a queryset in this instance.
Here I am using annotate only in order to get the GROUP BY on contact.id_contact.
Edit: My goal is to get the Contacts who have no "customer" relation in dealercontact but have "ref" relation(s) (according to the WHERE clause of course).
Models
class Contact(models.Model):
id_contact = models.AutoField(primary_key=True)
title = models.CharField(max_length=255L, blank=True, choices=choices_custom_sort(TITLE_CHOICES))
last_name = models.CharField(max_length=255L, blank=True)
first_name = models.CharField(max_length=255L, blank=True)
[...]
date_data_collected = models.DateField(null=True, db_index=True)
class Dealer(models.Model):
id_dealer = models.AutoField(primary_key=True)
address1 = models.CharField(max_length=45L, blank=True)
[...]
class DealerContact(Auditable):
id_dealer_contact = models.AutoField(primary_key=True)
contact = models.ForeignKey(Contact, db_column='id_contact')
dealer = models.ForeignKey(Dealer, db_column='id_dealer')
activity = models.CharField(max_length=32, choices=choices_custom_sort(ACTIVITIES), db_index=True)
type = models.CharField(max_length=32, choices=choices_custom_sort(DEALER_CONTACT_TYPE), db_index=True)
I figured this out by adding two binary fields in DealerContact: is_ref and is_customer.
If type='ref' then is_ref=1 and is_customer=0.
Else if type='customer' then is_ref=0 and is_customer=1.
Thus, I am now able to use annotate(nb_customers=Sum('is_customer')) and then use filter(nb_customers=0).
The final queryset consists in:
Contact.objects.filter(dealercontact__dealer__pk__in=(265,),
dealercontact__activity='gardening',
date_data_collected__gte=datetime.date(2012,10,1),
date_data_collected__lt=datetime.date(2013,10,1))\
.annotate(nb_customers=Sum('dealercontact__is_customer'))\
.filter(nb_customers=0)
Actually there is a way you can add your own custom HAVING and GROUP BY clauses if you need.
Just use my example with caution - if Django ORM code/paths will change in future Django versions, you will have to update your code too.
Image you have Book and Edition models, where for each book there can be multiple editions and you want to select first US edition date within Book queryset.
Adding custom HAVING and GROUP BY clauses in Django 1.5+:
from django.db.models import Min
from django.db.models.sql.where import ExtraWhere, AND
qs = Book.objects.all()
# Standard annotate
qs = qs.annotate(first_edition_date=Min("edition__date"))
# Custom HAVING clause, to limit annotation by US country only
qs.query.having.add(ExtraWhere(['"app_edition"."country"=%s'], ["US"]), AND)
# Custom GROUP BY clause will be needed too
qs.query.group_by.append(("app_edition", "country"))
ExtraWhere can contain not just fields, but any raw sql conditions and functions too.
Are you not using raw query just because you want orm object? Using Contact.objects.raw() generate instances similar filter. Refer to https://docs.djangoproject.com/en/dev/topics/db/sql/ for more help.
My goal is to get the Contacts who have no "customer" relation in
dealercontact but have "ref" relation(s) (according to the WHERE
clause of course).
This simple query fulfills this requirement:
Contact.objects.filter(dealercontact__type="ref").exclude(dealercontact__type="customer")
Is this enough, or do you need it to do something more?
UPDATE: if your requirement is
Contacts that have a "ref" relations, but do not have "customer"
relations with the same dealer
you can do this:
from django.db.models import Q
Contact.objects.filter(Q(dealercontact__type="ref") & ~Q(dealercontact__type="customer"))
Is there a way in Django to write a query using the ORM, not raw SQL that allows you to JOIN on another table without there being a foreign key? Looking through the documentation it appears in order for the One to One relationship to work there must be a foreign key present?
In the models below I want to run a query with a JOIN on UserActivity.request_url to UserActivityLink.url.
class UserActivity(models.Model):
id = models.IntegerField(primary_key=True)
last_activity_ip = models.CharField(max_length=45L, blank=True)
last_activity_browser = models.CharField(max_length=255L, blank=True)
last_activity_date = models.DateTimeField(auto_now_add=True)
request_url = models.CharField(max_length=255L, blank=True)
session_id = models.CharField(max_length=255L)
users_id = models.IntegerField()
class Meta:
db_table = 'user_activity'
class UserActivityLink(models.Model):
id = models.IntegerField(primary_key=True)
url = models.CharField(max_length=255L, blank=True)
url_description = models.CharField(max_length=255L, blank=True)
type = models.CharField(max_length=45L, blank=True)
class Meta:
db_table = 'user_activity_link'
The link table has a more descriptive translation of given URLs in the system, this is needed for some reporting the system will generate.
I've tried creating the foreign key from UserActivity.request_url to UserActivityLink.url but it fails with the following error: ERROR 1452: Cannot add or update a child row: a foreign key constraint fails
No, there isn't an effective way unfortunately.
The .raw() is there for this exact thing. Even if it could it probably would be a lot slower than raw SQL.
There is a blogpost here detailing how to do it with query.join() but as they themselves point out. It's not best practice.
Just reposting some related answer, so everyone could see it.
Taken from here: Most efficient way to use the django ORM when comparing elements from two lists
First problem: joining unrelated models
I'm assuming that your Model1 and Model2 are not related,
otherwise you'd be able to use Django's related objects
interface. Here are two approaches you could take:
Use extra and a SQL subquery:
Model1.objects.extra(where = ['field in (SELECT field from myapp_model2 WHERE ...)'])
Subqueries are not handled very efficiently in some databases
(notably MySQL) so this is probably not as good as #2 below.
Use a raw SQL query:
Model1.objects.raw('''SELECT * from myapp_model1
INNER JOIN myapp_model2
ON myapp_model1.field = myapp_model2.field
AND ...''')
Second problem: enumerating the result
Two approaches:
You can enumerate a query set in Python using the built-in enumerate function:
enumerate(Model1.objects.all())
You can use the technique described in this answer to do the enumeration in MySQL. Something like this:
Model1.objects.raw('''SELECT *, #row := #row + 1 AS row
FROM myapp_model1
JOIN (SELECT #row := 0) rowtable
INNER JOIN myapp_model2
ON myapp_model1.field = myapp_model2.field
AND ...''')
The Django ForeignKey is different from SQL ForeignKey. Django ForeignKey just represent a relation, it can specify whether to use database constraints.
Try this:
request_url = models.ForeignKey(UserActivityLink, to_field='url_description', null=True, on_delete=models.SET_NULL, db_constraint=False)
Note that the db_constraint=False is required, without it Django will build a SQL like:
ALTER TABLE `user_activity` ADD CONSTRAINT `xxx` FOREIGN KEY (`request_url`) REFERENCES `user_activity_link` (`url_description`);"
I met the same problem, after a lot of research, I found the above method.
Hope it helps.