In Django, I have a User model and a Following model:
class User():
uid = models.UUIDField(primary_key=True)
class Following():
follower_uid = models.ForeignKey(USER_MODEL, related_name="followers")
followed_uid = models.ForeignKey(USER_MODEL, related_name="following")
with corresponding database tables for both object types.
When Django loads a User object, I want to also load the number of followers of that user in the same database query, i.e. using a join. What I don't want to do is load the user first and then do a second query to get the number of followers.
Is this possible using the Django object model or do I have to write raw sql?
I also want to load a second-degree count; that is, the number of followers of the followers of the user. As before, I want this count to be loaded in the same database query as the user itself.
Appreciate specific syntax and examples, I have read a ton of Django documentation and nothing seems to answer this. Thanks!
you can do this query:
from django.db.models import Count
>>> user = User.objects.filter(pk=some_id).annotate(num_followers=Count('followers'))
>>> user
[<User: someuser>]
>>> user[0].id
some_id
>>> user[0].num_followers
123
Related
what does these lines of code mean in Django View: i couldn't find a details explanation, I came to Django from a Laravel background, so I can understand the models and relationships... thanks
customer = request.user.customer
product = Product.objects.get(id=productId)
order, created = Order.objects.get_or_create(customer=customer, complete=False)
orderItem, created = OrderItem.objects.get_or_create(order=order, product=product)
customer = request.user.customer
The request object has a user, the user is the authenticated user (if no user is authenticated then the AnonymousUser object is returned instead). In this example the User model (i.e. the user table) has a field called customer and we are accessing that field.
product = Product.objects.get(id=productId)
Here we are simply querying the Product table for a specific product with the given productId. Note, Django will raise an error if two records are returned when you use the .get() method (i.e. if two rows in the Product table have the same productId.
order, created = Order.objects.get_or_create(customer=customer, complete=False)
Next we use the get_or_create() method to look up an order based off of the customer (the value of which we extracted above. If an order cannot be found we will create one instead. The value of createdwill be True if a neworder` was created or False if one already existed.
orderItem, created = OrderItem.objects.get_or_create(order=order, product=product)
Just as above we are getting or creating an OrderItem using the order and product fields.
I have a Profile model with a decorator property
#property
def primary_phone(self):
When I query for the primary phone for a given id like this it works
x = Profile.objects.get(id=3)
x.primary_phone
outputs
Out[3]: '+256771000022'
However filtering for the same like this
Profile.objects.filter(primary_phone="+256771000022").exists()
outputs
FieldError: Cannot resolve keyword 'primary_phone' into field. Choices are: _created_at, _modified_at, apierror, business,...)
#porperty is a managed python attribute https://docs.python.org/3/library/functions.html#property and can not be queried through DB because its data is not part of the DB.
Use a django model field if you need to query it or use Python to handle this attribute after querying the DB.
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.
We have a postgres jsonb field in Django that allows users to store arbitrary user data. We wish to allow users to query this field but are unsure about the security implications.
The model
from django.db import models
class Item(models.Model):
user = models.ForeignKey("user", null=False)
meta = JSONField()
Query
def custom_query(operation, value):
qs = Item.objects.filter(user=user)
params = {
"meta__" + operation: value
}
qs = qs.filter(**params)
Usage:
Assuming meta is {"a": 1}.
custom_query(operation="contains", value={"a": 1})
custom_query(operation="a", value=1)
The above should be valid and equivalent queries.
Is this a secure way to perform the query?
I'd suggest adding an allowlist for valid operations and maybe checking the value is suitably simple (only strings, for example), but in the presence of other filters that ensure the rows that can be selected are those the user can see, I don't see a problem with this.
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 }}