using different serializers Django - django

hope someone can help me out.
I am trying to call different serializes depending on some factors, and one of the factor is if the field called "code" (which is inside MyCards table) is not null then call the rewardsSerializer.
the point is that I am not managing to get to get the value of code to see if it's None or not.
down here you can see how I have tried to get the value of "code" with no success
'''
class MyCardsViewSet(ModelViewSet):
serializer_class = MyCardsSerializer
queryset = MyCards.objects.all()
def get_serializer_class(self):
if self.request.GET.get('code') != None:
return RewardSerializer
if self.action == 'update':
return UpdateMyCardsSerializer
else:
return MyCardsSerializer
'''

class MyCardsViewSet(ModelViewSet):
queryset = MyCards.objects.all()
if you are passing the code as parameter, then only you can get the value using
self.request.GET.get('code')
if you are passing the code inside body, you have to access it using
self.request.data.get('code')
then you can rewrite the code as
def get_serializer_class(self):
if self.request.data.get('code') != None:
return RewardSerializer
if self.action == 'update':
return UpdateMyCardsSerializer
return MyCardsSerializer

Related

returning user instead of True or False in Django Permission class

I have a custom permission class which I am importing in one of the views. In that permission class, I am using return user instead of True or False. But it seems to be working. But I dont really understand why.
The class is as follows:
class ExamplePermission(BasePermission):
def has_permission(self, request, view):
user = CustomUser.objects.filter(user=request.user).first()
if view.action == 'list':
return user
elif request.method == 'POST' or 'PATCH' or view.action == 'destroy' or 'update':
return user and user.user_type == ADMIN
else:
return False
Here in above permission class , I am returning user in some places instead of boolean true or false. What is the meaning of returning user? How it is still working??
Yes, it will work. Check out https://docs.python.org/3/library/stdtypes.html#truth-value-testing. As long as user is not empty it will return True.
By default, an object is considered true unless its class defines
either a bool() method that returns False or a len() method
that returns zero, when called with the object.
So even though the object user is NOT a Boolean, it will be treated as such when doing any logical comparisons.
In fact this is a way one often checks to see if a queryset is empty:
if some_queryset:
print("There is at least one object in some_queryset")
Although, as the docs say, the exists() method is a bit faster, and better, if you don't need the actual queryset, and just want to know if the queryset is empty or not.
EDIT
To answer your question you posted in the comment, you must separate each condition you are testing in the if .. or: In other words, if you want to test if A equals B or C or D, the you cannot do:
if A == B or C or D
Instead you must do,
if A == B or A == C or A == D
Thus your code should look like this:
class ExamplePermission(BasePermission):
def has_permission(self, request, view):
user = CustomUser.objects.filter(user=request.user).first()
if view.action == 'list':
return user
elif request.method == 'POST' or request.method == 'PATCH' or view.action == 'destroy' or view.action == 'update':
return user and user.user_type == ADMIN
else:
return False

'SalaryAdmin' object has no attribute 'request'

Hello I want to add method to list display in django admin but am getting error say SalryAdmin has no attribute request
here my admin
#admin.register(Salary)
class SalaryAdmin(admin.ModelAdmin):
list_display = ['user_name', 'action']
def action(self, obj):
if not self.request.user.is_superuser:
if obj.hr_state == 'request-change-approved' and self.request.user.user_role.position.code == 'HRM':
return True
else:
return False
else:
return True
any help appreciated
You are getting error for obvious reason - ModelAdmin class does not have request member. Instead of this, you should mark method as action and request would be passed as an argument to the method - like so:
#admin.register(Salary)
class SalaryAdmin(admin.ModelAdmin):
list_display = ['user_name', 'action']
actions = ['action']
#admin.action(description='')
def action(self, request, queryset):
obj = queryset.first() # TODO - implement object retrieval logic here
if not request.user.is_superuser:
if obj.hr_state == 'request-change-approved' and request.user.user_role.position.code == 'HRM':
return True
else:
return False
else:
return True
Here is the source to the official docs.

Django: 'Q' object is not iterable

