how to show my post only to my friends in django - python

I want to show my post only to my friends, How can I filter my post only to my friends? I have tried to filter in html code but as my friends get more, it repeats my post more (I mean repeat one post few times )
My models.py
class PostUser(models.Model):
posttype = models.CharField(max_length=3000, default='postuser')
content = models.TextField(null = True, blank= True)
media_image = models.FileField(null = True, blank= True)
media_video = models.FileField(null = True, blank= True)
per_to = models.CharField(max_length=300, null=True, blank=True, default='everyone')
status = models.CharField(max_length=3000, default='active')
date = models.DateField(auto_now_add=True)
time = models.TimeField(auto_now_add=True)
datetime = models.DateTimeField(auto_now_add=True)
like = models.IntegerField(null=True, blank=True)
comment = models.IntegerField(null=True, blank=True)
share = models.IntegerField(null=True, blank=True)
user_pk = models.IntegerField()
class Friends(models.Model):
# Sender
friend_one = models.IntegerField()
# Reciver
friend_two = models.IntegerField()
per_to = models.CharField(max_length=300, null=True, blank=True, default='everyone')
status = models.CharField(max_length=3000, default='active')
datetime = models.DateTimeField(auto_now_add=True)
date = models.DateField(auto_now_add=True)
time = models.TimeField(auto_now_add=True)
and this is my views.py
allmyfriends = Friends.objects.filter(Q(friend_one = request.user.pk) | Q(friend_two = request.user.pk))

You can do it like :-
models.py
Choices = (
(True,'Friends can see Post'),
(False,'Friends cannot see Post'),
)
class Friends(models.Model):
...................
only_friends = models.BooleanField(max_length=30,choices=Choices,default=True)
Accessing Friends model objects in views Because our BooleanField is in Friends model so we have to access Friends model first to access only_friends BooleanField
def some_view(request,user_id):
allmyfriends = get_object_or_404(Friends,user_user_id)
p = request.user
you = p.user
friends = p.friends.all()
context = {'allmyfriends':allmyfriends,'friends':friends}
return render(request, 'mains:your_template.html', context}
urls.py
path('some_view/<int:user_id>/',views.some_view,name='some_view'),
your_template.html
Now do work carefully here.
In showing post, I assume that you're using loop for showing all of users Post. like :-
{% for posts in post %}
{% if posts.user.only_friends == True and posts.user in request.user.friends.all %}
{{ post.posttype }}
{% endif %}
{% endfor %}
Note :- This FrontEnd will only work after you set your PostUser ForeignKey relationship with User like :-
from django.contrib.auth.models import User
class PostUser(models.Model):
..................
user = models.ForeignKey(User,on_delete=models.CASCADE,default='',unique=True)
Also Set your Friends model ForeignKey RelationShip with User
class Friends(models.Model):
.............
user = models.ForeignKey(User,on_delete=models.CASCADE,default='',unique=True)
Explanation :-
In models.py :- I add a BooleanField with Choices.
In views.py :- I access Friends model to access its objects with user_id. ( Check carefully in template urls while using user_id )
In template :- I use a for loop to show all the Posts. AND In for loop i started if statement that If the user who posted a post is in request.user's friends and have BooleanField True then Post will show and if request.user isnot in posted user then the post will not be shown.

