I been struggling with doing a query to get the rows where a Item only exsist in Parnet field and not Child field.
This is the models.
class Item(models.Model):
text = models.CharField()
class ItemRelations(models.Model):
child = models.ForeignKey('Item', related_name='itemchild')
parent = models.ForeignKey('Item', related_name='itemparent')
So I only want to filter out the Items that are not a child, but is a Parent. I call it the firstParent.
I tried so many different ways. I want to accomplish something like this.
firstParent = Item.objects.filter(ItemRelations_parent__is=self).exclude(ItemRelations_child__is=self)
Is it possible. Or does one have to make several queries and loops to manage this?
I'd suggest using the isnull filter for readability.
firstParent = Item.objects.filter(itemparent=self, itemchild__isnull=True)
In the specific example you've given in this comment, Item.pk is the attribute on the Item class. You want the item instance, not the class.
I believe, re-reading, that the question is:
Show the Item where the Item is a parent, but not a child. In that case:
firstParent = Item.objects.filter(itemparent__isnull=False, itemchild__isnull=True) will give you the queryset of objects that match that.
You may, subsequently wish to filter it further to match other aspects. Remember that django querysets are lazy. Don't confuse making several filters with making actual queries/database hits.
QuerySets are lazy – the act of creating a QuerySet doesn’t involve any database activity. You can stack filters together all day long, and Django won’t actually run the query until the QuerySet is evaluated.
It is possible.
firstParent = Item.objects.exclude(child=self).filter(parent=self)
write exclude before filter.
Related
I am writing a method that computes a complex Django QuerySet.
It starts like this
qs1 = A.objects.filter(b_set__c_obj__user=user)
qs1 will eventually become the result, but before it does, the method goes on with several further steps of filtering and annotation.
b_set is an 1:n relationship, but I know that at most one of the c_obj can actually match.
I need to reference this c_obj, because I need another attribute email from it for one of the filtering steps (which is against instances of another model D selected based on c_obj's email).
user can be either a User model instance or an OuterRef Django ORM expression, because the whole queryset created by the method is subsequently also to be used in a subquery.
Therefore, any solution that involves evaluating querysets is not suitable for me. I can only build a single queryset.
Hmm?
I need to reference this c_obj, because I need another attribute email from it for one of the filtering steps (which is against instances of another model D selected based on c_obj's email).
If you do all the filtering with the same .filter(…) clause then that c_obj will be the same for all conditions. In other words if you want to filter A such that it has a related B and a related C for example that has as email='foo#bar.com', and as type='user', you can filter with:
qs1 = A.objects.filter(
b_set__c_obj__email='foo#bar.com',
b_set__c_obj__type='user'
)
This is thus different from:
qs1 = A.objects.filter(
b_set__c_obj__email='foo#bar.com'
).filter(
b_set__c_obj__type='user'
)
since here it will look for an A object that has a related c_obj with email='foo#bar.com and for a c_obj (not per se the same one) with type='user'.
I have two models:
class User(models.Model):
# stuff
class Task(models.Model):
# stuff
When assigning new tasks to users, I want to avoid assigning tasks the worker has gotten before. The approach I'm trying to take is to add a third model, Assignment that tracks tasks assigned to a given user:
class Assignment(models.Model):
worker = models.ForeignKey(User)
task = models.ForeignKey(Task)
How, though, do I go about forming a query based on this? My thought was to start by filtering Assignments by the user being assigned to, but I'm getting stuck after that point.
def get_tasks(worker):
previous_assignments = Assignment.objects.filter(worker=worker)
# then something like...
assignable_tasks = Task.objects.exclude(pk__in=previous_assignments.task) #clearly wrong
Is there a way to access the task ids inside the previous_assignments queryset? Not sure if this isn't the best approach, or if I am just missing how to move past this step.
Edit: It occurs to me I could populate an empty set with Tasks by looping through those Assignment objects and then use an __in exclude argument like above... Any better approaches than that?
In order to exclude with pk_in, you would need a list or queryset of task ids to exclude. For example you could do:
previous_assignments = Assignment.objects.filter(worker=worker).values_list('task_id')
assignable_tasks = Task.objects.exclude(pk__in=previous_assignments)
However you don't need to do this. You can use the double underscore notation to follow the relationship from assignment to worker:
assignable_tasks = Task.objects.exclude(assignment__worker=worker)
Note that you could use a many-to-many field instead, and Django will take care of creating the joining table for you:
class Task(models.Model):
users = models.ManyToManyField(User)
In this case, your query becomes:
assignable_tasks = Task.objects.exclude(users=worker)
I am setting up webservices for an application and I have the following models:
class Parent(models.Model):
...
class Child(models.Model):
parent = models.ForeignKey(Course)
...
The relation is One to Many (1 Parent, many Children)
Now, I would like to get all the Parent objects with its particular Child and send it as a JSON Request.
Is it possible to do so without having to first get all the "Childs" and iterate through them looking for the ones related to the particular parent?
I think that would be extremely inefficient for really large databases, plus the "Childs" won't be repeated in other "Parents"
Thank you very much
Every relationship in Django automatically gets its reverse relation added to the model. In the case of a ForeignKey or ManyToManyField that relation contains several objects. In that case, the default attribute name is set to <model>_set, so in this case child_set. This is a manager and can be used as such, so e.g. to iterate over all children:
for child in parent.child_set.all():
do_something()
You can also specify the attribute name used for the reverse relation using the related_name attribute:
class Child(models.Model):
parent = models.ForeignKey(Parent, related_name='children')
for child in parent.children.filter(some_field=True):
do_something()
Read more in the documentation on following relations backwards and how are backward relationships possible.
Why would you need to iterate? Even if Django didn't provide you with a special backwards syntax, you could always do this:
Child.objects.filter(parent=my_parent)
but as a cursory Google for the title of your question would have shown, there is a special syntax for backwards relations:
my_parent.child_set.all()
Yes, in django you can use:
parentInstance.child_set.all()
where parentInstance is one particular parent in your Parent database. That will return all of the Child objects associated with it in an efficient manner. To make it a JSON response, you can try something like this:
import json
from django.http import HttpResponse
response_data = {}
response_data[str(parentInstance)] = parentInstance.child_set.all()
return HttpResponse(json.dumps(response_data), content_type="application/json"
adopted from here.
I have a tree-like Django model say named A, which been done by django-mptt.
class A(MPTTModel):
parent = TreeForeignKey('self')
this class automaticly has the 'children' manager, so i can easily get the subtree
There is another model, which have FK link to A:
class SomeModel(models.Model):
link_to_a = models.ForeignKey(A)
I know, that if i want to get SomeModel set of A instance i can do that:
a = A.objects.filter(blah)
a.somemodel_set.all()
and the question is:
what is the most pythonic way to fetch somemodel_set of each instance in some queryset under A model, i.e. i want 4 example this:
some_A_instance.children.all().get_all_somemodel_instances()
and get_all_somemodel_instances() should retrieve ziped queryset of sets for each children
Do you just need the related items in one list, or do you need to associate each set with their parent? If the former, you can get them all at once with this:
related_items = SomeModel.objects.filter(link_to_a=some_A_instance.children.all())
which will do one single query (with a subquery) to get everything.
Otherwise, you can use prefetch_related() to get all items' related sets in one go:
items = some_A_instance.children.all().prefetch_related('somemodel_set')
This should do:
[child.somemodel_set.all() for child in some_A_instance.children.all()]
This feature is used in a view where I list search results. In my search form, I have some ModelChoiceFields to search by foreign keys. Usual workflow mean turn our current search more and more precise, so to disable a lot of not pertinent results, I'm trying to remove entries which would return no result if no other search parameters change.
I'm using an object queryset to restrict propositions in some dropdown lists.
I use theses dropdown lists to filter by foreign keys my objects list.
My function argument to filter is an Objects queryset, for now :
class MySearchForm(Form):
things = ModelChoiceField(queryset=models.Thing.objects.none())
def __init__(self, *args, **kwargs):
my_objects_queryset = kwargs.pop('my_objects_queryset',models.Thing.objects.all())
super(MySearchForm, self).__init__(*args, **kwargs)
self.fields['things'].queryset = \
models.Thing.objects.filter(object__in=my_objects_queryset).distinct()
My problem is how to remove a 'where close' from an existing query_set.
Here, I want to remove from my_objects_queryset where closes which filter by thing = ForeignKey(models.Thing)
Is it possible?
Something like a way to list all the filters of our queryset and edit/delete them on the fly.
Short answer:
No, you cannot do that.
Long answer:
Theoretically, it may be possible (to some extent anyway) but certainly not advisable.
(I have not fully studied the django source, so what follows is a result of a simple climb up the call tree.)
Looking at the source for QuerySet, filter() and exclude() return a clone of the queryset itself with the new rule added on (using clone.query.add_q()).
clone.query is an object representing an SQL Query while the add_q() method adds the new rule to clone.query.where which is essentially a root node of a tree.
How the new node is added to the tree depends on whether the rule is to be connected with an AND or an OR clause. This is important as it affects the correctness of the final SQL query that is generated.
So, to list filters assigned to a queryset, one "simply" need to understand how the queryset.query.where tree is represented (see django.utils.tree).
The difficult bit is of course the removing filters such that it does not affect the remaining rules. I shall refrain from offering a solution as there is no guarantee that the implementation will not change thus invalidating the solution. I suspect it is possible to do but it smells of something that should only be done out of academic interest, or not at all.
Some hacking here.
You can bypass it by "reseting" the current filters as below:
from django.db.models.sql.where import WhereNode
qs = YourModel.objects.filter(column=1).all()
qs.query.where = WhereNode() # resets current filters
qs.all() # now you can apply new filters