I have following APIView:
class SubmitFormAPIView(APIView):
def put(self, request, pk):
# some other codes
form = Form.objects.get(id=pk)
tu_filter, target_user = self._validate_target_user(request, form)
user_status, created = UserFormStatus.objects.get_or_create(
tu_filter,
form_id=pk,
user_id=request.user.pk
)
# Some other codes.
def _validate_target_user(request, form):
if some_conditions:
return Q(), None
else:
try:
target_user_id = int(request.GET.get('target_user_id))
except ValueError:
raise ValidationError()
target_user = get_user_model().objects.get(id=target_user_id)
return Q(target_user_id=target_user_id), target_user
but when django wants to execude get_or_create method, raises following error:
TypeError: 'Q' object is not iterable
Note: If _validate_target_user() returns Q(), None, no errors raised and view works fine. The error will be raised when return Q(target_user_id=target_user_id), target_user is returned.
I know, question information is not completed, just I want to know, what may cause this error?
From the source of get_or_create(...), def get_or_create(self, defaults=None, **kwargs):
which indicating that the get_or_create(...) doesn't not accept any args unlike the get() or filter(...) methods.
Since your are executing the function as below, Python thinks that the tu_filter is the value for default parameter, which is expected to be an iterable.
get_or_create(
tu_filter,
form_id=pk,
user_id=request.user.pk
)
Instead of returning a Q object, you can also just pass a dictionary of filters instead, like
{ 'target_user_id': target_user_id }
The you can run the get_or_create with **tu_filter as arguments, bypassing the need for Q.
class SubmitFormAPIView(APIView):
def put(self, request, pk):
# some other codes
form = Form.objects.get(id=pk)
tu_filter, target_user = self._validate_target_user(request, form)
user_status, created = UserFormStatus.objects.get_or_create(
**tu_filter,
form_id=pk,
user_id=request.user.pk
)
# Some other codes.
def _validate_target_user(request, form):
if some_conditions:
return {}, None
else:
try:
target_user_id = int(request.GET.get('target_user_id))
except ValueError:
raise ValidationError()
target_user = get_user_model().objects.get(id=target_user_id)
return { 'target_user_id': target_user_id }, target_user
Edit: As to what causes the error, my guess would be that using Q as part of your get_or_create statement is unclear to Django, because it doesn't know what to do with it in case the object needs to be created. A better approach would therefor be:
UserFormStats.objects.filter(tu_filter).get_or_create(form_id=pk, user_id=request.user.pk)

Django ClassView inheritance

I have the following views which are working fine:
class FriendView(View):
message_success = None
message_error = None
def get_pending_received(self):
return models.FriendRequest.objects.filter(recipient_user_id=self.request.user.id)
def get_existing_friends(self):
return models.Friendship.objects.filter(source_user_id=self.request.user.id)
def get_pending_sent(self):
return models.FriendRequest.objects.filter(sender_user_id=self.request.user.id)
def get(self, request):
return render(request, 'template/friends.html',
{'pending_received_requests': self.get_pending_received(),
'existing_friends': self.get_existing_friends(),
'pending_sent_requests': self.get_pending_sent(),
'message_success': self.message_success,
'message_error': self.message_error})
class DeleteFriendView(FriendView):
def get(self, request, friendship_id):
try:
friendship = models.Friendship.objects.get(id=friendship_id)
except models.Friendship.DoesNotExist:
raise Http404
if not ((friendship.source_user_id == request.user.id) or (friendship.dest_user_id == request.user.id)):
return HttpResponseForbidden("Forbidden")
friendship.delete()
message_success = "Friend has been deleted"
return render(request, 'template/friends.html',
{'pending_received_requests': self.get_pending_received(),
'existing_friends': self.get_existing_friends(),
'pending_sent_requests': self.get_pending_sent(),
'message_success': 'Friend has been Deleted'})
My question is, is there a way to put the logic in the DeleteFriendView that is execute before get() without overriding get()? This would be cleaner and would reduce duplicate code but after reading the ClassView documentation I can't seem to figure out the best way to do this, since I can't access self outside of a method in the class view?
I would imagine something like this:
class DeleteFriendView(FriendView):
def before_get(self):
try:
friendship = models.Friendship.objects.get(id=friendship_id)
except models.Friendship.DoesNotExist:
raise Http404
if not ((friendship.source_user_id == request.user.id) or (friendship.dest_user_id == request.user.id)):
return HttpResponseForbidden("Forbidden")
friendship.delete()
self.message_success = "Friend has been deleted"
This way the get() method could be reused.
Thanks,
Mark
Why don't you want to override get()? You can put your logic there, then simply call the superclass method.
class DeleteFriendView(FriendView):
def get(self, request):
# delete-specific logic here
# now call FriendView get()
return super(DeleteFriendView, self).get(request)
Note by the way that it's very poor practice to put data-modifying actions in a GET request. They're far too easy to trigger by accident, or via malicious links. You should always put things like deletion in a POST request.

django: Passing arguments to a custom view class (inheritance edition)

again, apologies for what is probably a straightforward question!
Ok, so!
my problem is i have a saveModel function, where it saves a model. If the model is all good (is_valid), it will save the model and redirect to pageA
if the model is bad, or the request is a GET, then i'd like to redirect to pageB
all well and good, but i do this several times, how annoying! I don't want to cut and paste all the time, so i came up with this:
class SaveModel(View):
def as_view(self):
if request.method == "POST":
form = SaveModel.getPostForm(self.request)
if form.is_valid():
processedForm = SaveModel.processForm(self.request)
processedForm.save()
if (self.success_template):
return render_to_response(self.success_template)
else:
return render_to_response('pageA.html')
else:
form = SaveModel.getForm()
if (self.context_object_name):
contextName = context_object_name
else:
contextName = 'form'
if (self.template_name):
return render_to_response(template_name,{contextName:form})
else :
return render_to_response('pageB.html',{contextName:form})
def getForm(self):
return None
def getPostForm(self,request):
return None
def processForm(self,form,request):
return None
THEN, i define other classes to handle particular models, like, for example, so:
class StoryModelView(SaveModel):
def getForm(self,request):
return StoryForm()
def getPostForm(self,request):
return StoryForm(request.POST)
def processForm(self,form,request):
theStory = form.save(commit=False)
theStory.user = request.user
return theStory
and then, finally, in my urls.py i will refer to (as above) the model to use like so:
url(r'^addStory/$',
StoryModelView.as_view(
context_object_name='form',
template_name='accounts/addStory.html',
success_template='accounts/addStorySuccess.html'
)
),
This doesn't seem to work though - pycharm assures me that my references to self.context_object_name and so on are invalid. I'm v. new to python and django (which is why i thought i'd build a website with them! clever andrew!), so i am sure that i've missed a whole bunch of things (abstract methods and stuff... python does that, right?)
what do i need to do to get this all working? Is this how i should be doing things?
ANSWER BY ME!
Ok, so the comments everyone has written about the CreateView are probably correct. "Probably" because i never ended up using it, because i ended up sticking with my code instead.
In case anybody is, like me, new to python and django and wants to see how the whole thing works, here we are!
class SaveModel(View):
success_template = None
context_object_name = None
template_name = None
def post(self, request):
form = self.getPostForm(self.request)
if form.is_valid():
processedForm = self.processForm(form,self.request)
processedForm.save()
if self.success_template:
return render_to_response(self.success_template)
else:
return render_to_response('accounts/addStorySuccess.html')
else:
self.renderValidations(form)
def get(self,request):
form = self.getForm()
self.renderValidations(form)
def renderValidations(self,form):
if self.context_object_name:
contextName = self.context_object_name
else:
contextName = 'form'
if self.template_name:
return render_to_response(self.template_name,{contextName:form})
else :
return render_to_response('accounts/addStory.html',{contextName:form})
def getForm(self):
return None
def getPostForm(self,request):
return None
def processForm(self,form,request):
return None
and that is the main class, then i can override it like so:
class StoryModelView(SaveModel):
def getForm(self):
return StoryForm()
def getPostForm(self,request):
return StoryForm(request.POST)
def processForm(self,form,request):
theStory = form.save(commit=False)
theStory.user = request.user
return theStory
i tripped myself up with how "self" works in python a few times. it seems to be magically sent across with all method calls, but you need it as the first arg in the method declaration (but you never need to use it when calling/using the method)
i think there's only post or get for methods when overriding the View class. i don't have a good idea of the "process" of the call, or what the order is, dispatch was mentioned as something to override, but i suspect that is only where i need to change when/how to deal with differing request types (GET, POST, HEAD etc)
oh! the urls.py!
url(r'^addStory/$',
StoryModelView.as_view(
context_object_name='form',
template_name = 'accounts/addStory.html',
success_template= 'accounts/addStorySuccess.html'
)
),
i can just chuck whatever i want into that "as_view" call, and then, as long as those parameters are defined in the overriding class it's all good.
so yay! my classes all work and women want me. use my code, and this can happen to you too!*
*results atypical and fictional. your results may differ.