django count of manytomany field - django

i wanted to place the foo/view code below into a property under the Foo model object, but the debug message says 'bar' cannot be found. Why does it work in the views.py, but not work when i place it in models.py( i did remember to import Bar)?
thanks!
foo/models.py
class Foo(models.Model):
title = models.CharField(_(u'Title'), max_length=600)
bar/models.py
class Bar(models.Model):
foo = models.ManyToManyField(Foo)
eg_id = models.PositiveIntegerField(_(u'Example ID'), default=0)
foo/views.py
from django.db.models import Count
qs = Foo.objects.filter(
bar__eg_id__in=id_list
).annotate(
bar_count=Count('bar')
).order_by('bar_count')

Your Bar class probably hasn't been defined yet in models.py - Try moving it above Foo.

Related

Order queryset using the number of related objects in Django

Let's say I have the following in models.py:
class A(models.Model):
...
class B(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE)
What I want to do is sort a queryset for A based on how many B objects they have, in descending order. What is the best approach to this issue? Thanks for any help.
You can work with a .annotate(…) [Django-doc] and then .order_by(…) [Django-doc]:
from django.db.models import Count
A.objects.annotate(
nb=Count('b')
).order_by('-nb')
Since django-3.2 you can work with .alias(…) [Django-doc] to prevent calculating this both as column and in the ORDER BY clause:
from django.db.models import A
A.objects.alias(
nb=Count('b')
).order_by('-nb')
This is:
queryset = A.objects.filter().order_by('B_A')
Here 'B_A' you have to put the related name
class A(models.Model):
...
class B(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE, related_name = 'B_A')

django models to model 'has-a' and 'contains' relationships

I have the following entities:
from django.db import models
class Foo(models.Model):
pass
class Bar(models.Model):
pass
class FooBar(models.Model):
pwned_foo = # Foobar contains one Foo object
bars = # Collection of Bar objects
How do I express the relation between Foo, Bar and FooBar in the FooBar class?
I think you want a OneToOneField and a ForeignKey like this:
from django.db import models
class Foo(models.Model):
pass
class Bar(models.Model):
foobar = models.ForeignKey(FooBar, related_name='bars')
class FooBar(models.Model):
pwned_foo = models.OneToOneField(Foo)
bars = # Collection of Bar objects
# you can access bars via the reverse relationship like
# myfoobar.bars
This assumes you want a 1-N relation with bars. If you want an N-N relation, then use a ManyToManyField.
If you really need to keep Bar clean of any relationship (can you explain why?) you could try something like ArrayField with the caveats that this only works on postgresql, and you would need to reimplement relationships in querysets, etc... And I suspect performance would suffer too...
from django.db import models
class Foo(models.Model):
pass
class Bar(models.Model):
pass
class FooBar(models.Model):
pwned_foo = models.OneToOneField(Foo, on_delete=models.CASCADE)
bars = models.ForeignKey(Bar, on_delete=models.CASCADE)

Django: Use select_related without a ForeignKey field

I have two models which are used with a database I don't control. Both are set with managed = False. The first model has a field which is a foreign key to the second model, but it's implemented as a CharField, not as a ForeignKey.
Is it possible to use select_related on the first model to access properties of the key'd second model?
Here's an example:
class Foo(models.Model):
class Meta:
managed = False
fieldone = models.CharField(max_length=10)
myfk = models.CharField(max_length=20) # In practice, this points to Bar.localkey
class Bar(models.Model):
class Meta:
managed = False
localkey = models.CharField(max_length=20)
someotherattribute = models.CharField(max_length=100)
Foo.objects.all().select_related('Bar') # I know this won't work, but is there something that will?
No, because there's nothing related.
But if you (or someone for some reason) have stored the ID (or some unique value such as localkey) from the 'related' object, you could perform a filter based on it.
foo = Foo.objects.first() # Pick one Foo object
foo_bar = Bar.objects.get(localkey=foo.myfk)
To make this looks like select_related you could try this:
class Foo(models.Model):
class Meta:
managed = False
fieldone = models.CharField(max_length=10)
myfk = models.CharField(max_length=20)
def bar(self):
return Bar.objects.get(localkey=self.myfk)
# probably you will need to manage common error when performing a .get()
# DoesNotExist and MultipleObjectsReturned
Then use like this:
foos = Foo.objects.all()
for foo in foos:
print foo.bar()
I am not sure if this is a good idea but you could decorate .bar() method as a property:
...
#property
def bar(self):
return Bar.objects.get(localkey=self.myfk)
And then call it like this:
foo # some random Foo object
foo.bar # this should return the 'related' Bar object

Django Rest Framework - Reverse relations

how do you include related fields in the api?
class Foo(models.Model):
name = models.CharField(...)
class Bar(models.Model):
foo = models.ForeignKey(Foo)
description = models.CharField()
Each Foo has a couple of Bar's related to him, like images or what ever.
How do I get these Bar's displaying in the Foo's resource?
with tastypie its quit simple, im not sure with Django Rest Framework..
I got it working! Shweeet!
Ok this is what I did:
Created serializers, Views and URLS for the Bar object as described in the Quickstart docs of Django REST Framework.
Then in the Foo Serializer I did this:
class FooSerializer(serializers.HyperlinkedModelSerializer):
# note the name bar should be the same than the model Bar
bar = serializers.ManyHyperlinkedRelatedField(
source='bar_set', # this is the model class name (and add set, this is how you call the reverse relation of bar)
view_name='bar-detail' # the name of the URL, required
)
class Meta:
model = Listing
Actualy its real simple, the docs just dont show it well I would say..
These days you can achieve this by just simply adding the reverse relationship to the fields tuple.
In your case:
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = (
'name',
'bar_set',
)
Now the "bar"-set will be included in your Foo response.
I couldn't get the above working because I have a model called FooSomething.
I found the following worked for me.
# models.py
class FooSomething(models.Model):
name = models.CharField(...)
class Bar(models.Model):
foo = models.ForeignKey(FooSomething, related_name='foosomethings')
description = models.CharField()
# serializer.py
class FooSomethingSerializer(serializers.ModelSerializer):
foosomethings = serializers.StringRelatedField(many=True)
class Meta:
model = FooSomething
fields = (
'name',
'foosomethings',
)

Django filter query using hasattr (or something like it)

I want to perform a filter on a model and return all objects that have a specific attribute.
model.objects.filter(hasattr(model, 'attrname'))
This obviously doesn't work, but not sure how to efficiently implement something siilar.
Thanks
EDIT
An example of where I would use this is when a model is inherited from another
class model1(models.Model):
...
class model2(model1):
...
if I do a model1.objects.all() each of the returned objects that are in model2 will have an extra attribute
If the models are related, you can the isnull in the filter.
model1.objects.filter('related_name__field_name__isnull=False)
where related name is in for the foreign key in model2
For Example:
class Owner(models.Model):
user = models.CharField(max_length=10)
class Car(models.Model):
car_type = models.CharField(max_length=10)
owner = models.ForeignKey(Owner, related_name='cars',
on_delete=models.CASCADE)
For owners with cars:
owners = Owner.objects.filter(cars__id__isnull=False)
I just put it in a:
try:
....
except AttributeError:
....
The way I did it was to suppress the FieldError exception:
from django.core.exceptions import FieldError
from contextlib import suppress
with suppress(FieldError):
model.objects.filter(field_in_other_class=value)
hope that helps