Django excluding a queryset without values() keyword - django

I have a few models but my question is about two of them, A and B.
class A(models.Model):
...
class B(models.Model):
a = models.ForeignKey(A)
c = models.CharField(...)
d = models.ForeignKey(C)
All i want is to exclude this queryset:
set1 = B.objects.all()
From below queryset:
set2 = A.objects.all()
I know i can manage this by:
set1 = B.objects.all().values('a')
set2 = A.objects.all().exclude(pk__in = set1)
But i need all values of set1 for the remaining code. If i use values(), i cant use "c" and "d" fields of set1.
So, is there any method excluding without narrowing the fields of B?
PS: i prefer to keep away from new queries. i know i can write a second query of B objects to fit my needs.

I believe you are asking for the instances of model A with no related B.
set2 = A.objects.filter(b__isnull=True)

Yes, you can use a list comprehension:
[obj.a for obj in queryset]
This will return a list with the a attribute for all the objects in the queryset, without modifying them.

Related

How do you combine two querysets into one queryset(not a list) in django?

I want to simply combine two querysets from different models into one queryset (and not a list as shown in other stackoverflow questions!). For example, here are two querysets:
a = Modelone.objects.all()
b = Modeltwo.objects.filter(number__gte=4)
Then, I want a queryset c that combines both a and b. Thank you, and please leave me any questions you have.
You cannot do that. Imagine the following:
class Modelone(models.model):
number = models.IntegerField()
class Modeltwo(models.model):
text = models.TextField()
Imagine you would filter against your joined queryset, how can that work if the rows (or instances) do not have the same fields?
If you would want to join two querysets of the same model, you could do that:
qs1 = modelone.objects.filter(nummber=10)
qs2 = modelone.objects.filter(nummber=20)
joined = qs1 | qs2
(source)
If your two models are similar and have overlapping fields, you could use something like django-polymorphic

Django count nested m2m objects

I have 3 models -
class A(models.Model):
...
b = models.ManyToManyField(B, related_name="related_a")
class B(models.Model):
...
class Entries(models.Model):
...
b = models.ForeignKey(B, related_name="entries")
I need to loop over all A objects and show the count of entries for each b in A, along with other fields in A. What would be the most efficient way to do it?
You can get it done in 2 queries:
from django.db.models import Count, Prefetch
qs = B.objects.annotate(entry_count=Count('entries'))
a_list = A.objects.prefetch_related(Prefetch('b', queryset=qs))
I hope I've got all related names right. You can access the counts like this:
for a in a_list:
for b_item in a.b:
print(b_item.entry_count)

Multiple object types references in Django

