I want to create functionality where it's clear whether the user liked every fetched post in template or not(so that I just check attribute post.liked which is True or False in for construction)
I suggested that method in models.py might help:
class Post(models.Model):
who_liked = models.ForeignKey('UserProfile', related_name='who_liked_QUESTION', blank=True, null=True)
def _is_it_liked(self, theuser):
if self.who_liked == theuser:
return True
else:
return False
liked_bool = property(_is_it_liked)
But I do not fully understand how should I fetch it then in template, considering that it's a function and I need to insert request.user as argument to the function and call it. Could you please explain how it works since documentation didn't help me much?
Related
I am trying to make an api backend of something like reddit. I want to ensure that whoever is creating a post (model Post) within a particular subreddit is a member of that subreddit (subreddit model is Sub). Here is my latest effort, which works but seems pretty sloppy, and the serializer for some context.
Post permissions.py
class IsMemberOfSubOrReadOnly(BasePermission):
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
elif request.data:
# prevent creation unless user is member of the sub
post_sub_pk = get_pk_from_link(request.data['sub'])
user = request.user
user_sub_pks = [sub.pk for sub in user.subs.all()]
if not (post_sub_pk in user_sub_pks):
return False
return True
Post serializers.py
from .models import Post
from redditors.models import User
from subs.models import Sub
class PostSerializer(serializers.HyperlinkedModelSerializer):
poster = serializers.HyperlinkedRelatedField(
view_name='user-detail',
#queryset=User.objects.all(),
read_only=True
)
sub = serializers.HyperlinkedRelatedField(
view_name='sub-detail',
queryset=Sub.objects.all()
)
class Meta:
model = Post
fields = ('url', 'id', 'created', 'updated', 'title', 'body',
'upvotes', 'sub', 'poster')
The issue with this approach is that since 'sub' is a hyperlinkedRelatedField on the Post serializer what I get back from request.data['sub'] is just the string hyperlink url. I then have a function, get_pk_from_link that uses regex to read the pk off the end of the url. Then I can use that to grab the actual model I want and check things. It would be nice if there were a more direct way to access the Sub model that is involved in the request.
I have tried searching the fields of the arguments that are available and I can't find a way to get to the Sub object directly. Is there a way to access the Sub model object through the hyperlink url?
I have also solved this problem by just using a serializer field validator (not shown above), but I am interested to know how to do it this way too. Maybe this is just a bad idea and if so please let me know why.
You are right, parsing the url is not the way to go. Since you want to perform the permission check before creating a Post object, I suspect you cannot use object level permissions either, because DRF does not call get_object in a CreateAPIView (since the object does not exist in the database yet).
Considering this is a "business logic" check, a simpler approach would be to not have that permission class at all and perform the check in your perform_create hook in your view (I had asked a similar question about this earlier):
from rest_framework.exceptions import PermissionDenied
# assuming you have a view class like this one for creating Post objects
class PostList(generics.CreateApiView):
# ... other view stuff
def perform_create(self, serializer):
sub = serializer.get('sub') # serializer is already validated so the sub object exists
if not self.request.user.subs.filter(pk=sub.pk).exists():
raise PermissionDenied(detail='Sorry, you are not a member of this sub.')
serializer.save()
This saves you hassle of having to perform that url parsing as the serializer should give you the Sub object directly.
So I'm writing my first project with DRF and I'm having some issues with setting up permissions for my viewsets. I already have authentication working with djangorestframework-jwt. Currently, I have a few different ViewSets defined. What I would like to do is allow the owner of a model object to make any changes they would like to that object, but prevent everyone else (aside admins) from even viewing the objects. Basically, I need a way of applying permission classes to specific methods to allow only admins to view 'list', owners to 'update, destroy, etc' and authenticated users to 'create'. Currently I have something like this:
class LinkViewSet(viewsets.ModelViewSet):
queryset = Link.objects.all()
serializer_class = LinkSerializer
with a model of
class Link(models.Model):
name = models.CharField(max_length=200)
url = models.URLField()
# another model with a OneToMany relationship
section = models.ForeignKey('homepage.LinkSection', related_name='links', on_delete=models.CASCADE
owner = models.ForeignKey('homepage.UserProfile'), related_name='links', on_delete=models.CASCADE)
and the permissions class I want to apply
class IsOwner(permissions.BasePermission):
def has_object_permissions(self, request, view, obj):
return obj.owner == request.user.userprofile
I'm sure it's possible to achieve this by writing completely custom views but I have a gut feeling that there is an easier way to do this especially since this is basically the last thing I have to do to finish the API. Thanks for any help and let me know if you need any more info.
I was able to create a permission class by checking which action was used in the view as follows here:
class IsOwner(permissions.BasePermission):
'''
Custom permission to only give the owner of the object access
'''
message = 'You must be the owner of this object'
def has_permission(self, request, view):
if view.action == 'list' and not request.user.is_staff:
print('has_permission false')
return False
else:
print('has_permission true')
return True
def has_object_permission(self, request, view, obj):
print('enter has_object_permission')
# only allow the owner to make changes
user = self.get_user_for_obj(obj)
print(f'user: {user.username}')
if request.user.is_staff:
print('has_object_permission true: staff')
return True
elif view.action == 'create':
print('has_object_permission true: create')
return True
elif user == request.user:
print('has_object_permission true: owner')
return True # in practice, an editor will have a profile
else:
print('has_object_permission false')
return False
def get_user_for_obj(self, obj):
model = type(obj)
if model is models.UserProfile:
return obj.user
else:
return obj.owner.user
get_user_for_obj is specifically for my implementation as a helper method since my model is inconsistent in how to obtain a user instance. You don't want to make has_permission too restrictive because has_object_permission will only run if has_permission returns True or if the method is not overridden.
so i currently have my likes app which deals with friend requests, and it works fine however my notification dont seem to be working. Whenever some likes someone else regardless of weather they are liked by that user or not it only sends the second of the two notify.send. I presume its an issue with the line "user = get_object_or_404(User, username=user.username)", however i dont know how to get round it. Here is my code:
def like_user(request, id):
pending_like = get_object_or_404(User, id=id)
user_like, created = UserLike.objects.get_or_create(user=request.user)
user = get_object_or_404(User, username=user.username)
liked_user, like_user_created = UserLike.objects.get_or_create(user=user)
if pending_like in user_like.liked_users.all():
user_like.liked_users.remove(pending_like)
elif request.user in liked_user.liked_users.all():
user_like.liked_users.add(pending_like)
notify.send(request.user,
#action=request.user.profile,
target=request.user.profile,
recipient=pending_like,
verb='sent you a friend request view'),
else:
user_like.liked_users.add(pending_like)
notify.send(request.user,
#action=request.user.profile,
target=request.user.profile,
recipient=pending_like,
verb='accepted your friend request view')
return redirect("profile", username=pending_like.username)
Here is an example of where the line " if request.user in liked_user.liked_users.all():" works fine i presume because the line " user = get_object_or_404(User, username=username)" has username in it.
#login_required
def profile_view(request, username):
user = get_object_or_404(User, username=username)
liked_user, like_user_created = UserLike.objects.get_or_create(user=user)
do_they_like = False
if request.user in liked_user.liked_users.all():
do_they_like = True
However in my first bit of code I'm trying to use user.username instead of username=username but i get the error "local variable 'user' referenced before assignment". What is the best way round this? am i do it completely wrong? should i try and pass in username, because when i do i get the error "like_user() takes exactly 3 arguments (2 given)". Sorry quite new to django, any help would be massively appreciated!
Here is my likes app model incase it helps:
class UserLike(models.Model):
user = models.OneToOneField(User, related_name='liker')
liked_users = models.ManyToManyField(User, related_name='liked_users', blank=True)
objects = UserLikeManager()
def __unicode__(self):
return self.user.username
def get_mutual_like(self, user_b):
i_like = False
you_like = False
if user_b in self.liked_users.all():
i_like = True
liked_user, created = UserLike.objects.get_or_create(user=user_b)
if self.user in liked_user.liked_users.all():
you_like = True
if you_like and i_like:
return True
else:
return False
really sorry for the long post, but im very stuck as most of this was written by some more advanced than me and im left with the issue of fixing it, any help would be massively appreciated!
thanks
You are correct, the issue is with the line:
user = get_object_or_404(User, username=user.username)
You're trying to get the attribute 'username' of the 'user' object, but 'user' is not defined within the scope of your function yet. In other words, at that point 'user' doesn't exist yet.
I don't have nearly enough info to start helping you, but if it was me debugging that, I'd consider the following:
You might not need that problematic line at all. It seems like request.user holds the reference to a user (The one who's liking stuff??), and pending_like seems to contain a reference to the recipient of the like. That's probably enough users to establish a 'like' relationship
Maybe do a print of the attributes of pending_like print(pending_like.__dict__) and request.user print(request.user.__dict__) to see if they contain all the info you need?
Now heads up! I am fresh noob off the NOOB-BUS from NOOBSVILLE!
So i am workin on a form to load up information and edit that form information and im in a headache. so i am using:
Django: 1.8
Pyhton: 3.5.1
backend is sqlite
I am using a form.ModelForm to load information into but when it comes to saving this is where i am stuck. the documentation is very confusing should i use all or just one clean.
this is the forms.py
class EditContact(forms.ModelForm):
class Meta:
model = Contact
#the list of all fields
exclude = ['date_modified']
def clean(self):
if self.date_of_entry is None:
print("looking to see what works")
self.date_of_entry = datetime.date.today()
return
def clean_ContactID(self):
#see this line below this comment i dunno what it does
ContactID= self.cleaned_data.get('ContactID')
print ("cleaning it")
# i also dont know what validation code suppose to look like
# i cant find any working examples of how to clean data
return ContactID
now there are mainly more def clean_methods but i think what i want to use is clean which should use all but in my view.
this is in view.py
def saveContactInfo (request):
#this part i get
if request.user.is_authenticated():
ContactID= request.POST['ContactID']
a = ListofContacts.objects.get(ContactID=ContactID)
f = EditContact(request.POST,instance=a)
print("plz work!")
if f.is_valid():
f.save()
return render (request,"Contactmanager/editContact.html", {'contactID': contactID})
else:
return HttpResponse("something isnt savin")
else:
return HttpResponse("Hello, you shouldnt ")
and this is model.py
def clean(self):
if self.ConactID is None:
raise ValidationError(_('ContactID cant be NULL!'))
if self.date_of_entry is None:
print("think it might call here first?")
self.date_of_entry = datetime.date.today()
print ( self.date_of_entry )
if self.modified_by is not None:
self.modified_by="darnellefornow"
print(self.modified_by )
if self.entered_by is not None:
self.entered_by = "darnellefornow"
print(self.entered_by )
ContactID = self.cleaned_data.get('ContactID')
return
now above the model has the fields and the types which all have blank = true and null = true except for the excluded field date_of_entry
and ive gotten to find out that when calling is_valid() in views it calls the models.clean() but it fails to save!!! and i dont know why! i dont know how to do the validation. i would like to know the process and what is required and even an example of form validation a field.
I think you're wanting info/answers on a couple of things here, looking at your code comments. Hopefully this helps:
1) You only need to use the clean_FIELDNAME functions if you need to handle something custom specifically for that field. The Django docs show this as an example:
def clean_recipients(self):
data = self.cleaned_data['recipients']
if "fred#example.com" not in data:
raise forms.ValidationError("You have forgotten about Fred!")
# Always return the cleaned data, whether you have changed it or
# not.
return data
So in that block, they are checking to see if the email list provided contains a particular email.
2) That also shows another question you asked in your comments about how to handle the validation. You'll see in that snippet above, you could raise a forms.ValidationError. This is discussed more here: https://docs.djangoproject.com/en/1.10/ref/forms/validation/
So, if an error is raised in any of those clean_ methods or in the main clean method, the form.is_valid() will be false.
Does that help?
I have a model form:
class SnippetForm(ModelForm):
class Meta:
model = Snippet
exclude = ['author', 'slug']
and I want to be able to edit a particular instance by using this:
def edit_snippet(request, snippet_id):
#look up for that snippet
snippet = get_object_or_404(Snippet, pk=snippet_id)
if request.user.id != snippet.author.id:
return HttpResponseForbidden()
if request.method == 'POST':
form = SnippetForm(data=request.POST, instance=snippet)
if form.is_valid():
form.save()
return HttpResponseRedirect(snippet.get_absolute_url())
else:
form = SnippetForm(instance=snippet)
return render_to_response(SNIPPET_EDIT_TEMPLATE,
{'form':form, 'add':False, 'user':request.user},
RequestContext(request))
Notice that at the line
form = SnippetForm(data=request.POST, instance=snippet)
, I created a form that use the data supplied from the user, and bound it with the instance found using the primary key (received from the url). According to django documentation, when I call save() the existing instance should be updated with POSTED data. Instead, what I see is a new object is created and saved into the database. What went wrong? Thanks a lot.
[Edit] This is really embarrassed. The code indeed has nothing wrong with it. The only thing that messed up the whole thing was the action I put in the template (as I use a same template for add and edit a snippet)....Thanks a lot for your help, really appreciate that.
I don't see why it would happen. What version of django is it?
In any case, you can manually force update passing the corresponding argument.
form = SnippetForm(data=request.POST, instance=snippet, force_update=True)