Sorting Related objects in Django - django

I have 2 models Category and Item. An Item has a reference to a Category.
class Category(models.Model):
name = models.CharField(max_length=32)
class Item(model.Models):
name = models.CharField(max_length=32)
category = models.ForeignKey(Category)
sequence = models.IntegerField()
The sequence field is supposed to capture the sequence of the Item within a category.
My question is:
What Meta Options do i need to set on category and/or item such that when i do:
category.item_set.all()
that I get the Items sorted by their sequence number.
PS: I am now aware of a meta option called ordering_with_respect_to .. but it is still unclear how it works, and also i have legacy data in the sequence columns. I am open to data migration, if the right approach requires that.

What you're looking for is:
class Item(model.Models):
name = models.CharField(max_length=32)
category = models.ForeignKey(Category)
sequence = models.IntegerField()
class Meta:
ordering = ['sequence',]
That will ensure that Items are always ordered by sequence.

category.item_set.all().order_by('sequence')

Kinda late, and the previous answers don't solve my specific question, but they led me to an answer, so I'm gonna throw this in:
I need to sort my prefetch_related objects specifically for only one view, so changing the default ordering is no good (maybe a model_manager would do it, idk). But I found this in the docs.
I have the following models:
class Event(models.Model):
foo = models.CharField(max_length=256)
....
class Session(models.Model):
bar = models.CharField(max_length=256)
event = models.ForeignKey(Event)
start = models.DateTimeField()
....
class Meta:
ordering = ['start']
Now in a particular view, I want to see all the Events, but I want their Sessions in reverse order, i.e., ordering = ['-start']
So I'm doing this in the view's get_queryset():
from django.db.models import Prefetch
session_sort = Session.objects.all().order_by('-start')
prefetch = Prefetch('sessions', queryset=session_sort)
events = Event.objects.all().prefetch_related(prefetch)
Hope it helps somebody!
*BTW, this is just a simplified version, there are a bunch of filters and other prefetch_related parameters in my actual use case.

Related

how to filter data from parant model based on if there relationship with child model