We are currently running with the following configuration to avoid other issues.
So for the question: let's assume that this is a must and we can not change the Models part.
At the beginning we had the following models:
class A(Model):
b = ForeignKey(B)
... set of fields ...
class B(Model):
...
Then we added something like this:
class AVer2(Model):
b = ForeignKey(B)
... ANOTHER set of fields ...
Assuming an object of type B can only be referenced by either A or AVer2 but never both:
Is there a way to run a query on B that will return, at runtime, the correct object type that references it, in the query result (and the query has both types in it)?
You can assume that an object of type B holds the information regarding who's referencing it.
I am trying to avoid costly whole-system code changes for this.
EDIT:
Apparently, my question was not clear. So I will try to explain it better. The answers I got were great but apparently I missed a key point in my question so here it is. Assuming I have the model B from above, and I get some objects:
b_filter = B.objects.filter(some_of_them_have_this_true=True)
Now, I want to get a field that is in both A and AVer2 with one filter into one values list. So for example, I want to get a field named "MyVal" (both A and AVer2 have it) I don't care what is the actual type. So I want to write something like:
b_filter.values(['a__myval', 'aver2__myval'])
and get something like the following in return: [{'myval': }]
Instead, I currently get [{'a__myval': , 'aver2__myval': None}]
I hope it is clearer.
Thanks!
Short answer: You can not make your exact need.
Long answer: The first thing that came to my mind when I read your question is Content Types, Generic Foreign Keys and Generic Relations
Whether you will use "normal" foreign keys or "generic foreign keys" (combined with Generic Relation), Your B instances will have both A field and AVer2 field and this natural thing make life easier and make your goal (B instance has a single Field that may be A or Avr2) unreachable. And here you should also override the B model save method to force it to have only the A field and the Avr2 to be None or A to be None and Avr2 to be used. And if you do so, don't forget to add null=True, blank=True to A and Avr2 foreign key fields.
On the other hand, the opposite of your schema makes your goal reachable:
B model references A and Avr2 that means that B model has ONE generic foreign key to both A and Avr2 like this: (this code is with Django 1.8, for Django 1.9 or higher the import of GenericRelation, GenericForeignKey has changed)
from django.db import models
from django.contrib.contenttypes.generic import GenericRelation, GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class B(models.Model):
# Some of your fields here...
content_type = models.ForeignKey(ContentType, null=True, blank=True)
object_id = models.PositiveIntegerField(null=True, blank=True)
# Generic relational field will be associed to diffrent models like A or Avr2
content_object = GenericForeignKey('content_type', 'object_id')
class A(models.Model):
# Some of your fields here...
the_common_field = models.BooleanField()
bbb = GenericRelation(B, related_query_name="a") # since it is a foreign key, this may be one or many objects refernced (One-To-Many)
class Avr2(models.Model):
# Some of your fields here...
the_common_field = models.BooleanField()
bbb = GenericRelation(B, related_query_name="avr2") # since it is a foreign key, this may be one or many objects refernced (One-To-Many)
Now both A and Avr2 have "bbb" field which is a B instance.
a = A(some fields initializations)
a.save()
b = B(some fields initializations)
b.save()
a.bbb = [b]
a.save()
Now you can do a.bbb and you get the B instances
And get the A or Avr2 out of b like this:
b.content_object # which will return an `A object` or an `Avr2 object`
Now let's return to your goals:
Is there a way to run a query on B that will return, at runtime, the correct object type that references it, in the query result (and the query has both types in it)?
Yes: like this:
B.objects.get(id=1).content_type # will return A or Avr2
You wanna perform something like this: b_filter = B.objects.filter(some_of_them_have_this_true=True) :
from django.db.models import Q
filter = Q(a__common_field=True) | Q(avr2__common_field=True)
B.objects.filter(filter)
Getting [{'a__myval': , 'aver2__myval': None}] is 100% normal since values is asked to provide two fields values. One way to overcome this, is by getting two clean queries and then chain them together like so:
from itertools import chain
c1 = B.objects.filter(content_type__model='a').values('a__common_field')
c2 = B.objects.filter(content_type__model='avr2').values('avr2__common_field')
result_list = list(chain(c1, c2))
Please notice that when we added related_query_name to the generic relation, a and avr2 has become accessible from B instances, which is not the default case.
And voilà ! I hope this helps !
I'm not sure what do you want to get in query set.
I assumed that you want set of "correct object types" that "has both types in it", so in fact you want set of related class types (like [<class 'main.models.A'>, <class 'main.models.A2'>]). If that is not the case, I can change answer after more specific details in comments.
This is solution for that "class list", you can use it to get what you precisely want.
# Our custom QuerySet that with function that returns list of classes related to given B objects
class CustomQuerySet(models.QuerySet):
def get_types(self, *args, **kwargs):
all_queryset = self.all()
return [b.get_a() for b in all_queryset]
# Our custom manager - we make sure we get CustomQuerySet, not QuerySet
class TypesManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return CustomQuerySet(self.model)
class B(models.Model):
# some fields
# Managers
objects = models.Manager()
a_types_objects = TypesManager()
# Get proper A "type"
def get_a(self):
if self.a_set.all() and self.a2_set.all():
raise Exception('B object is related to A and A2 at the same time!')
elif self.a_set.all():
return A
elif self.a2_set.all():
return A2
return None
class A(models.Model):
b = models.ForeignKey(
B
)
class A2(models.Model):
b = models.ForeignKey(
B
)
And now you can use it like this:
>>> from main.models import *
>>> B.a_types_objects.all()
<CustomQuerySet [<B: B object>, <B: B object>]>
>>> B.a_types_objects.all().get_types()
[<class 'main.models.A'>, <class 'main.models.A2'>]
>>> B.a_types_objects.filter(id=1)
<CustomQuerySet [<B: B object>]>
>>> B.a_types_objects.filter(id=1).get_types()
[<class 'main.models.A'>]
Using a_types_objects works like normal objects, but it returns CustomQuerySet, which has extra function returning list of class.
EDIT:
If you worrying about changing a lot of B.objects.(...) into B.a_types_objects.(...) you could just set your main manager to TypesManager like that:
class B(models.Model):
# some fields
# Override manager
objects = TypesManager()
Rest of your code will remain intact, but from now on you will use CustomQuerySet instead of QuerySet - still, nothing really changes.

ORDERBY query on 2 django models collectively

I have 2 models namely A and B. Both have a common field, say timestamp.
X = A.objects.all()
Y = A.objects.all()
results = chain(X,Y) # Union
Now I want to apply ORDERBY query on results. How can I do that?
While the python way of doing it is by using some kind of sorting, this is normally not the correct way of doing it in Django.
The reason is that in order to join two queries of two different models, you have to transform them to lists (as other answers suggest).
While this seems good, most of the time it is not, because you hit the database, i.e. the database is asked for all elements of both models for creating both lists, and them joining.
The most likely reason why you are joining two models in a single list is because both should be derived from a common model, e.g. an Abstract model. Doing this both models have a natural common field obtained by being subclasses of the abstract model.
The ordering of such a query can then be made using order_by. This is more efficient because queries are lazy.
EDIT:
Example:
class Parent(models.Model):
date_field = models.DateField()
def pretty_show():
# returns general behavior
class B(Parent):
# other specific fields
def pretty_show():
# returns specific behavior
class C(Parent):
# other specific fields
def pretty_show():
# returns specific behavior
X = Parent.objects.all().order_by("date_field")
for x in X:
x.pretty_show()
Notice that Parent class in this case is not Abstract and childs are not proxies, but this choice strongly depends on what you want to achieve. Hope this helps.
You mean:
X = A.objects.all()
Y = B.objects.all()
results = chain(X,Y) # Union
You can cast result to list(). List has order function:
ut.sort(key=lambda x: x.count, reverse=True)
more
You could try to create a list of the model objects and then sort by their time stamp like this (both models must have the same time_stamp attribute):
l= list(x)+list(y)
l.sort(key=lambda l: l.time_stamp)
Hope this is what you are looking for.

How do I find all ObjectAs that Don't have an ObjectP?

Sounds so simple, but has me stymied:
class A(models.Model):
various fields
class P(models.Model):
various fields
a = models.ForeignKey(A)
object_a = A.objects.get(pk=1)
p_objects = P.objects.filter(a=objecta) # one way
p_objects = A.p_set.all() # other way
to get all the P's that belong object_a is easy. What I can't figure out is how to get all the A objects that have no associated P object.
I can't reverse the ForeignKey relation because each A might have more than one P.
This will work
A.objects.filter(p__isnull=True)