Django sort objects with foreign key case insensitive - django

I'm trying to sort a query according to a foreign key field case insensitive.
My models are:
class Post(models.Model):
title = models.CharField(max_length = 80)
author = models.ForeignKey(User, default = User)
trade_option = models.CharField(max_length= 10)
class Book(models.Model):
book_title = models.CharField(max_length = 60)
post = models.ForeignKey(Post)
I want to sort my post objects according to the book_title field case insensitive.
I know if I want to sort case insensitive with the fields in Class Post I can just do:
posts = Post.objects.filter(trade_option= 'buy').extra(select =
{'lower_name' : 'lower( title )'}).order_by('lower_name')
However, when I try the same technique with sorting with foreign key book with book_title:
posts = Post.objects.filter(trade_option= 'buy').extra(select =
{'lower_name' : 'lower( book__book_title )'}).order_by('lower_name')
I get the error "no such column: book__boot_title"
I would like to know where I'm doing this wrong. Thanks

I had the same problem. Here's what you should do:
from django.db.models.functions import Lower
posts = Post.objects.filter(trade_option= 'buy').order_by(Lower('title'))

A related lookup like book__book_title only works in the context of Django. Anything in extra is not processed by Django, so these lookups won't work. You have to explicitly fetch the result from the related table.
Please note that the ordering you're trying to define is most likely not what you expect it to be: a Post object can have multiple related Book objects. Ordering by book__book_title (manually or through the orm) can give you either an indeterminate ordering or duplicate rows, depending on the exact details of your query. You need to define which related book_title the posts should be ordered by.

Related

Django query joining two tables

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.

How to set the range of the primary-key of django objects based on the foreign key range and is it advisable?

I have the below models:
class Author(model):
name = CharField(...)
class Post(mode):
author = ForeignKey(Author)
title = CharField(...)
Suppose we have at most 100 authors. So the primary key of the autors would be in the range of 1 to 100
I want the primary keys of the post model be based on the primary key of the author of the post.
I mean, if the author's primary key is 34, then his/her posts primary keys be 34000001, 34000002, 34000003
is advisable to do this and how can I do it?
This is generally not advisable and might tempt you to follow (or invent) antipatterns. If your goal is to be able to easily access a particular author's posts, for example, you would be safest using Django's normal ORM patterns. For example:
# models.py
class Author(model):
name = CharField(...)
class Post(model):
author = ForeignKey(Author, related_name='posts')
title = CharField(...)
Then anywhere you want, you can...
sally = Author.objects.get(id=1)
sallys_posts = author.posts.all()
This would be much safer than things you might otherwise be tempted to do such as Post.objects.filter(pk__startswith=sally.pk) which would be something that I think would lead to a large number of bugs down the line, and would also mean that you miss out on a lot of normal pattern Django ORM benefits.

Django using the values method with m2m relationships / filtering m2m tables using django