#Ramin Zamani- Your friend model is messed up. Have a look at this.
Rename your model Friends to UserFriend. It's a model that shows the relation between 2 entities - The user and his/her friends/followers. The model name shouldn't be plural.
from django.conf import settings
class UserFriend(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
friend = models.ForeignKey(settings.AUTH_USER_MODEL)
................
Also, I could have used a many-to-many relation but I prefer the foreign key relation. It's up to your preference.
Your post model needs to be fixed
class Post(models.Model):
created_by= models.ForeignKey(settings.AUTH_USER_MODEL)
...............
And if you want only specific users to let's say view a post then you can do the following.
in your views.py
post = Post.objects.get(slug=self.kwargs.get('name'))
#to check if the user trying to access the post is a friend of the postcreator user.
friend = UserFriend.objects.get(user=post.created_by,friend=self.request.user)
if friend:
render()
else:
return HttpResponseForbidden()

Related

How can I display the added Friends to their respective user friends list?

I am developing a feature same like Facebook. means a user can send a friend request and then that specific user will accept the friend request. so it means they both become friends with each other.
Now how can I display them in such a way that user A is a friend of User B and vice versa?
That's how I have developed it but can't display both of the users in the friend's list.
models.py:
class AddToNetwork(models.Model):
NETWORK_CHOICES = (
('ADD', 'Add'),
('ACCEPT', 'Accept'),
('DELETE', 'Delete'),
)
id = models.UUIDField( primary_key = True, default = uuid.uuid4,editable = False)
add_to = models.ForeignKey(User, on_delete=models.CASCADE, related_name="add_to", null=True, blank=True)
added_from = models.ForeignKey(User, on_delete=models.CASCADE, related_name="add_from", null=True, blank=True)
network_status = models.CharField(max_length=30, choices = NETWORK_CHOICES)
added_at = models.DateTimeField(auto_now_add = True)
deleted_at = models.DateTimeField(auto_now = True)
def __str__(self):
return str(self.add_to)
Sending a friend request using the following method/logic:
def addToNetwork(request, id):
try:
add_to = User.objects.get(id = id)
current_user = User.objects.get(id = request.user.id)
network = AddToNetwork.objects.create(add_to = add_to, added_from=current_user,network_status = 'ADD')
messages.success(request,f'Request Sent to {add_to.first_name} {add_to.last_name}')
return redirect('userProfileDetail', id)
except User.DoesNotExist:
add_to = None
return render(request,'network/network_userProfile.html')
Accepting a friend request using the following logic:
def acceptNetworkRequest(request, id):
try:
# if 'accept_friendRequest' in request.GET:
friend_request = AddToNetwork.objects.get(id = id)
print("\n Friend Request \n", friend_request)
friend_request.network_status = 'ACCEPT'
friend_request.save()
messages.success(request, f'You accepted {friend_request.added_from.first_name} as a friend!')
return redirect('friends', id)
except AddToNetwork.DoesNotExist:
return render(request,'network/friendRequestList.html')
Logic to display the friends in the respective profiles:
def friends(request, id):
user = User.objects.get(id = request.user.id)
if user:
approved_friends = AddToNetwork.objects.filter(Q(added_from_id = user),network_status = 'ACCEPT')
context = {
'friends': approved_friends,
# 'approved': friend_to,
# ' key' : value
}
return render(request, 'friend_list.html', context)
Now I want to use the model attribute add_to and added_from so that I can display the friends in their respective profiles. At the moment, either it displays a friend which uses the add_to attribute or added_from.
template for friends_list:
{% for friend in friends %}
<tr>
<!-- <td scope="row">{{forloop.counter}}</td> -->
<td>{{friend.add_to.first_name}} {{friend.add_to.last_name}}</td>
<br>
</tr>
</tr>
{% endfor %}
is there anyone who can guide me?
EDIT:
my user model:
class User(AbstractUser):
username = None
id = models.UUIDField( primary_key = True, default = uuid.uuid4,editable = False)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.CharField(max_length=50, unique=True)
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
friends = models.ManyToManyField("self", blank=True)
The way to get the list of friends from your current model would be something like this:
User.objects.filter(
Q(add_from=user)& # get requests sent by the user
Q(add_from__network_status='ACCEPT')| # must be accepted
Q(add_to=user)& # and those sent to the user
Q(add_to__network_status='ACCEPT') # must be accepted
)
This should work fine but you might want another model that actually stores the friend lists, you may find it to be more concise and manageable. Something like storing friends as a m2m field on the user or having a FriendList model that stores the main user and their friends.
Edit - my code above is incorrect, the add_from field accesses the related AddToNetwork objects but then doesn't access the users related to those objects. It's necessary to add __add_to and __added_from respectively to get back to the user again.
get_user_model().objects.filter(
Q(add_from__add_to=user)& # get requests sent by the user
Q(add_from__network_status='ACCEPT')| # must be accepted
Q(add_to__added_from=user)& # and those sent to the user
Q(add_to__network_status='ACCEPT') # must be accepted
)

Liked time of request.user is not showing

I just started learning Django. I am building a simple Blog App and I am trying to get the user liked time of post of request.user.
I made a Post model and a Like model. And when user like show the like time of user.
But it is not showing the liked time.
models.py
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=30, default='')
class Likepost(models.Model):
by_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_of = models.ForeignKey(Post, on_delete=models.CASCADE)
date_liked = models.DateTimeField(auto_now_add=True)
views.py
def blog_post_detail(request, post_id):
obj = get_object_or_404(Post, pk=post_id)
accessLikes = obj.likepost_set.all()
for All in accessLikes:
if request.user == All.by_user
All.request.user.date_liked
context = {'obj':obj}
return render(request, 'blog_post_detail.html', context}
What i am trying to do :-
I am trying to access liked time of request.user
It is keep showing :-
Likepost' object has no attribute 'request'
I will really appreciate your help. Thank You
You can obtain the Likepost object of a user by filtering the queryset, and try to retrieve the corresponding like:
def blog_post_detail(request, post_id):
obj = get_object_or_404(Post, pk=post_id)
likepost = obj.likepost_set.filter(by_user=request.user).first()
context = {'obj':obj, 'likepost': likepost}
return render(request, 'blog_post_detail.html', context}
Next you can render this in the template with:
{% if likepost %}
Liked by you at {{ likepost.date_liked }}
{% endif %}
Normally one can prevent multiple Likeposts for the same object and the same user with a UniqueConstraint [Django-doc]:
class Likepost(models.Model):
by_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_of = models.ForeignKey(Post, on_delete=models.CASCADE)
date_liked = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['by_user', 'post_of'],
name='like_once_per_post'
)
]
If you plan to store both like and unlike events, you get the most recent Likemodel with:
def blog_post_detail(request, post_id):
obj = get_object_or_404(Post, pk=post_id)
likepost = obj.likepost_set.filter(by_user=request.user).order_by('-date_liked').first()
# …

