i am working on a website where users can send friend request, accept request, cancel request. I have an error AttributeError object has no attribute 'delete'. This happens when i cancel friend request. This my code i tried:
Model:
class FriendRequest(models.Model):
to_user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, related_name='to_user')
from_user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, related_name='from_user')
date = models.DateTimeField(auto_now_add=True, null= True)
class Meta:
verbose_name = 'Friend request'
verbose_name_plural = 'Friend requests'
ordering = ['-date']
def __str__(self):
return "from {}, to {}".format(self.from_user.username, self.to_user.username)
Views.py:
#login_required
def cancel_friend_request_view(request, id):
user = get_object_or_404(User, id=id)
frequest = FriendRequest.objects.filter(from_user=request.user, to_user=user).first()
frequest.delete()
print(frequest)
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
I solved it like this(I do not know if it is the best way, but it worked), removing .first():
#login_required
def cancel_friend_request_view(request, id):
user = get_object_or_404(User, id=id)
frequest = FriendRequest.objects.filter(from_user=request.user, to_user=user)
frequest.delete()
print(frequest)
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
It may be better to pass through the FriendRequest's ID instead, to entirely skip this section of code:
user = get_object_or_404(User, id=id)
frequest = FriendRequest.objects.filter(from_user=request.user, to_user=user).first()
You could rather do this:
friend_request = get_object_or_404(FriendRequest, id=id)
friend_request.delete()
Also consider not deleting data entirely from your database, and rather making use of deleted flags in order to keep a behavioural history throughout your software.
.first() will return None if no matching row is found.
https://docs.djangoproject.com/en/3.0/ref/models/querysets/#first
Note that first() is a convenience method, the following code sample is equivalent to the above example:
try:
p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
p = None
[edit] you need to handle the None-case, which should have been clear from the above explanation. Also, I recommend to learn using the python debugger to inspect types of variables instead of relying on debug-prints.
#login_required
def cancel_friend_request_view(request, id):
user = get_object_or_404(User, id=id)
frequest = FriendRequest.objects.filter(from_user=request.user, to_user=user)
if frequest is None:
raise Http404()
frequest.delete()
Related
Good day, I have a Model object, which contains the user who created it, as well as the one specified in a form.
with localhost:800/delete/int:title_id/ the one object is deleted.
Question:
How can I make sure that user a user who also created the object can delete it.
If userXY owns the object with ID 123 and he calls localhost:800/delete/123, the object will be deleted.
But if OtherNutzerYX calls localhost:800/delete/123, the object will not be deleted because the object does not belong to him, but to UserXY.
models.py
class NewTitle(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
default=None,
null=True,
on_delete=models.CASCADE,
)
title = models.CharField(max_length=200)
creator_adress = models.GenericIPAddressField(null=True)
id = models.BigAutoField(primary_key=True)
def __str__(self):
return str(self.title)
urls.py
path('delete/<int:title_id>/', views.title_delete),
views.py
def title_view(request):
titles = NewTitle.objects.all()
custom_title_id = random.randint(1111, 1111111111111)
if request.method == 'POST':
form = NewTitleForm(request.POST, instance=NewTitle(user=request.user))
if form.is_valid():
obj = form.save(commit=False)
obj.creator_adress = get_client_ip(request)
obj.id = custom_title_id
while NewTitle.objects.filter(id=obj.id).exists():
obj.id = random.randint(111, 11111111111)
obj.save()
return redirect('/another')
else:
form = NewTitleForm()
return render(request, 'test.html', {'form': form, 'titles': titles})
def title_delete(request, title_id):
if #WHAT CODE HERE?:
NewTitle.objects.filter(id=title_id).delete()
else:
return redirect('https://example.com')
return HttpResponseRedirect('/another')
The relevant code is the title_delete function.
I don't know what to write in the if statement. It has something to be like: 'if user of the title id == the user who is requesting the url == delete the model' 'else = if the user is not the owner, go to example.com and do not delte the model'
We can get the user who requested the url with request.user now we just need to check if the request.user is equal to the owner of the model. How?
(By the way, if there are better ways to create a custom ID for each model or you notice something else about my code that could be better, please tell me)
Thanks for your help :-)
As you have mentioned you only want to delete the title object if the object is created by a currently logged-in user.
Here is how you can achieve it
def title_delete(request, title_id):
user_title = NewTitle.objects.filter(id=title_id,
user=request.user)
if user_title:
user_title.delete()
else:
return redirect('https://example.com')
return HttpResponseRedirect('/another')
you can also call .first() after filter if you are sure that your user base only one title
user_title = NewTitle.objects.filter(id=title_id,
user=request.user).first()
I'm a beginner and I'm trying to create a small network project in which users can follow each other. I have implemented the follow button right, so it updates my models and displays proper info to users, but I can't get unfollow to work properly. I'm guessing it's something to do with the way I implemented follow model (with many to many field), but I'd like to implement it this way for practice... Anyhow, here's the code:
Models:
class User(AbstractUser):
pass
class Follow(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_follow")
following = models.ManyToManyField(User, blank=True, related_name="followers")
And view:
def users(request, username):
"""Displaying user profiles"""
if request.method == "POST":
user = request.user
profile = User.objects.get(username=username)
follow = Follow(user=user)
follow.save()
if "unfollow" in request.POST:
profile.followers.remove(user)
follow.following.remove(profile)
return HttpResponseRedirect(reverse('users', args=(username,)))
elif "follow" in request.POST:
follow.following.add(profile)
return HttpResponseRedirect(reverse('users', args=(username,)))
This code yields in: "ValueError at /users/test
Cannot query "admin": Must be "Follow" instance." at the profile.followers.remove(user) line...
Playing with it in shell I found out (at least I think so) that the line under it (follow.following.remove(profile) - which by the way was there before I tried with the profile.followers.remove(user)) removes the profile from Follow model, but for some reason it is not by itself updated in the Users model (for followers) ???
from django.db import models
# Create your models here.
class User(models.Model):
name = models.CharField(max_length=40)
pwd = models.CharField(max_length=40)
def __str__(self):
return self.name
class Follow(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
another_user = models.ManyToManyField(User, related_name='another_user')
def __str__(self):
return self.user.name
============================================================================
views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from .models import User, Follow
# Create your views here.
def index(request):
if 'user' in request.session:
return render(request, 'index.html')
else:
return redirect('login')
def profile(request, user_name):
user_obj = User.objects.get(name=user_name)
session_user = User.objects.get(name=request.session['user'])
session_following, create = Followers.objects.get_or_create(user=session_user)
following, create = Followers.objects.get_or_create(user=session_user.id)
check_user_followers = Followers.objects.filter(another_user=user_obj)
is_followed = False
if session_following.another_user.filter(name=user_name).exists() or following.another_user.filter(name=user_name).exists():
is_followed=True
else:
is_followed=False
param = {'user_obj': user_obj,'followers':check_user_followers, 'following': following,'is_followed':is_followed}
if 'user' in request.session:
return render(request, 'profile.html', param)
else:
return redirect('index')
def follow_user(request, user_name):
other_user = User.objects.get(name=user_name)
session_user = request.session['user']
get_user = User.objects.get(name=session_user)
check_follower = Followers.objects.get(user=get_user.id)
is_followed = False
if other_user.name != session_user:
if check_follower.another_user.filter(name=other_user).exists():
add_usr = Followers.objects.get(user=get_user)
add_usr.another_user.remove(other_user)
is_followed = False
return redirect(f'/profile/{session_user}')
else:
add_usr = Followers.objects.get(user=get_user)
add_usr.another_user.add(other_user)
is_followed = True
return redirect(f'/profile/{session_user}')
return redirect(f'/profile/{session_user}')
else:
return redirect(f'/profile/{session_user}')
=============================================================================
User This For Reference...Follow And Unfollw Logic
This is probably very simple and basic but I'm struggling with grabbing a newly-created object in Django. It is for a basic library-style app. Over in models, I do this to create a Book object:
def add_book(self, postData, user_id):
title = postData['title']
first_name = postData['first_name']
last_name = postData['last_name']
user_obj = User.objects.get(id=user_id)
if not Author.objects.filter(first_name=first_name, last_name=last_name).exists():
author_obj = Author.objects.create(first_name=first_name, last_name=last_name)
else:
author_obj = Author.objects.get(first_name=first_name, last_name=last_name)
return self.create(title=postData['title'], created_by=user_obj, author=author_obj)
Then in views, I call that method and wish to redirect to a page specifically for that newly-created object. I think you can see that I have most of the code down, but don't know what to put in place of the "????".
def books_add(request):
if request.method == "POST":
errors = Book.objects.book_validation(request.POST)
if not errors:
Book.objects.add_book(request.POST, request.session['uid'])
book_id = Book.objects.get(????).id
return redirect('/books/book/{}/'.format(book_id))
else:
context = {
'errors' : errors,
}
1st part use get_or_create for retrieve or create a model entry
def add_book(self, postData, user_id):
title = postData['title']
first_name = postData['first_name']
last_name = postData['last_name']
user_obj = User.objects.get(id=user_id)
author_obj, created = Author.objects.get_or_create(first_name=first_name, last_name=last_name)
return self.create(title=postData['title'], created_by=user_obj, author=author_obj)
2nd part, return self.create return a Book entity :
def books_add(request):
if request.method == "POST":
errors = Book.objects.book_validation(request.POST)
if not errors:
book = Book.objects.add_book(request.POST, request.session['uid'])
return redirect('/books/book/{}/'.format(book.id))
else:
context = {
'errors' : errors,
}
There are some issues here. At the very least, look at Django Forms before you go much further. This is what a view that creates an object could look like:
def add_book(request):
if request.POST:
author, created = Author.objects.get_or_create(first_name=first_name,
last_name=last_name)
book = Book(title = request.POST['title'],
user_obj = request.GET['user'],
author = author,)
book.save()
return redirect('/books/book/{}/'.format(book.id))
else:
return render(request, 'book_form.html')
You really need to look into ModelForms to handle your POSTs. But start with looking at Forms.
I'm trying to create a django app in which one user can add other user as Friend. Here's what I did,
models.py,
class Friend(models.Model):
users = models.ManyToManyField(User)
current_user = models.ForeignKey(User, related_name='all', null=True)
views.py
# view for adding or removing friends
def change_friends(request, pk):
new_friend = User.objects.get(pk=pk)
data = Friend.objects.get(current_user=request.user)
frnds = data.users.all()
new_friend in frnds:
data.users.remove(new_friend)
else:
data.users.add(new_friend)
redirect(request.META['HTTP_REFERER'])
# Displaying frinends,
def following(request, id=None):
my_friend, created = Friend.objects.get_or_create(current_user_id=id)
all_friends = my_friend.users.all()
return render(request, 'all/follow.html', {'all_friends': all_friends})
This code was working fine until I added friends from 1 account only, but when I added several friends from several accounts it started showing an error get() returned more than one Friend -- it returned 2!.
How can we fix that? Thank You!
in change friend function this line of code change like this
new_friend = User.objects.filter(pk=pk).first()
Try this...
Delete all Friend instances in admin and change model to:
class Friend(models.Model):
users = models.ManyToManyField(User)
current_user = models.OneToOne(User, related_name='friend', on_delete=models.CASCADE, null=True)
then views should be:
# view for adding or removing friends
def change_friends(request, pk):
new_friend = User.objects.get(pk=pk)
friends = request.user.friend.users.all()
new_friend in friends:
request.user.users.remove(new_friend)
else:
request.user.users.add(new_friend)
redirect(request.META['HTTP_REFERER'])
# Displaying frinends,
def following(request, id=None):
my_friend, created = Friend.objects.get_or_create(current_user_id=id)
all_friends = my_friend.users.all()
return render(request, 'all/follow.html', {'all_friends': all_friends})
If you use many_to_many to record friends relationship,it should be better set model as:
current(OneToOne) users(ManyToMany)
If you use ForeignKey to record friends relationship,it should be better set model as:
current(ForeignKey) user(ForeignKey)
Update
If you can't change models anymore,just change code to:
def change_friends(request, pk):
new_friend = User.objects.get(pk=pk)
data = Friend.objects.filter(current_user=request.user)
has_user = False
for x in data:
if new_friend in x.users.all():
has_user = True
x.users.remove(new_friend)
if not has_user:
firend = Friend.objects.filter(current_user=request.user).first()
if friend:
friend.users.add(new_friend)
else:
friend = Friend.objects.create(current_user=request.user)
friend.users.add(new_friend)
redirect(request.META['HTTP_REFERER'])
I am passing an (is_followed) parameter from one class based view FollowToggleAPIView to another UserDetailAPIVIew. I do this using Django session (from reading other thread on this platform) in the hope of displaying the follow-status (True or False) of the user_to_toggle variable on the UserSingleProfileSerializer.
Here are my views:
class UserDetailAPIVIew(generics.RetrieveAPIView):
'''
Displays a list of a user's posts
'''
serializer_class = UserSingleProfileSerializer
queryset = User.objects.all()
def get_object(self):
self.object = get_object_or_404(User,
username__iexact=self.kwargs.get('username')
)
return self.object
def get_serializer_context(self):
'''
passing the extra is_following argument to the UserDetailAPIVIew
'''
context = super(UserDetailAPIVIew, self).get_serializer_context()
is_followed = self.request.session.get('followed')
context.update({'followed': is_followed})
return context
class FollowToggleAPIView(APIView):
'''
Uses the custom model manager for user toggle follow
'''
def get(self, request, username, format=None):
user_to_toggle = get_object_or_404(User, username__iexact=username)
me = request.user
message = 'Not allowed'
if request.user.is_authenticated():
is_followed = UserProfile.objects.toggle_follow(me, user_to_toggle)
request.session['followed'] = is_followed
return Response({'followed': is_followed})
return Response({'message': message}, status=400)
The toggle_follow method is defined in the custom model manager as follows:
class UserProfileManager(models.Manager):
def toggle_follow(self, user, to_toggle_user):
''' follow unfollow users '''
user_profile, created = UserProfile.objects.get_or_create(user=user)
if to_toggle_user in user_profile.following.all():
user_profile.following.remove(to_toggle_user)
added = False
else:
user_profile.following.add(to_toggle_user)
added = True
return added
class UserProfile(models.Model):
'''
Extends the Django User model
'''
user = models.OneToOneField(settings.AUTH_USER_MODEL,
related_name='profile')
following = models.ManyToManyField(settings.AUTH_USER_MODEL,
blank=True,
related_name='followed_by')
objects = UserProfileManager()
def get_absolute_url(self):
return reverse_lazy('profiles:detail',
kwargs={'username':self.user.username})
def __str__(self):
return 'Username: {} [ Followers ({});
Following({})]'.format(self.user.username,
self.user.followed_by.all().count(),
self.following.all().count())
The urls.py:
urlpatterns = [
url(r'^(?P<username>[\w.#+-]+)/$', UserDetailAPIVIew.as_view(),
name='user-posts-api'),
url(r'^(?P<username>[\w.#+-]+)/follow/$',
FollowToggleAPIView.as_view(), name='follow-api'),
]
The only problem is that the value of (is_followed) displayed in UserSingleProfileSerializer is set for all user instances at once (not for the specific user we want to follow).
I am certainly not following/unfollowing all users at the same time (since the FollowToggleAPIView targets a specific user by his username).
I want to know how can I transfer the value of (is_followed) only to the specific user (user_to_toggle) in the UserDetailAPIVIew. Thank you in advance.
The session is completely the wrong thing to use here. You're storing a single "followed" value which only records the last user they toggled and has no relation to the profile they're actually viewing.
Instead of doing this, you should simply query in the UserDetailAPIVIew the followed status of the specific user.
def get_serializer_context(self):
context = super(UserDetailAPIVIew, self).get_serializer_context()
is_followed = self.request.user.profile.following.filter(username=self.object).exists()
context.update({'followed': is_followed})
return context
Note also, your toggle method is itself very inefficient - there's no need to retrieve every follow from the database just to check whether the current user is among them. Use exists again:
user_profile, created = UserProfile.objects.get_or_create(user=user)
if user_profile.following.filter(username=to_toggle_user).exists():