Get FK set for each instance in queryset - python

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()]

Related

Can I filter a parent model by occurences in a child model in django?

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)

Django query table to find Item that only exist in Parent field

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.

Django .values_list() alternative that returns a QuerySet for a ForeignKey field's model?

I'm looking for a clean way to convert one type of QuerySet to another based on a models ForeignKey field, so basically something like a .values_list('my_fk', flat=True) but returning a proper QuerySet instead of a values_list() variant.
For example:
class Parent(models.Model):
child = models.ForeignKey(Child)
...
children_qs = Parent.objects.filter(...).theMagicMethod('child')
Here children_qs should now be a queryset for all Child instances used in the earlier query, instead of a queryset returning Parent instance.
You can sort of do this with a custom queryset and an __in lookup but it feels a bit smelly:
class ParentQuerySet(models.QuerySet):
...
def children(self):
return Child.objects.filter(id__in=self.values_list('child_id', flat=True))
This takes all the child_id FK's from the records in the Parent's queryset and re-query Child directly. When I inspect the SQL it does a sub query, and I'm not sure if this is optimal or has some odd side effects. It does look like the ordering from the original Parent query is gone, and so are duplicates.
Does anyone got something better then this?
note: I'm aware I could query directly via Child and filter the Parent's fields using the reverse lookup, but that doesn't support everything you can do on the main model.
Try this, it will return query_set of Child class
parent_primary_keys = Parent.objects.filter(...).values_list('pk',flat=True)
children_qs = Child.objects.filter(id__in=parent_primary_keys)
It sounds like you can leverage the Django's function prefetch_related. Check out this answer and django's documentation

Django backwards relation

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.

Django Queryset only method and foreign keys real fields ('_id')

In order to minimize bugs in competitive access to instances of django models, I'm using "only" method of QuerySet. For example, I have models like:
#models.py
class MyModel1(models.Model):
...
class MyModel2(models.Model):
field_1 = models.ForeignKey(MyModel1)
...
I'm using "only" in this way:
instances = list(MyModel2.objects.filter(...).only('id'))
Will field "field_1_id" of instances be loaded at this moment (not 'field_1', exactly 'field_1_id')?
only() method loads only the id of model MyModel2.
Check out using the values_list() method, which can return a list of values if you have only one field you need and you pass it flat = True:
instances = MyModel2.objects.filter(...).values_list('id', flat=True)

Categories

Resources