I have these models
class Tree(models.Model):
field = models.TextField()
class TaskProgress(models.Model):
base_task = models.ForeignKey(BaseTask, on_delete=models.CASCADE)
tree = models.ForeignKey(Tree, on_delete=models.CASCADE)
class BaseTask(models.Model):
trees=models.ManyToManyField(Tree, through='TaskProgress')
class TaskType1(BaseTask):
child1_field = models.TextField()
class TaskType2(BaseTask):
child2_field = models.TextField()
how to get all taskprogress when related to TaskType2 ,
TaskProgress.objects.filter(???)
I added extra field on BaseTask class
TASK_TYPE =[('I','Irrigation'),('C','Care'),('A','Assessment'),('O','Other')]
class BaseTask(models.Model):
trees=models.ManyToManyField(Tree, through='TaskProgress')
worker = models.ManyToManyField(User)
task_type = models.CharField(max_length=1,choices=TASK_TYPE,null=True)
And the filter will be like this
TaskProgress.objects.filter(base_task__task = "I")
I do not think what you are asking is possible, if the models are designed like described. The base_task ForeignKey is specifically pointing at a BaseTask. Even though TaskType1 and TaskType2 inherit from BaseTask, they have no relation in the database. They only look similar.
Option 1: Look into Generic Relations in Django. Basically it allows you to have a ForeignKey relation with more than one type of model. I would not recommend it though. Generic relations are a mess if you don't know want you are doing.
Option 2: Rethink your layout. Maybe you can move the relation to the two TaskTypes instead and adress them via related_name.
class TaskProgress(models.Model):
# base_task = models.ForeignKey(BaseTask, on_delete=models.CASCADE)
tree = models.ForeignKey(Tree, on_delete=models.CASCADE)
class TaskType1(BaseTask):
task_progress = models.OneToOneField(TaskProgress, related_name='task_type_1'
child1_field = models.TextField()
class TaskType2(BaseTask):
task_progress = models.OneToOneField(TaskProgress, related_name='task_type_2'
child2_field = models.TextField()
This way you create a one-to-one-relation between the TaskProgress and the TaskType. You should be able to query one or the other by checking whether a relation exists, e.g. all TaskProgress instances with a relation to a TaskType1 instance.
# Query all TaskProgress instances, that have a TaskType1
TaskProgress.objects.filter(task_type_1__isnull=False)

Django ordering by multiple fields

I am having strange issue with django ordering. For some reason I am not able to order my List View by multiple fields.
Here is my class.
class Syslog(models.Model):
receivedat = models.DateTimeField(default=timezone.now)
facility = models.PositiveSmallIntegerField()
priority = models.PositiveSmallIntegerField()
fromHost = models.CharField(max_length=50)
message = models.TextField(max_length=500)
class Meta:
ordering = ['id','receivedat', 'fromHost']
Here is my view class as well:
class HomePageView(ListView):
model = Syslog
template_name = 'home.html'
context_object_name = 'all_logs'
paginate_by = 10
ordering = ['-id', 'receivedat', 'fromHost']
When a single field in ordering is specified (e.g. ordering = ['-id']), but when I add a second or third parameter everything goes back to defaults (only the first field of ordering is getting recognized)
Does anyone have encounter a similar problem?
The same problem persists in the Django shell as well. When I execute the following query Syslog.objects.all().order_by('id') everything looks good, but when I add another field to order_by (e.g. Syslog.objects.all().order_by('id','fromHost')) the second field doesn't get recognized at all.
Regards,
Jordan
Intsead of this -> Syslog.objects.all().order_by('id','fromHost')
Use this -> Syslog.objects.order_by('id','fromHost')
remove .all()
After slept on my problem I have figured out why I was not getting the result I expected. The issue is that my development data set is quite similar and while the ordering changes it didn't reflect on the template because of objects similarity.

Getting a queryset using a foreign key field in the "other side" of a foreign key relation

Forgive me if the question does not make sense, trying to teach myself django. I've been trying to search how to do this but i'm not sure if i'm using the right words in my search.
I have the following models.
class Category(models.Model):
code = models.CharField(max_length=10, unique=True)
description = models.CharField(max_length=50)
class UserGroupHeader(models.Model):
code = models.CharField(max_length=10, unique=True)
description = models.CharField(max_length=50)
class UserGroupDetail(models.Model):
usergroupheader = models.ForeignKey(UserGroupHeader, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
How do i get a query set from the Category model using the UserGroupHeader? so far what i've got is something like this UserGroupHeader.objects.get(pk=9).usergroupdetail_set.all(), now from the result of this how do i get the Category model?
I'm not sure if I understood exactly what you are trying to do, but in general, while querying, you can follow relations using double underscores. Below are a couple of possible queries:
my_group_header = UserGroupHeader.objects.get(...)
Category.objects.filter(usergroupdetail__usergroupheader=my_group_header) # Gets Category objects related to my_group_header through UserGroupDetail model
Category.objects.filter(usergroupdetail__usergroupheader__code='abc') # Gets Category objects related to UserGroupHeader object with code 'abc' through UserGroupDetail model
UserGroupHeader.objects.filter(usergroupdetail__category__code='abc') # Gets UserGroupHeader objects related to Category object with code 'abc' through UserGroupDetail model
Your query UserGroupHeader.objects.get(pk=9).usergroupdetail_set.all() would return a QuerySet of UserGroupDetail objects. In order to get the category of each UserGroupDetail, you can:
for user_group_detail in UserGroupHeader.objects.get(pk=9).usergroupdetail_set.all():
category = user_group_detail.category
print(category. description)
Or something similar according to your needs

Selecting distinct nested relation in Django

To describe the system quickly, I have a list of Orders. Each Order can have 1 to n Items associated with it. Each Item has a list of ItemSizes. Given the following models, which have been abbreviated in terms of fields for this question, my goal is to get a distinct list of ItemSize objects for a given Order object.
class ItemSize(models.Model):
name = models.CharField(max_length=10, choices=SIZE_CHOICES)
class Item(models.Model):
name = models.CharField(max_length=100)
sizes = models.ManyToManyField(ItemSize)
class OrderItem(models.Model):
order = models.ForeignKey(Order)
item = models.ForeignKey(Item)
class Order(models.Model):
some_field = models.CharField(max_length=100, unique=True)
So... if I have:
o = Order.objects.get(id=1)
#how do I use the ORM to do this complex query?
#i need o.orderitem_set.items.sizes (pseudo-code)
In your current set up, the answer by #radious is correct. However, OrderItems really shouldn't exist. Orders should have a direct M2M relationship with Items. An intermediary table will be created much like OrderItems to achieve the relationship, but with an M2M you get much simpler and more logical relations
class Order(models.Model):
some_field = models.CharField(max_length=100, unique=True)
items = models.ManyToManyField(Items, related_name='orders')
You can then do: Order.items.all() and Item.orders.all(). The query you need for this issue would be simplified to:
ItemSize.objects.filter(item__orders=some_order)
If you need additional data on the Order-Item relationship, you can keep OrderItem, but use it as a through table like:
class Order(models.Model):
some_field = models.CharField(max_length=100, unique=True)
items = models.ManyToManyField(Items, related_name='orders', through=OrderItem)
And you still get your simpler relationships.
ItemSize.objects.filter(items__orderitems__order=some_order)
Assuming you have reverse keys like:
ItemSize.items - reverse fk for all items with such size
Item.orderitems - reverse for all orderitems connected to item
Item.orders - you can guess ;)
(AFAIR that names would be choose by default, but I'm not sure, you have to test it)
More informations about reverse key queries are available in documentation.

Django query on history

I have a model "Item", which has a 1:n to "Location". Means, there is a location-history for items.
Location has a FK to "Room", "Room" to "Floor" and "Floor" to "Building".
Now, I want to select all Items which are currently located an a specific Floor.
I could solve it with a list comprehension, but is there any nicer way to solve it with one query?
-- Update --
Thanks for your answers, unfortunately, they do not match the requirement. To clarify the issue, here some code snippets of the models:
class Item(models.Model):
[..]
class Location(models.Model):
item = models.ForeignKey(Item)
room = models.ForeignKey(Room)
created_at = models.DateTimeField(auto_now_add=True)
[..]
class Room(models.Model):
floor = models.ForeignKey(Floor)
[..]
class Floor(models.Model):
building = models.ForeignKey(Building)
[..]
class Building(models.Model):
[..]
I want to get all items that are currently located on a specific floor, specified by a floor id (as you can see in the models, an item can be relocated). Thanks again.
I don't know your models exactly, but something like that should do it:
items_on_floor_one = Item.objects.filter(\
location__in=Location.objects.filter(room__floor__number=1,\
room__floor__building__name='my_building'))
items = Item.objects.filter(location__room__floor__name='building 1', location__room__floor__num=1)