class Book(models.Model):
name = models.CharField(max_length=127, blank=False)
class Author(models.Model):
name = models.CharField(max_length=127, blank=False)
books = models.ManyToMany(Books)
I am trying to filter the authors so I can return a result set of authors like:
[{id: 1, name: 'Grisham', books : [{name: 'The Client'},{name: 'The Street Lawyer}], ..]
Before I had the m2m relationship on author I was able to query for any number of author records and get all of the values I needed using the values method with only one db query.
But it looks like
Author.objects.all().values('name', 'books')
would return something like:
[{id: 1, name: 'Grisham', books :{name: 'The Client'}},{id: 1, name: 'Grisham', books :{name: 'The Street Lawyer'}}]
Looking at the docs it doesn't look like that is possible with the values method.
https://docs.djangoproject.com/en/dev/ref/models/querysets/
Warning Because ManyToManyField attributes and reverse relations can
have multiple related rows, including these can have a multiplier
effect on the size of your result set. This will be especially
pronounced if you include multiple such fields in your values() query,
in which case all possible combinations will be returned.
I want to try to get a result set of n size with with the least amount of database hits authorObject.books.all() would result in at least n db hits.
Is there a way to do this in django?
I think one way of doing this with the least amount of database hits would be to :
authors = Authors.objects.all().values('id')
q = Q()
for id in authors:
q = q | Q(author__id = id)
#m2m author book table.. from my understanding it is
#not accessible in the django QuerySet
author_author_books.filter(q) #grab all of the book ids and author ids with one query
Is there a built in way to query the m2m author_author_books table or am I going to have the write the sql? Is there a way to take advantage of the Q() for doing OR logic in raw sql?
Thanks in advance.
I think you want prefetch_related. Something like this:
authors = Author.objects.prefetch_related('books').all()
More on this here.
If you want to query your author_author_books table, I think you need to specify a "through" table:
class BookAuthor(models.Model):
book = models.ForeignKey(Book)
author = models.ForeignKey(Author)
class Author(models.Model):
name = models.CharField(max_length=127, blank=False)
books = models.ManyToMany(Books, through=BookAuthor)
and then you can query BookAuthor like any other model.

django - query filter on manytomany is empty

In Django is there a way to filter on a manytomany field being empty or null.
class TestModel(models.Model):
name = models.CharField(_('set name'), max_length=200)
manytomany = models.ManyToManyField('AnotherModel', blank=True, null=True)
print TestModel.objects.filter(manytomany__is_null=True)
print TestModel.objects.filter(manytomany=None)
Adding to #Bernhard answer, other possible solution can be achieved using the Q() object.
from django.db.models import Q
filters = Q(manytomany=None)
TestModel.objects.filter(filters)
Negation:
filters = ~Q(manytomany=None)
TestModel.objects.filter(filters)
Even though the topic has already an answer this could be of help. Try with lookups:
empty = TestModel.objects.filter(manytomany__isnull = True)
#........If you want to get their counter part
not_empty = TestModel.objects.filter(manytomany__isnull = False)
Basically, you get two query sets: one where your manytomany fields are empty, and the other with objects that have data in the manytomanyfield.
Hope this could be of some help!
this is an old question but I needed it and the provided answers didn't work for me, but I did fix it and got the proper filtering (on Django 2.2) this is how:
testModel.objects.filter(testmodel__anothermodel=None)
as you can see using the model name all lower case then two underscores then the many to many model name that did it for me

Django ORM: count a subset of related items

I am looking to find a way to annotate a queryset with the counts of a subset of related items. Below is a subset of my models:
class Person(models.Model):
Name = models.CharField(max_length = 255)
PracticeAttended = models.ManyToManyField('Practice',
through = 'PracticeRecord')
class Club(models.Model):
Name = models.CharField(max_length = 255)
Slug = models.SlugField()
Members = models.ManyToManyField('Person')
class PracticeRecord(PersonRecord):
Person = models.ForeignKey(Person)
Practice = models.ForeignKey(Practice)
class Practice(models.Model):
Club = models.ForeignKey(Club, default = None, null = True)
Date = models.DateField()
I'm looking to make a queryset which annotates the number of club specific practices attended by a person. I can already find the total number of practices by that person with a query of Person.objects.all().annotate(Count('PracticeRecord'))
However I would like someway to annotate the number of practices that a person attends for a specific club.
I would prefer something using the django ORM without having to resort to writing raw SQL.
Thanks.
However I would like someway to annotate the number of practices that a person attends for a specific club.
Let us see.
First, find the specific club.
club = Club.objects.get(**conditions)
Next, filter all Persons who have practiced at this club.
persons = Person.objects.filter(practicerecord__Practice__Club = club)
Now, annotate with the count.
q = persons.annotate(count = Count('practicerecord'))
Edit
I was able to successfully make this work in my test setup: Django 1.2.3, Python 2.6.4, Postgresql 8.4, Ubuntu Karmic.
PS: It is a Good Idea™ to use lower case names for the fields. This makes it far easier to use the double underscore (__) syntax to chain fields. For e.g. in your case Django automatically creates practicerecord for each Person. When you try to access other fields of PracticeRecord through this field you have to remember to use title case.
If you had used lower case names, you could have written:
persons = Person.objects.filter(practicerecord__practice__club = club)
# ^^ ^^
which looks far more uniform.
PPS: It is Count('practicerecord') (note the lower case).
I'm afraid that raw sql is the only option here. Anyway it's not that scary and hard to manage if you put it to model manager.