Example model:
class Book(models.Model):
title = models.TextField()
class Author(models.Model):
book = models.ForeignKey(Book)
name = models.CharField(max_length=50)
and some example data:
Book:
id title
1 test111
2 test222
3 test333
4 test444
Author:
book_id name
1 test111
1 test222
2 test222
2 test333
3 test111
3 test333
4 test111
4 test333
I want get all books which authors name contains '111' and '333' (so all books which have at least 2 authors: first with 111 in name, second with 333 in name)
I can reach this goal by using chain query:
books = Book.objects.filter(author__name__icontains="111").filter(author__name__icontains="333")
which return two books with id: 3 and 4
Is there any way to reach above goal by using Q objects?
You can combine reduce and Q, see The power of django’s Q objects post.
from django.db.models import Q
import operator
auths = [ "111", "333" ]
query = reduce(operator.and_, [ Q(author__name__icontains=x) for x in auths ] )
books = Book.objects.filter( query )
This one is for dynamic auth list:
pk_list = qs.values_list('pk', flat=True) # i.e [] or [1, 2, 3]
if len(pk_list) == 0:
Book.objects.none()
else:
q = None
for pk in pk_list:
if q is None:
q = Q(pk=pk)
else:
q = q | Q(pk=pk)
Book.objects.filter(q)
Related
I have such models:
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
tags = models.ManyToManyField(to="tag", related_name="tags", blank=True)
def __str__(self):
return self.title
class Tag(models.Model):
value = models.CharField(max_length=50)
parent = models.ManyToManyField("Tag", related_name="children", blank=True)
image = models.ImageField(upload_to="tags", null=True, blank=True)
def __str__(self):
return self.value
And I have an object, for example, post C which has tag 2 and tag 3
Currently I cannot create a combined ( q1 & q2 ) query, where I will be able to filter by that condition.
I am using combined queries because the case is more complex(so double filter won't do), I want to be able to filter by such queries as " 2 or ( 3 and 4 ) ", "(1 or 2) and (3 or 4)"
>>> q1
<Q: (AND: ('tags__in', '2'))>
>>> q2
<Q: (AND: ('tags__in', '3'))>
>>> Post.objects.filter(q1)
<QuerySet [<Post: post_B>, <Post: post C>, <Post: post D>]>
>>> Post.objects.filter(q2)
<QuerySet [<Post: post C>, <Post: post D>]>
>>> Post.objects.filter(q1 & q2)
<QuerySet []>
>>> str(Post.objects.filter(q1).query)
'SELECT "posts_post"."id", "posts_post"."title", "posts_post"."content" FROM "posts_post" INNER JOIN "posts_post_tags" ON ("posts_post"."id" = "posts_post_tags"."post_id") WHERE "posts_post_tags"."tag_id" IN (2)'
>>> str(Post.objects.filter(q2).query)
'SELECT "posts_post"."id", "posts_post"."title", "posts_post"."content" FROM "posts_post" INNER JOIN "posts_post_tags" ON ("posts_post"."id" = "posts_post_tags"."post_id") WHERE "posts_post_tags"."tag_id" IN (3)'
>>> str(Post.objects.filter(q1 & q2).query)
'SELECT "posts_post"."id", "posts_post"."title", "posts_post"."content" FROM "posts_post" INNER JOIN "posts_post_tags" ON ("posts_post"."id" = "posts_post_tags"."post_id") WHERE ("posts_post_tags"."tag_id" IN (2) AND "posts_post_tags"."tag_id" IN (3))'
What I've found is https://stackoverflow.com/a/8637972/20685072
It says " ANDed Q objects would not work "
from django.db.models import Q
query = Q()
for the or condition //
query.add(Q(id__in=[1,2] | Q(id__in=[3,4], query.connector)
as you asked for 2 and 3 //
query.add(Q(id=2) & Q(id=3), query.connector)
data = Model.objects.filter(query)
depends what is the requirement you can modify it as per need
I resolved it using raw Sql queries, UNION and INTERSECT
Let's say I have the following Django model:
class Person(models.Model):
SHIRT_SIZES = (
(0, 'Small'),
(1, 'Medium'),
(2, 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.IntegerField(choices=SHIRT_SIZES)
I can create create a Person instance and get the shirt_size display value very easily:
john = Person(name="John", shirt_size=2)
john.shirt_size # 2
john.get_shirt_size_display() # 'Medium'
How can I do this the other way? That is, given a shirt size of Medium, how can I get the integer value? I there a method for that or should I write my own method on the Person object like so:
class Person(models.Model):
...
#staticmethod
def get_shirt_size_key_from_display_value(display_value):
for (key, value) in Person.SHIRT_SIZES:
if value == display_value:
return key
raise ValueError(f"No product type with display value {display_value}")
The docs recommend the following:
class Person(models.Model):
SMALL = 0
MEDIUM = 1
LARGE = 2
SHIRT_SIZES = (
(SMALL, 'Small'),
(MEDIUM, 'Medium'),
(LARGE, 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.IntegerField(choices=SHIRT_SIZES)
Now the name MEDIUM is attached to your model and model instances:
>>> john = Person(name="John", shirt_size=2)
>>> john.shirt_size
2
>>> john.MEDIUM
2
If given a string, you can use getattr:
def get_shirt_size(instance, shirt_size):
return getattr(instance, shirt_size.upper())
choices_dict = {y: x for x, y in shirt_size.choices}
will give you a dictionary with all the ints as values and sizes as keys.
So you can write a function that returns the int of whatever shirt size you put in, or make choices_dict part of your Person object.
So I want to display all available items for any given date, shouldn't be that hard but somehow I ran into a problem concerning related items.
Let's say we have the following models, a model to store all bookings and a model with the Item.
Then I would create ListView to retrieve all items available between any given dates. I override the queryset to retrieve the data filled in by the user.
This seems to be working but there's an issue, even though I check if the "form_start_date" or "form_end_data" are in conflict with existing bookings, when a single Item has multiple bookings it does not work.
example
Bookings [X] for item #01:
01-01-2019 to 01-03-2019
01-11-2019 to 01-18-2019
Jan 2019 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
---------- --- --- --- --- --- --- --- --- --- ---- ---- ---- ---- ---- ---- ---- ---- ----
Item #01 X X X O O O X X X X X X X X
Item #02 X X X X X
When I check for availablity [O] for 01-06-2019 to 01-08-2019, item #01 is not available, what am I missing here?
models.py
class Booking(models.Model):
item = models.ForeignKey('Item', on_delete=models.SET_NULL, null=True)
start_date = models.DateField()
end_date = models.DateField()
class Item(models.Model):
name = models.CharField(max_length=20, unique=True)
views.py
class AvailableItems(generic.ListView):
model = Item
def get_queryset(self):
start_date = datetime.strptime(self.request.GET.get('start_date'), '%Y-%m-%d')
end_date = datetime.strptime(self.request.GET.get('end_date'), '%Y-%m-%d')
# As Willem suggested in the comments, it is easier to check for available items
available_items = (
Item.objects.filter(booking__start_date__gte = start_date, booking__end_date__lte = end_date)
)
if start_date and end_date:
return available_items
else:
return Item.objects.all()
Let us first analyze when two intervals (f1, t1) and (f2, t2) overlap. A simpler problem to solve, is finding out when two intervals do not overlap. That holds for two cases:
given t1 < f2, since then the first event ends before the second; or
given t2 < f1, since then the first event ends before the second begins.
So that means that two events overlap given t1 ≥ f2 and t2 ≥ f1.
With this knowledge, we can design a filter like:
bookings = Booking.objects.filter(
end_date__gte=form_start_date,
start_date__lte=form_end_date
)
return Item.objects.exclude(
booking__in=bookings
)
This then results in a query like:
SELECT item.*
FROM item
WHERE NOT (
item.id IN (
SELECT V1.item_id
FROM booking V1
WHERE (V1.id IN (
SELECT U0.id FROM booking U0
WHERE (U0.end_date >= 2019-01-01 AND U0.start_date <= 2019-02-02)
AND V1.item_id IS NOT NULL
)
)
)
(here 2019-01-01 and 2019-02-02 are hypothetical start and end dates).
I think it is probably better to process the two dates through a Form however to do proper validation and cleaning.
For example if we populate an empty database with the data as provided in the question, we get:
>>> i1 = Item.objects.create(name='Item #01')
>>> i2 = Item.objects.create(name='Item #02')
>>> b1 = Booking.objects.create(item=i1, start_date=)
KeyboardInterrupt
>>> from datetime import date
>>> b1 = Booking.objects.create(item=i1, start_date=date(2019,1,1), end_date=date(2019, 1, 3))
>>> b2 = Booking.objects.create(item=i1, start_date=date(2019,1,11), end_date=date(2019, 1, 18))
>>> bookings = Booking.objects.filter(
... end_date__gte=date(2019, 1, 6),
... start_date__lte=date(2019, 1, 8)
... )
>>> Item.objects.exclude(
... booking__in=bookings
... )
<QuerySet [<Item: Item object (2)>, <Item: Item object (3)>]>
>>> b3 = Booking.objects.create(item=i2, start_date=date(2019,1,2), end_date=date(2019, 1, 6))
>>> bookings = Booking.objects.filter(
... end_date__gte=date(2019, 1, 6),
... start_date__lte=date(2019, 1, 8)
... )
>>> Item.objects.exclude(
... booking__in=bookings
... )
<QuerySet [<Item: Item object (2)>]>
So first I constructed two items, and made two bookings on the first item. If we then make a query, we see that both items pop up. If we then add an extra booking for Item #02, then if we perform the query again, we see that only the first item (I first made an item for test purposes that was then removed) is showing up, since for the given range, the second item is no longer available: it has been booked by booking b3.
I have two tables as follows,
cabinet
cabinet_id cabinet_name
1 Test1
2 Test2
3 Test3
cabinet_maping
id product_id cabinet_id size
1 34 1 20
2 34 3 25
How can I select all cabinet_name from cabinet table where cabinet_maping.product_id = 34 in Django
Expected Output as follows,
cabinet_id cabinet_name size
1 Test1 20
2 Test2 0
3 Test3 25
I think that yours models could look like
class Cabinet(models.Model):
name = models.CharField(max_length = 30)
class cabinet_maping(models.Model):
product = models.ForeignKey(Product)
cabinet = models.ForeignKey(Cabinet)
size = .....
You should do sth like this:
cabinet_name = cabinet_maping.objects.filter(product__id = 34)
for item in cabinet_name:
print item.cabinet.id
print item.cabinet.name
print item.size
I didn't check it but i think that should work (that works :))
I agree with Silwestpl that your models are wrong as using Foreign Key would make this query a lot easier. But just an answer to your question would be. I am assuming that you dont have any relationships between the two tables as you haven't mentioned any.
x = Cabinet_mapping.objects.filter(product_id = somevalue).distinct('cabinet_id')
response_list = []
for y in x:
response_dict = {}
response_dict['cabinet_id'] = y.cabinet_id
response_dict['cabinet_name'] = cabinet.objects.get(id = y.cabinet_id).cabinet_name
response_dict['size'] = y.size
response_list.append(response_dict)
return response_list
This would be the answer according to the details you have provided.
But Ideally I would do something like this
class Product(models.Model):
# id is generated automatically.
## any other fields that you want for product.
class Cabinet(models.Model):
# again id is auto-genearated.
cabinet_name = models.CharField(max_length=100)
cabinet_size = models.IntegerField()
products = models.ManytoManyField(Product)#This automatically maps product_id n cabinet_id.
And your query would be.
x = Cabinet.objects.filter(product.id=some_value)
print (x.id , x.cabinet_name, x.cabinet_size) # I used print, but you can use it anyway you want.
This would be what you require. Please use the second solution if you are looking into something serious.
I have 3 models in django:
class Movie(models.Model):
mid = models.IntegerField(primary_key = True)
name = models.CharField(max_length = 100)
class User(models.Model):
username = models.CharField(max_length=128, null=True)
uid = models.CharField(max_length=100)
movie = models.ManyToManyField(Movie, through = "Vote")
class Vote(models.Model):
movie = models.ForeignKey(Movie)
user = models.ForeignKey(User)
rating = models.IntegerField()
here rating = 0/1, 0 means dislike, 1 means like
i want to make some queries using filters:
find out all movies that a current user likes. For that i use this following 2 queries, but none of them work. In both cases it gives erroneous results
ans = Movie.objects.filter(vote__user = self).filter(vote__rating = 1)
ans = Movie.objects.filter(user__uid = self.uid).filter(vote__rating = 1)
I have a list of users in an array ids. I want to find out how many users from this list like a particular movie?
i tried this, but this is also incorrect:
ret = User.objects.filter(uid__in = ids).filter(vote__movie = mov).filter(vote__rating = 1)
can somebody please help me with these 2 queries?
I'd also suggest letting django assign the model's id's but if you are using a legacy database or for some other reason need to assign the id's you can query like so:
# uid is some uid
user = User.objects.get(uid=uid)
likes = Movie.objects.filter(vote__user=user, vote__rating=1)
or
likes = Movie.objects.filter(vote__user__uid=some_uid, vote__rating=1)
count of people in the list of users who like a specific movie:
>>> uids = ['1','2','3']
>>> # if mov is a Movie instance
>>> votes = Vote.objects.filter(user__uid__in=uids, movie=mov, rating=1)
>>> print votes.query
SELECT "so1_vote"."id", "so1_vote"."movie_id", "so1_vote"."user_id", "so1_vote"."rating" FROM "so1_vote" INNER JOIN "so1_user" ON ("so1_vote"."user_id" = "so1_user"."id") WHERE ("so1_user"."uid" IN (1, 2, 3) AND "so1_vote"."movie_id" = 1 AND "so1_vote"."rating" = 1 )
>>> # if mov is a mid for a movie
>>> # get movie instance by using Movie.objects.get(mid=mov)
>>> # or query:
>>> # votes = Vote.objects.filter(user__uid__in=uids, movie__mid=mov, rating=1)
>>> likes_count = votes.count()
>>> print likes_count
0
combined:
likes_count = Votes.objects.filter(user__uid__in=uids, movie=mov, rating=1).count()