How to pin posts in django?

So i want to know how to pin posts in django. When i click on check, and click create post, it should pin the post to the top. And newer posts will just appear bottom, without interfering with the pinned post. The pin system should also be able to pin multiple posts. So if i pin another post, both posts should stay at the top. The other not pinned posts should just appear below.
models.py
class AskSection(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
is_pin = models.BooleanField()
likes = models.ManyToManyField(User, related_name='likes', blank=True)
is_marked = models.BooleanField(default=False)
date_posted = models.DateTimeField(default=timezone.now)
class Meta:
ordering = ['-date_posted']
verbose_name_plural = "Ask Section"
def __str__(self):
return str(self.title)
forms.py
class AskSectionCreateForm(forms.ModelForm):
is_pin = forms.BooleanField(label="pin ask post", required=False)
class Meta:
model = AskSection
fields = ('title', 'description', 'is_pin')
widgets = {
'title': forms.TextInput(attrs={
'class': 'form-control'
}),
'description': forms.Textarea(attrs={
'class': 'form-control'
}),
}
views.py
#login_required
def create_ask_post(request):
if request.method == "POST":
form = AskSectionCreateForm(request.POST or None)
if form.is_valid():
title = form.cleaned_data.get('title')
description = form.cleaned_data.get('description')
is_pin = form.cleaned_data.get('is_pin')
obj = form.save(commit=False)
obj.title = title
obj.description = description
obj.is_pin = is_pin
obj.user = request.user
obj.save()
messages.success(request, f'You have posted {title} successfully')
return redirect('/details_ask/' + str(title) + '/')
else:
form = AskSectionCreateForm()
else:
form = AskSectionCreateForm()
context = {
'form': form
}
return render(request, "editor/create_ask_post.html", context)
html file
{% for question in all_questions %}
<!-- random HTML not important code --> <!-- code is related to just the styling of posts and just model fields -->
{% endfor %}
so please let me know how to do this. The HTML file isn't really important. It just contains the card, and model fields.
Thanks!
So you don't really need another field in your model you could just work with your DateTimeField but as commented before I would add a rank = models.Integerfield(default=0) to the AskSection model. (Don't forget to migrate)
you have a views.py file with a function where you set the context for your html (not the one you showed in your answer but the other one where you define all_questions). here you can set an order for all_questions like so all_questions = AskSection.objects.filter(user=request.user).order_by("rank", "-is_pin"). Your code might look a little different now I dont know if you filter by user I just assumed that...
When a User adds a new question you increase your the rank on the new question so you always have a clean order. Whenever a user "Pins" a question you take highest rank and add a number + set the Boolean to True.
An alternative way would be working with the Datefield date_posted like so
all_questions = AskSection.objects.filter(user=request.user).order_by("date_posted", "-is_pin"). in that case the date would act as a "rank". saves you a migration but its not as flexible as a Integerfield.

Django - pass a list of results when querying using filter

