I am trying to figure out the best (simplest) ways to call all the "Items" from my different User "Accounts."
The Account has the User ForeignKey.
class Acct ( models.Model ):
user = models.ForeignKey ( settings.AUTH_USER_MODEL)
The Item has the Account foreignKey.
class Items ( models.Model ):
acct = models.ForeignKey ( Acct )
So when I have a list of Acct's - what is the best way to get all the User's Items for the different accounts?
I can get all the Accounts with something like this:
a = request.user.acct_set.all ().filter ( active = 1 )
Now what is the next call to get the Items for those Accounts?
Only thing I can figure out is to add the User foreignKey to the Items also. (I would also have to add the Active field.)
I hope this makes sense. Thank you.
You don't need to add any more foreign keys - you can just traverse the existing relationships directly in one query like so:
# Fetch all the items associated with all accounts for this user
items = Item.objects.filter(acct__active=1, acct__user=request.user)
Not sure this is most efficient method, but it seems to be working.
id = request.user.acct_set.values_list ( 'id', flat=True ).filter ( active = 1 )
items = Item.objects.filter ( acct__in = id ) .order_by ( 'date' )
If there is a better way I would appreciate knowing it.
Thanks.
Related
I have 2 models, Product and Productdetails with a OneToOne relationship like this:
class Product(IotaModel):
details = models.OneToOneField(
ProductDetails,
null=True,
on_delete=models.SET_NULL
)
I want a queryset that do this:
SELECT *
FROM product_details
WHERE id = (
SELECT details_id
FROM products
WHERE id=<product_id>
)
I tried this:
details_id = Product.objects.filter(pk=product_pk, active=True).only('details')[:1]
return ProductDetails.objects.filter(pk=details_id, active=True)
But it does not work becouse .only('details') gave me the fields (id, details_id) and the next filter takes id instead of details_id.
I also try to add .defer('id') but that does not work.
I know that I can get the details_id using .values_list('details_id') but I think this would involve making 2 queries to the Database.
What could be the best approach to reach the desired SQL query in just 1 hit to the DB?
Thanks in advance!
You should make just the query for the ProductDetails. Something like this I think.
product_details = ProductDetails.objects.get(
active=True, product__id=product_pk, product__active=True
)
The double underscore it's to look on related objects.
Imagine I have a model which looks something like following:
class Car(models.Model):
TYPE_CHOICES = [
(1: 'Hatchback')
(2: 'Saloon')
type = models.CharField(choices=TYPE_CHOICES, ...)
color = models.CharField()
owner = models.ForeignKey(User, ...)
And I want to count objects by specific values. Say black saloons owned by Johns or white hatchbacks owned by Matts.
The best what I came up so far is:
Car.objects.annotate(
black_saloons_owned_by_Johns=Count(
'type',
filter=(
Q(type=2) &
Q(owner__first_name='John')
)
),
white_hatchbacks_owned_by_Matts=Count(
'type',
filter=(
Q(type=1) &
Q(owner__first_name='Matt')
)
)
).aggregate(
aggregated_black_saloons_owned_by_Johns=Sum(
'black_saloons_owned_by_Johns'
),
aggregated_white_hatchbacks_owned_by_Matts=Sum(
'white_hatchbacks_owned_by_Matts'
)
)
Is there a better way to get the desired result? Thanks.
Update.
As I said, I need to perform multiple lookups in a single query. The query I used had just one example. I updated it. Should have explicitly point it out. Sorry.
We can filter the queryset, and then use .count() [Django-doc]:
Car.objects.filter(type=2, owner__first_name='John').count()
or if you need to perform multiple lookups, you can use .aggregate(..) directly:
You can Count the Car objects directly with:
Car.objects.aggregate(
total_john=Count(
'pk', filter=Q(type=2, owner__first_name='John')
),
total_matts=Count(
'pk', filter=Q(type=1, owner__first_name='Matt')
)
)
this will then return a dictionary that has two keys: 'total_john' and 'total_matts', and these will contain the count of their number of Cars respectively.
I am trying to query a list of rooms. There can be many participants in the rooms. I want to exclude rooms where there are participants a user blocked or got blocked from. The below code works if participants are simple ForeignKey(User) fields. However, the thing is participants are ManyToManyField field. It is written like
participants = models.ManyToManyField(
settings.AUTH_USER_MODEL, related_name='participants'
)
And the below code does not work.
get_blocked = Exists(
users_models.Relationship.objects.filter(
from_user=OuterRef('participants'),
to_user=info.context.user,
status='BLOCK',
)
)
blocking = Exists(
users_models.Relationship.objects.filter(
from_user=info.context.user,
to_user=OuterRef('participants'),
status='BLOCK',
)
)
models.Room.objects.annotate(
get_blocked=get_blocked, blocking=blocking
).filter(
participants=info.context.user, get_blocked=False, blocking=False
).distinct()
Again, the same logic works when the participants are simple ForeignKey(User). I am wondering if there is any way to resolve this issue.
so my models:
class User(models.Model):
username = models.CharField(max_lenght=255)
class Group(models.Model):
users = models.ManyToManyField('User')
class ActiveGroup(models.Model):
group = models.ForeignKey('Group', null=True, blank=True, default=None)
user = models.OneToOneField('User')
So the deal is every user can be a member of any group ( which is represented by m2m field on Group model). But can be active member in only one of them at the same time ( thats why oneToOneField relation on activegroup model).
Problem: need to make a queryset for Group models with count of users which are active.
so its suppose to be like
SELECT group.id, COUNT(
SELECT COUNT(*) FROM activegroup WHERE activegroup.group_id = group.group.id
) as users_count FROM group.
I've tried django.db.models Case and When things but nothing helps.
I would suggest you changing your database design. You have rows with partially-empty entries AND duplication. And that's not good. If this project is expected to grow, you might face problems in the future.
For example: Group -> User and ActiveGroup.group -> user. It is the same information stored two times. I'd change it before going to production. My suggestion would be using through:
https://docs.djangoproject.com/en/1.10/topics/db/models/#extra-fields-on-many-to-many-relationships
You could create a boolean field called "active" and simply switch that every time the active group is changed.
But of course, these are only suggestions. :)
Your solution is:
Group.objects.annotate(total=Count('activegroup__user'))
ag = Group.objects.annotate(total=Count('activegroup__user'))
for g in ag:
print(g, g.total)
>> group_1 2
group_2 1
In your model, you might face another group called null, which will be users without group.
https://docs.djangoproject.com/en/1.10/ref/models/querysets/#annotate
I have following models:
class Domain(models.Model):
name = models.CharField(...)
plan = models.ForeignKey(Plan, ....)
class Plan(models.Model):
name = models.CharField(...)
num_ex_accounts = models.IntegerField(...)
class PlanDetails(models.Model):
accounts = models.IntegerField()
plan = models.ForeignKey(Plan, ....)
class Mailbox(models.Model):
domain = models.ForeignKey(Domain, ...)
Any domain has a plan, any plan has N plan details which has accounts value for create mailboxes using a domain, I want to get in a queryset domains which exceed the accounts value, using raw sql the sql is like:
SELECT domain
FROM domain, plan
WHERE plan.id = domain.plan_id
AND (
SELECT SUM(accounts)
FROM plandetails WHERE plandetails.plan_id=plan.id
)
<=
(
SELECT COUNT(*)
FROM mailbox WHERE mailbox.domain_id=domain.id
)
I tried in django something like this:
domains = Domain.objects.filter(
Q(
PlainDetails.objects.filter(plan = Domain.plan).aggregate(Sum('accounts')) <=
Mailbox.objects.filter(domain = Domain).count()
)
)
But it doesn't works, it throws an error about the Domain.plan, is there a way to reference that field value from parent query in the sub-query? is this queryset valid or is there another (better) approach? or I should use simply raw sql, what is the best option in this case?
If you are using Django 1.8 and higher, then try this:
Domain.objects.annotate(
account_sum=Sum('plan__plandetails_set__accounts'),
mailbox_count=Count('mailbox_set__id'))
).filter(
account_sum__lte=F('mailbox_count')
)
Replace plandetails_set and mailbox_set with the appropriate related name if you explicitly specified any in the ForeignKey fields to Plan. Those two are just the defaults if none has been specified.
UPDATE
For very complex queries, it might be better to just run the actual SQL using cursors. Django ORM is usually good enough, but depending on the needs, it may not be enough. Please view the docs.