I have a ManyToManyField in my model. I need to get the third item for every query as below.
class Staff(models.Model):
status = models.BooleanField(default=True)
person = models.ForeignKey(Person)
staff_job_categories = models.ManyToManyField(StaffJobCategory)
staff_titles = models.ManyToManyField(PersonTitle, null=True, blank=True)
def get_job_categories(self):
return self.staff_job_categories.all()[3]
I use the get_job_categories function for admin list_filter but I want to show only the 3rd item in every many to many array.
But get the
List index out of range ERROR;
by the way;
def get_job_categories(self):
return self.staff_job_categories.all()[:3]
Works fine. but gets all the objects till i get what i want.
This should work for jobs binded to less than 3 categories:
return ( self.staff_job_categories.all()[2]
if self.staff_job_categories.count() >= 3
else None
)
Or:
return ( self.staff_job_categories.all()[2]
if len( self.staff_job_categories.all()[:3] ) >= 3
else None
)
To avoid all categories recount.
The first item in a sequence has index 0. The third item has index 2. Perhaps that's your only problem?
E.g. try:
def get_job_categories(self):
return self.staff_job_categories.all()[2]
However, this assumes that you know for certain that all staff have at least three job categories and that their order is predictable. Is that actually the case? I don't think Django's ManyToManyField gives you any guarantee about the order that the related objects will be returned.
Related
I'm trying to implement a qs.sort_by operation, but I'm not sure how to go about it.
Given the following models
class Group(models.Model):
name = models.CharField(max_length=300)
( ... )
class Budget(models.Model):
( ... )
amount = models.DecimalField(
decimal_places=2,
max_digits=8,
)
group = models.ForeignKey(
Group,
related_name="budgets",
on_delete=models.CASCADE,
)
Where each Group has either 0 or > 5 budgets assigned
I am trying to sort a Group.objects.all() queryset by the amount of their last (as in, most recently assigned) Budget.
I know that if it was a OneToOne I could do something like the following:
Group.objects.all().order_by('budget__amount')
With ForeignKey relations I was hoping I could do something like
Group.objects.all().order_by('budgets__last__amount')
But alas, that is not a valid operation, but I am not sure on how to proceed otherwise.
Does anyone know on how to perform this sorting operation? (if it is indeed possible)
This should do it:
latest = Budget.objects.filter(group=OuterRef('pk')).order_by('-pk')
Group.objects.annotate(
latest_budget=Subquery(
latest.values('amount')[:1]
)
).order_by('latest_budget')
You can try use sorted with a custom key:
sorted(Group.objects.all(), key=lambda x: x.last__budget__amount)
Perhaps .last__budget__amount is not a valid method, but you get the idea.
I need to filter for objects where the number of elements in a ManyToMany relationship matches a condition. Here's some simplified models:
Place(models.Model):
name = models.CharField(max_length=100)
Person(models.Model):
type = models.CharField(max_length=1)
place = models.ManyToManyField(Place, related_name="people")
I tried to do this:
c = Count(Q(people__type='V'))
p = Places.objects.annotate(v_people=c)
But this just makes the .v_people attribute count the number of People.
Since python-2.0, you can use the filter=... parameter of the Count(..) function [Django-doc] for this:
Place.objects.annotate(
v_people=Count('people', filter=Q(people__type='V'))
)
So this will assign to v_people the number of people with type='V' for that specific Place object.
An alternative is to .filter(..) the relation first:
Place.objects.filter(
Q(people__type='V') | Q(people__isnull=True)
).annotate(
v_people=Count('people')
)
Here we thus filter the relation such that we allow people that either have type='V', or with no people at all (since it is possible that the Place has no people. We then count the related model.
This generates a query like:
SELECT `place`.*, COUNT(`person_place`.`person_id`) AS `v_people`
FROM `place`
LEFT OUTER JOIN `person_place` ON `place`.`id` = `person_place`.`place_id`
LEFT OUTER JOIN `person` ON `person_place`.`person_id` = `person`.`id`
WHERE `person`.`type` = V OR `person_place`.`person_id` IS NULL
My objects are set up similar to this:
class Event(Model):
pass
class Inventory(Model):
event = OneToOneField(Event)
def has_altered_item_counts(self):
return any(obj.field_one is not None or obj.field_two is not None for obj in self.itemcounts_set.all())
class ItemCounts(Model):
inventory = ForeignKey(Inventory)
field_one = IntegerField(blank=True, null=True)
field_two = IntegerField(blank=True, null=True)
Basically, I'd like to filter uniquely on Event where inventory.has_altered_item_counts would return False
I have
Q(inventory__itemcounts__field_one__isnull=True) & \
Q(inventory__itemcounts__field_two__isnull=True)
but that returns the event every time it meets those conditions. Given that result, I want to exclude that event if the number of times it appears is less than the total number of item counts. Does any of this make sense? Is this possible with filter? I really sort of need it to be, this is part of a programmatically built batch update
I ended up doing it in a "get the PKs I'm trying to exclude first, then exclude those PKs"
Example in case anyone else is interested:
counts = Q(inventory__itemcounts__field_one__isnull=False) | \
Q(inventory__itemcounts__field_two__isnull=False)
bad_pks = set(Event.objects.filter(counts).values_list("pk", flat=True))
Event.objects.exclude(pk__in=bad_pks).update(stuff)
Works like a charm.
i have a model with ratings, the results of a filter query must be in a special order for chartit (comparing ratings for trainee's) but I can't find the right way to do it. (ok I'm new to Django and python ;)
class Bewertung(models.Model):
auffassung = models.PositiveSmallIntegerField()
interesse = models.PositiveSmallIntegerField()
arbeitsabw = models.PositiveSmallIntegerField()
aufmerksamkeit = models.PositiveSmallIntegerField()
arbeitsgenauigkeit = models.PositiveSmallIntegerField()
verhalten = models.PositiveSmallIntegerField()
ausb_sach = models.PositiveSmallIntegerField(null=True, blank=True)
ausb_fuehr = models.PositiveSmallIntegerField(null=True, blank=True)
the query:
qs = Bewertung.objects.filter(pk__in=pk_list)
I want to compare the integer values in a multi bar chart e.g.
auffassung_from_pk(1,2,3) interesse_from_pk(1,2,3) .. n
but every try ends in a list with a lot of unordered values
(Auffassung_from_pk(1), interesse_from_pk(1), Auffassung_from_pk(2) ..)
I can't find a way to solve it nice and efficient in an python way.
so I need a little help, can you help?
#Sachin Kukreja correct, separate it and order it. so every field (e.g. auffassung must be one list with every result from the queryset.)
if I have 3 resulting query sets (pk_list=(1,2,3)) I need something like ((1,2,1),(2,3,3)...) ((auffassung),(interesse))
#Rajez there are no multiple filters
i set all my approach's to zero to start new. I have only this (in the Django shell)
for q in qs:
print(q.auffassung)
print(q.interesse)
i am really struggling at this at the moment
try it:
import itertools
qs = Bewertung.objects.filter(pk__in=pk_list)
values = qs.values_list('auffassung', 'interesse')
result = list(itertools.chain(*values))
I've got a model like this:
class Thing(models.Model):
property1 = models.IntegerField()
property2 = models.IntegerField()
property3 = models.IntegerField()
class Subthing(models.Model):
subproperty = models.IntegerField()
thing = modelsForeignkey(Thing)
main = models.BooleanField()
I've got a function that is passed a list of filters where each filter is of the form {'type':something, 'value':x}. This function needs to return a set of results ANDing all the filters together:
final_q = Q()
for filter in filters:
q = None
if filter['type'] =='thing-property1':
q = Q(property1=filter['value'])
elif filter['type'] =='thing-property2':
q = Q(property2=filter['value'])
elif filter['type'] =='thing-property2':
q = Q(property3=filter['value'])
if q:
final_q = final_q & q
return Thing.objects.filter(final_q).distinct()
Each Subthing has a Boolean property 'main'. Every Thing has 1 and only 1 Subthing where main==True.
I now need to add filter that returns all the Things which have a Subthing where main==True and subproperty==filter['value']
Can I do this as part of the Q object I'm constructing? If not how else? The queryset I get before my new filter can be quite large so I would like a method that doesn't involve looping over the results.
It's a bit easier to understand if you explicitly give your Subthings a "related_name" in their relationship to the Thing
class Subthing(models.Model):
...
thing = models.ForeignKey(Thing, related_name='subthings')
...
Now, you use Django join syntax to build your Q object:
Q(subthings__main=True) & Q(subthings__subproperty=filter['value'])
The reverse relationship has the default name 'subthing_set', but I find that it's easier to follow if you give it a better name like 'subthings'.
Using (instead of final_q=Q() in the beginning)
final_q=Q(subthing_set__main=True)
sub_vals = map(lambda v: v['value'], filters)
if sub_vals:
final_q = final_q & Q(subthing_set__subproperty__in=sub_vals)
should get you what you want, you can also adjust your loop to build the sub_vals list and apply it after the loop.
subthing_set is and automatically added related field added to the Thing to access related Subthings.
you can assign another related name, e.g.
thing=models.ForeignKey(Thing,related_name='subthings')