In my FollowingPageView function, I'm trying to filter posts based on the logged in user's list of user's he/she is following.
You'll see the Profile model has a "following" field that captures the names of users the Profile owner is following. What I'm trying to do in my view is capture these names of the users in "following" then pass them to Post.objects.filter(created_by=user_list), but I will only get the last user in that list in this case. How can I iterate over the "user_list" Queryset and pass that to Post.objects.filter in order to return the posts from each user in that list? In this case, I should have two users in the Queryset [<User: winter>, <User: daisy>].
models.py
class Profile(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
bio = models.TextField(null=True, blank=True)
website = models.CharField(max_length=225, null=True, blank=True)
follower = models.ManyToManyField(User, blank=True, related_name="followed_user") # user following this profile
following = models.ManyToManyField(User, blank=True, related_name="following_user") # profile user that follows this profile
def __str__(self):
return f"{self.user}'s' profile id is {self.id}"
def following_users(self):
for username in self.following:
return username
def get_absolute_url(self):
return reverse("network:profile-detail", args=[str(self.id)])
class Post(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
subject = models.CharField(max_length=50)
body = models.TextField(max_length=1000)
timestamp = models.DateTimeField(auto_now_add=True)
likes = models.ManyToManyField(User, blank=True, related_name="posts")
def __str__(self):
return f"{self.created_by} posted {self.body}"
views.py
# Following Users
def FollowingPageView(request, pk):
profile = get_object_or_404(Profile, id=pk)
user_list = []
for user in profile.following.all():
user_list.append(user)
posts = Post.objects.filter(created_by=user_list[0])
print(user_list)
paginator = Paginator(posts, 10)
page_number = request.GET.get("page")
page_obj = paginator.get_page(page_number)
try:
if request.method == "GET":
return render(request, "network/follow-posts.html", { "profile": profile, "page_obj": page_obj })
except ValueError:
return render(request, "network:index.html", {"error": ValueError})
One approach is to use an __in query. Here, because you're not using user_list for anything else, you'll probably get the best results from using an inner query:
posts = Post.objects.filter(created_by__in=profile.following.all())
But note the performance advice in the linked docs - test it on your actual setup and see.
Possibly with a distinct() call required, I can't remember exactly what triggers the possibility of duplicate records with many-to-many fields.
There are other ways to express it using field references, something like:
posts = Post.objects.filter(created_by__profile__followed_user=profile.user).distinct()
Backing databases tend to do that with a join rather than a subquery, so it can have different performance characteristics.

Django Twitter clone. How to restrict user from liking a tweet more than once?

I'm not sure where to start. Right now, the user can press like as many times they want and it'll just add up the total likes for that tweet.
models.py
class Howl(models.Model):
author = models.ForeignKey(User, null=True)
content = models.CharField(max_length=150)
published_date = models.DateTimeField(default=timezone.now)
like_count = models.IntegerField(default=0)
rehowl_count = models.IntegerField(default=0)
def get_absolute_url(self):
return reverse('howl:index')
def __str__(self):
return self.content
views.py
class HowlLike(UpdateView):
model = Howl
fields = []
def form_valid(self, form):
instance = form.save(commit=False)
instance.like_count += 1
instance.save()
return redirect('howl:index')
Django Twitter clone. How to restrict user from liking a tweet more than once?
As well as tracking how many Likes a post has, you'll probably also want to track who has "Liked" each post. You can solve both of these problems by creating a joining table Likes with a unique key on User and Howl.
The unique key will prevent any User from doing duplicate likes.
You can do this in Django with a ManyToManyField, note that since this means adding a second User relationship to Howl, we need to disambiguate the relationship by providing a related_name
Eg:
class Howl(models.Model):
author = models.ForeignKey(User, null=True, related_name='howls_authored')
liked_by = models.ManyToManyField(User, through='Like')
# ...rest of class as above
class Like(models.Model):
user = models.ForeignKey(User)
howl = models.ForeignKey(Howl)
class Meta:
unique_together = (('user', 'howl'))
like_count count then becomes redundant, since you can use Howl.liked_by.count() instead.
The other benefit of this is that it allows you to store information about the Like - eg when it was added.
An idea could be adding a column to your table named likers and before incrementing like_counts check if the models.likers contains the new liker or not. If not increment the likes, if yes don't.
Changed liked_count in my models.py to
liked_by = models.ManyToManyField(User, related_name="likes")
views.py
class HowlLike(UpdateView):
model = Howl
fields = []
def form_valid(self, form):
instance = form.save(commit=False)
instance.liked_by.add(self.request.user)
instance.like_count = instance.liked_by.count()
instance.save()
return redirect('howl:index')
index.html
{{howl.liked_by.count}}

Categories

Resources