Complex query with Django (posts from all friends) - python

I'm new to Python and Django, so please be patient with me.
I have the following models:
class User(models.Model):
name = models.CharField(max_length = 50)
...
class Post(models.Model):
userBy = models.ForeignKey(User, related_name='post_user')
userWall = models.ForeignKey(User, related_name='receive_user')
timestamp = models.DateTimeField()
post = models.TextField()
class Friend(models.Model):
user1 = models.ForeignKey(User, related_name='request_user')
user2 = models.ForeignKey(User, related_name='accept_user')
isApproved = models.BooleanField()
class Meta:
unique_together = (('user1', 'user2'), )
I know that this may not be the best/easiest way to handle it with Django, but I learned it this way and I want to keep it like this.
Now, all I want to do is get all the post from one person and it's friends. The question now is how to do it with the Django filters?
I think in SQL it would look something like this:
SELECT p.* FORM Post p, Friend f
WHERE p.userBy=THEUSER OR (
(f.user1=THEUSER AND f.user2=p.userBy) OR
(f.user2=THEUSER AND f.user1=p.userBy)
)
With no guarantee of correctness, just to give an idea of the result I'm looking for.

from django.db.models import Q
Post.objects.filter( \
Q(userBy=some_user) | \
Q(userBy__accept_user__user1=some_user) | \
Q(userBy__request_user__user2=some_user)).distinct()
UPDATE
Sorry, that was my fault. I didn't pay attention to your related_name values. See updated code above. Using userBy__accept_user or userBy__request_user alone won't work because that'll be a reference to Friend which you can't compare to to User. What we're doing here is following the reverse relationship to Friend and then once we're there, seeing if the other user on the friend request is the user in question.
This also illustrates the importance of describing reverse relationships appropriately. Many people make the same mistake you've made here and name the related_name after the model they're creating the FK to (User), when actually, when we're talking about reversing the FK, we're now talking about Friend. Simply, your related names would make more sense as something like: friend_requests and accepted_friends.

(with User u)
friends_u1 = Friend.objects.filter(user1 = u).getlist('user2_id', flat=True)
friends_u2 = Friend.objects.filter(user2 = u).getlist('user1_id', flat=True)
friends_and_user = friends_u1+friends_u2+u.id
Post.objects.filter(userBy__id__in = friends_and_user)

Related

Retrieving model objects created by different users in Django

I am making simple feed which consists of entries made by authors which current user is subscribed to. I have 3 models which are default user model, my "Post" model which is related to User via ForeignKey:
class Post(models.Model):
...
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
...
"Relations" model which has 2 fields:
follower = models.ForeignKey(User, on_delete=models.CASCADE, related_name="follows")
following = models.ForeignKey(User, on_delete=models.CASCADE, related_name="followed")
So I wrote this code to retrieve needed posts:
user = request.user
posts = Post.objects.filter(author__in = [relation.following_id for relation in user.follows.all()]).all()
And honestly it works just fine, but is there any way to make my query better? Thank you.
Yes, you can make the JOIN in the database, so:
Post.objects.filter(author__followed__follower=user)
or if you want to include Post objects for which user is the author, you can work with Q-objects:
from django.db.models import Q
Post.objects.filter(Q(author=user) | Q(author__followed__follower=user))
Since you do the JOIN at the database side, you do this in one query, wheras the list comprehension will be performed in 2 queries.

How to check if user is in a certain table

I have my user table in django, and to differ all the users I created two tables, (Teacher and Student).
Both tables are getting an fk from user
So, in order to make authorization how do I check if one's user is in a certain table.
I need to check it this way
def test_func(self):
return self.request.user.check..if..it..exists..in..table
My models are like this.
class Teacher(models.Model):
User = models.OneToOneField(settings.AUTH_USER_MODEL)
This depends on how your models are set up.
If your Teacher model looks something like this;
class Teacher(models.Model):
user = models.ForeignKey(User)
Then you should be able to check if the user is a teacher by using the implicit backref;
self.request.user.teacher_set.exists()
As the question has been updated to show that the model is slightly different than I anticipated, here is an update.
class Teacher(models.Model):
user = models.OneToOneField(User)
Which means that the backref will be a little different.
hasattr(self.request.user, "teacher")
As you've mentioned that you are doing this inside a django template, I'm pretty sure that the following will work:
{% if user.teacher %}
Since you haven't posted your models, I am giving you a rough idea how to do it.
in your views.py -
from .models import Teacher,Student
def test_func(request):
user = request.user
if (Teacher.objects.filter(user=user).count() > 0) or (Student.objects.filter(user=user).count > 0):
#do your stuffs here..
One way is to query both tables:
teacher = Teacher.objects.filter(user=self.request.user)
student = Student.objects.filter(user=self.request.user)
if teacher or student:
# do what you want.
If you put in your relation the argument "related_name" you can do it using inverse relationship
class SomeTable(models.Model):
user = models.ForeignKey(
User, #Your user model or Django one
verbose_name = "User",
related_name = "inverse_relation_name"
)
Then you have to call using keyword arguments for the filters:
SomeTable.inverse_relation_name.filter(id=self.request.user.id) #You will get a queryset
Or
SomeTable.inverse_relation_name.get(id=self.request.user.id) # You will get the object or a exception

