I am having trouble writing a query using Django ORM, I want to find the latest record in each group. I am putting chat messages in the model and I want to find the latest chat of each user and show chats latest chat of each user and with the latest user's chat on the home screen just like in WhatsApp, Skype or similar apps. Currently, I am using the following query,
Chats.objects.all().order_by('user_id', '-date').distinct('user_id')
Using this I am able to get the latest chat of each user but I am not able to get the sequence correct. The result of the query is in the order of which the users were created in the database which I understand is correct, but I want to show the user who sent the latest chat at the top.
My Models.py
class Chats(models.Model):
user_id = models.ForeignKey(User, on_delete=models.CASCADE)
chat = models.CharField(max_length=1023, null=True, blank=True)
date = models.DateTimeField(auto_now_add=True)
Thank you so much, Please let me know if any other information is required.
Option 1: Order on the Django/Python layer
The items are first sorted by user_id, and only in case of a tie, it takes the one with the latest date. But that means that you eventually get for each user a Chats object, ordered by the user_id.
I think here your only option is to sort it at the Django/Python level, so wrap it into a list, and sort by the date:
from operator import attrgetter
items = list(Chats.objects.order_by('user_id', '-date').distinct('user_id'))
items.sort(key=attrgetter('date'), reverse=True)
# work with items
and then render the items in the template.
Option 2: Annotate the User model instead
Another option is to annotate the User model and thus work with a QuerySet of User objects:
from django.db.models import Max, OuterRef, Subquery
User.objects.filter(
chats__isnull=False
).annotate(
last_date=Max('chats__date'),
last_message=Subquery(
Chat.objects.filter(user_id=OuterRef('pk')).order_by('-date').value('chat')[:1]
)
).order_by('-last_date')
Here the User objects will have an extra attribute .last_date with the latest date time of the object, and .last_message with that message.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Related
I'm building a marketplace that have 2 user types i.e. buyers and sellers.
Now I want to create a relationship model between buyers, sellers and OrderedItems, so that the sellers gets notified once the buyers orders an item.
This is the model I made:
class Ordered(models.Model):
ordered_items = models.ManyToManyField(Cart)
seller = models.ForeignKey(SellerProfile, on_delete = models.SET_NULL, null = True)
buyer = models.ForeignKey(CustomerProfile, on_delete = models.CASCADE)
ordered_on = models.DateTimeField(auto_now_add = True)
But I don't know how to implement this in views.py
Any suggestion will be really helpful
Thank you
As per the current model relation you can use pre_save or post-save signals on ordered model. Whenever you update a new value(item added by buyer) in ordered_items(many-to-many relation) you can send a mail to seller linked with that buyer. That mailing part will be defined inside pre or post signals .
In views.py you will get input parameters as follows -:
seller_id
ordered_item
buyer_id
And you will search for seller_id and buyer_id combination using filter statement in Ordered model and set ordered_item in that field. After that model save function will be called and after that post_save signal will be triggered and will send the mail to seller.
There will be 2 conditions for this scenario -:
Either you create a new value if user is buying his/her first item.
Buyer is adding another item in cart. In this case you will update the existing value.
In Either case model's save function will automatically be initiated.
Reference -:
Pre-save and Post-save signals in django
Django Official Documentation on signals
I have one model called Weight (filled by User input/choice) and another called Enterprise.
class Weight(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="weights")
weight_of_history = models.IntegerField(null=True, blank=True)
class Enterprise(models.Model):
...
The weight is saved, one per user, and replaced everytime the user choose new one.
Inside the Enterprise class, I am creating an property that must get the "weight_of_history" (depending on the user, who has chosen the weight) from Weight class, but the models have no Foreign key or related name between them.
class Enterprise(models.Model):
...
#property
def ranking(self):
weight_of_history = <-- HERE I NEED TO TAKE WEIGHT_HISTORY FROM THE FIRST MODEL
THEN I COULD CALCULATE
How could I do that? Thank you!
You can use django's powerful query functionality and fetch the required objects from the db. Here are the docs that might help you with that. Django docs are amazing, so I would recommend that you read up on queries, models, and forms to have easier time with you project.
For your example we can fetch all the weights for the user in one query by filtering the weights by user. In fact, django ORM allows for chaining filters and you can create really sophisticated queries
class Enterprise(models.Model):
...
#property
def ranking(self):
weight_of_history = Weight.objects.filter(user=some_user)
If you do not know the user beforehand, then you can do inside the view and grab the user that makes the request and filter using this user:
#views.py
user_making_request = request.user
weight_of_history = Weight.objects.filter(user=user_making_request)
What is the best way to achieve low load on the database or application server for this use case:
Let's say I want to build a web application that has for each user an overview page. The overview page shows in an aggregated form for each user the user's data. For example, if it were a library application it would show how many times the user visited the library in total, how many books he read in total, how many books were delivered delayed in total, how many minutes he spend in the building. Each time the user visits the overview page the up-to-date values should be displayed. While the user interacts with the site the numbers change.
What I could do is for every overview page refresh do several counts in the database. But that would be expensive.
views.py
def overview(request, userID):
booksCount = Book.objects.count()
booksReadCount = Book.objects.filter(UserID=userID, Status='read').count()
# ... many more, same way
libraryVisitedCount = LibraryVisits.objects.filter(UserID=userID).count()
# many counts like these on different tables for the user
data = {
"booksCount" : booksCount,
"booksReadCount" : booksReadCount,
# ... many more
"libraryVisitedCount" : libraryVisitedCount
}
render(..., context=data)
I have thought I could store a JSON object with the data to be presented on the overview page in a database table and I update the JSON each time an event happend on the site which affects the count of objects.
Or I could use a materiliazed view but to refresh it I would have to recalculate all the data of all users each time, right?
Other ideas? I'm using django webframework and postgres database.
TL;DR: I wondered isn't there a better way to receive counts than do several counts in the database each time?
Thanks.
Lets say, in Book, LibraryVisit etc models, there is ForeignKey to User model with related_name like this:
class Book(models.Model):
UserID = models.ForeignKey(User, related_name='books', on_delete=DO_NOTHING)
class LibraryVisit(models.Model):
UserID = models.ForeignKey(User, related_name='library_visit', on_delete=DO_NOTHING)
Then you can use annotation and conditional expression like this:
from django.db.models import Case, IntegerField, Sum, When
def overview(request, userID):
users = User.objects.filter(pk=userId)
users = users.annotate(
booksReadCount=Sum(
Case(
When(book__Status='read', then=1),
output_field=IntegerField()
)
)
).annotate(library_visited_count=Count('library_visit'))
# FYI: please use snake_case when defining object attribute(like model fields) as per PEP-8 style guide
data = {
"user_object" : users.first(), # taking first item of the User queryset. Also DB is hit once in this step
"booksCount" : Book.objects.count()
}
# access counts in view like this:
# user.book_read_count
# user.library_visited_count
return render(..., context=data)
# bold marked words are related_name
And render counts in template like this:
{{ user_object.book_read_count }}
{{ user_object.library_visited_count }}
I'm trying to track Foreign Keys using django-field-history, but when I add it, it does additional queries on every page using the Model
For example
from field_history.tracker import FieldHistoryTracker
class Author(models.Model):
user = models.ForeignKey('auth.user)
field_history = FieldHistoryTracker(['user'])
will always give more queries on pages using Author, like so
SELECT ••• FROM "auth_user" WHERE "auth_user"."id" = '2'
1239 similar queries. Duplicated 1235 times.
I've tried using user_id instead of user in Field History Tracker, but it will always return None. Using user.id or anything like it just returns an error.
I really need to keep that history data, but not at the cost of thousands of additional queries.
Also, would really enjoy keeping django-field-history as my whole DB is using it, but I'm aware I might have to switch package, and if so, which one would you advise ?
As far as my understanding goes, you are trying to log which user has updated, for this you should use _field_history_user as described in the documentation.
For example:
class Pizza(models.Model):
name = models.CharField(max_length=255)
updated_by = models.ForeignKey('auth.User')
field_history = FieldHistoryTracker(['name'])
#property
def _field_history_user(self):
return self.updated_by
It would always update which user has updated the row for this table.
I want to create a queryset which retrieve users efficiently ordered by the create date of the last Message they sent OR received (like most of the chat interfaces do). I am using Django Private Chat, which uses this simple model for messages.
I simply don't know how writing the order_by statement considering the previous model. Maybe annotate, Subquery or other query expressions would be convenient in this case.
users = User.objects.filter(
username__icontains=user,
id__in=request.user.followers
).order_by( ????? )
You should be able to do this using annotate and something like;
from django.db.models import Max
users = User.objects.filter(
username__icontains=user,
id__in=request.user.followers
).annotate(
last_message=Max('messages_created')
).order_by('-last_message')
The Max function takes the related name from the Message model, found here, then the created timestamp of that model.