How I can join two ManyToManyField number to Integer Field in Django - django

I want to get the numbers of values in Two ManyToMany Field and compain both to one Integer Field by function (def)
class Video(models.Model):
viewers = models.ManyToManyField(Account, related_name='video_views')
viewers_by_ip = models.ManyToManyField(UsersByIP, default='192.168.0.1', blank=True)
viewers_count = models.IntegerField('Here I want to get the number of both field')

You many do it without saving sum of count viewers_by_ip and viewers into database, actually you have to ways.
Make a property method that will calculate it pet each object:
class Video(models.Model):
#property
def total_viewes_count(self):
return self.viewers.count() + self.viewers_by_ip.count()
Add a annotation to queryset which getting model object for you:
from django.db.models import Count
Video.objects.annotate(
total_viewes_count=Count('viewers')+Count('viewers_by_ip')
).get(pk=<Video's primary key>)
in both cases you may use it in template like that:
{{ video.total_viewes_count }}

Related

Return a value of a model based on the max value of another field of the model

I have a model in Django with two fields: name and meter. I have created a views file, and I want to find and print the name with the biggest meter. The code below find and prints the biggest meter itself which is good, but not what I want. I want to print the name that is related to that meter. Any idea how to do so?
My Views:
def details (request):
top_meter = Energy_Consuption.objects.aggregate(Max('meter'))
return render(request,'details.html', {'top_meter':top_meter})
(The view is connected to an HTML page.)
My Model:
class Energy_Consuption(models.Model):
name=models.IntegerField()
meter=models.IntegerField()
class Meta:
db_table:'Energy_Consuption'
You can do something like this:
Energy_Consuption.objects.filter(
meter=Energy_Consuption.objects.aggregate(Max('meter'))['meter__max']
).values_list('name', flat=True)
This will return the list of the names of the Energy_Consuption objects that matches the max value. Note that you need filter here, because there can be more than one Energy_Consuption with the max value.
You can just sort the QuerySet by meter and take the latest item.
Additionally, you can use getattr in case of QuerySet being empty.
biggest_item = EnergyConsumption.objects.latest('meter')
biggest_name = getattr(biggest_item, 'name', None)
If your meter field is not unique, you can provide additional fields to the latest method to disambiguate:
biggest_item = EnergyConsumption.objects.latest('meter', 'id')

how to call back unique together constraints as a field django

i'm trying to call back unique constraints field , in my project i have to count number of M2M selected
class Booking(models.Model):
room_no = models.ForeignKey(Room,on_delete=models.CASCADE,blank=True,related_name='rooms')
takes_by = models.ManyToManyField(Vistor)
#property
def no_persons(self):
qnt = Booking.objects.filter(takes_by__full_information=self).count()#but this doesnt work
return qnt
Cannot query "some room information": Must be "Vistor" instance.
class Vistor(models.Model):
full_name = models.CharField(max_length=150)
dob = models.DateField(max_length=14)
city = models.ForeignKey(City,on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(fields=['full_name','dob','city'],name='full_information')
]
def __str__(self):
return f'{self.full_name} - {self.city} - {self.dob}'
it it possible to access full_information through Booking model ? thank you ..
If you want to count the number of Visitors related to that booking, you can count these with:
#property
def no_persons(self):
self.taken_by.count()
This will make an extra query to the database, therefore it is often better to let the database count these in the query. You can thus remove the property, and query with:
from django.db.models import Count
Booking.objects.annotate(
no_persons=Count('takes_by')
)
The Bookings that arise from this QuerySet will have an extra attribute no_persons with the number of related Visitors.

Prefetch or annotate Django model with the foreign key of a related object

Let's say we have the following models:
class Author(Model):
...
class Serie(Model):
...
class Book(Model):
authors = ManyToManyField(Author, related_name="books")
serie = ForeignKey(Serie)
...
How can I get the list of authors, with their series ?
I tried different combinations of annotate and prefetch:
list_authors = Author.objects.prefetch(Prefetch("books__series", queryset=Serie.objects.all(), to_attr="series"))
Trying to use list_authors[0].series throws an exception because Author has no series field
list_authors = Author.objects.annotate(series=FilteredExpression("books__series", condition=Q(...))
Trying to use list_authors[0].series throws an exception because Author has no series field
list_authors = Author.objects.annotate(series=F('books__series'))
returns all possible combinations of (author, serie) that have a book in common
As I'm using PostgreSQL for my database, I tried:
from django.contrib.postgres.aggregates import ArrayAgg
...
list_authors = Author.objects.annotate(series=ArrayAgg('books__serie', distinct=True, filter=Q(...)))
It works fine, but returns only the id of the related objects.
list_authors = Author.objects.annotate(series=ArrayAgg(
Subquery(
Serie.objects.filter(
livres__auteurs=OuterRef('pk'),
...
).prefetch_related(...)
)
))
fails because it needs an output_field, and a Model is not a valid value for output_field
BUT
I can get the number of series for an author, so why not the actual list of them:
list_authors = Author.objects.annotate(nb_series=Count("books__series", filter=Q(...), distinct=True)
list_authors[0].nb_series
>>> 2
Thus I assume that what I try to do is possible, but I am at a loss regarding the "How"...
I don't think you can do this with an annotation on the Author queryset - as you've already found you can do F('books__series') but that will not return distinct results. Annotations generally only make sense if the result is a single value per row.
What you could do instead is have a method on the Author model that fetches all the series for that author with a relatively simple query. This will mean one additional query per author, but I can't see any alternative. Something like this:
class Author:
def get_series(self):
return Serie.objects.filter(book__authors=self).distinct()
Then you just do:
list_authors = Author.objects.all()
list_authors[0].get_series()

How to filter values returned on a filed defined as foreign key

In models.py I have two models:
class X(models.Model):
id = models.AutoField(primary_key=True)
class Y(models.Model):
id_fk = models.ForeignKey(X,related_name='x_y')
in views.py:
class CreateY(generic.CreateView):
model = Y
now the field Y.id_fk will be a list of values of all in X.id
the question is:
how can i add extra condition?
means if X.id = 1,2,3,4,5
then available values for Y.id_fk will be the same
but what if i want to display only values <= 3
how can I accomplish this?
Perhaps queryset lookups could be helpful using the underscore notation.
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#lt is an example of retrieving field data less than some value.

Django: construct a QuerySet inside a view?

I have models as follows:
class Place(models.Model):
name = models.CharField(max_length=300)
class Person(models.Model):
name = models.CharField(max_length=300)
class Manor(models.Model):
place = models.ManyToManyField(Place, related_name="place"))
lord = models.ManyToManyField(Person, related_name="lord")
overlord = models.ManyToManyField(Person, related_name="overlord")
I want to get all the Places attached with the relation 'lord' to a particular person, and then get the centre, using a GeoDjango method. This is as far as I've got:
person = get_object_or_404(Person, namesidx=namesidx)
manors = Manor.objects.filter(lord=person)
places = []
for manor in manors:
place_queryset = manor.place.all()
for place in place_queryset:
places.append(place)
if places.collect():
centre = places.collect().centroid
However, this gives me:
AttributeError at /name/208460/gamal-of-shottle/
'list' object has no attribute 'collect'
Can I either (a) do this in a more elegant way to get a QuerySet of places back directly, or (b) construct a QuerySet rather than a list in my view?
Thanks for your help!
The way you're doing this, places is a standard list, not a QuerySet, and collect is a method that only exists on GeoDjango QuerySets.
You should be able to do the whole query in one go by following the relations with the double-underscore syntax:
places = Place.objects.filter(manor__lord=person)
Note that your use of related_name="place" on the Manor.place field is very confusing - this is what sets the reverse attribute from Place back to Manor, so it should be called manors.