Django count foreign key through relation

I am building a simple forum application and I need some help with counting foreign key objects via through relation.
Let's say my models look like this:
class Forum(models.Model):
title = models.CharField(max_length=255)
description = models.TextField()
slug = models.SlugField(unique=True, blank=True)
class Thread(models.Model):
title = models.CharField(max_length=255)
forum = models.ForeignKey(to=Forum, related_name='threads')
slug = models.SlugField(unique=True, blank=True)
class Post(models.Model):
body = models.TextField()
author = models.ForeignKey(User)
thread = models.ForeignKey(to=Thread,related_name='posts')
Now we create forum object.
forum = Forum.objects.get(slug=forum)
We can count the number of threads inside forum like this: forum.threads.count()
My question is, how can i count all the posts in a forum?
I've tried something like all_posts = forum.thredas.posts.count() but as expected it didn't work.
Thanks!
Generally in Django it is a good principle that when you want to do something with a model, you should start your query from that model. So, since you need to count posts, you should start with the Post model.
From there you can use the double-underscore syntax to filter to the particular Forum you want.
forum_posts = Post.objects.filter(thread__forum=my_forum)
forum_post_count = forum_posts.count()

Django: follow relations backwards

Hey, I have models like this:
class Galleries(models.Model):
creation_date = models.DateTimeField()
name = models.CharField(max_length=255, unique=True)
gallery_type = models.ForeignKey(Categories)
class Categories(models.Model):
handle = models.CharField(max_length=255, unique=True)
class Values(models.Model):
category = models.ForeignKey(Categories)
language = models.CharField(max_length=7)
category_name = models.CharField(max_length=50)
And now, I just want to reach the values of categories by starting from Galleries. For example: galleries = Galleries.objects.get(id=1). And now I want to reach somehow the values by using this "galleries" object... To get values with specific language would be much more better... I miss skills in Django ORM, so if you can, please point me to some docs or give some code example. Thanks!
galleries = Galleries.objects.get(id=1)
values = galleries.gallery_type.values_set.filter(language='language')
Interestingly, you used the exact wording that the docs use to refer to the related field lookups. I always found the definition strange to the gut, maybe because they put it in quotes.
FOLLOWING RELATIONSHIPS "BACKWARD"
http://docs.djangoproject.com/en/1.2/topics/db/queries/#following-relationships-backward
You may want to use the select_related method of objects so you reduce the number of queries you are making. select_related
gallery = Galleries.objects.select_related().get(id=1)
You can set a related name for the Values model in the category fk:
class Values(models.Model):
category = models.ForeignKey(Categories, related_name="categories")
language = models.CharField(max_length=7)
category_name = models.CharField(max_length=50)
now you can get your list of values for a specific language by doing
values = gallery.gallery_type.categories.filter(language="language")

Django import problem with models.py and multiple ManyToManyFields()

I am working on creating a simple contest submission system using django. This is my first real django project. Basically each user can view a list of problems, submit a file, and view a results page.
Each problem can be associated with multiple contests, and different contests can use the same problem. Because of this, both problem and contest have a manyToManyField with each other. This is what is causing my problem.
Here is the initial models.py implementation I am going with:
startfile
from django.db import models
class User(models.Model):
username = models.CharField(max_length=50)
firstname = models.CharField(max_length=50)
lastname = models.CharField(max_length=50)
class Problem(models.Model):
name = models.CharField(max_length=50)
filename = models.CharField(max_length=300)
contests = models.ManyToManyField(Contest)
class Contest(models.Model):
name = models.CharField(max_length=50)
problems = models.ManyToManyField(Problem)
date = models.DateField()
class Submission(models.Model):
user = models.ForeignKey(User)
problem = models.ForeignKey(Problem)
filename = models.CharField(max_length=300)
endfile
Is there a simple way to fix this? Or should I rethink my entire layout? I tried breaking each class into its own django app but I don't think thats how I should do it. The error I get is that Contest can not be found (because it exists lower in the file).
All advice is appreciated!
You don't need a ManyToManyField in both Contest and Problem. Many-to-many fields are already bidirectional. Just put it on one - doesn't matter which.
Djano will automatically create the reverse relation for you, so you only need to create it one end, eg.
class Problem(models.Model):
name = models.CharField(max_length=50)
filename = models.CharField(max_length=300)
contests = models.ManyToManyField(Contest, related_name='problems')
related_name gives you the possibility to assign a name to the reverse relation. Without defining the relation on the Contest model, you can then access eg. a_contest.problems.all()!

Categories

Resources