REQUEST certain Elements in Django POST API from User - django

I want to Request a dictionary like
{
"username": "a",
"password": "b",
"year": "2019-20",
"start": 1,
"end": 2,
"reference_id": "two"
}
from a user so that a user can hit the API and get the desired result.
My view looks like
def post(self,request, *args, **kwargs):
# self.http_method_names.append("post")
user = request.POST.get("user")
pasd = request.POST.get("pasd")
year = request.POST.get("year")
start = request.POST.get("start")
end = request.POST.get("end")
reference_id = request.POST.get("reference_id")
#Passing the parameters to another function to get the result.
status = main_function(user=user,pasd=pasd,year=year,start=start,end=end,reference_id=reference_id)
return Response(status)
Now the problem when I'm posting something in Django like
I'm getting None in every field. Ideally, I should get the values passed in the Dictionary.
Can someone please help me here.

In django rest framework, you should use request.data instead of request.POST, as it requires parser to receive JSON data. More info can be found in drf docs.
Change your code to:
def post(self,request, *args, **kwargs):
# self.http_method_names.append("post")
user = request.data.get("username") # <-- this used incorrect key
pasd = request.data.get("password") # <-- this used incorrect key
year = request.data.get("year")
start = request.data.get("start")
end = request.data.get("end")
reference_id = request.data.get("reference_id")
#Passing the parameters to another function to get the result.
status = main_function(user=user,pasd=pasd,year=year,start=start,end=end,reference_id=reference_id)
return Response(status)

I Got The Answer. SO if You Want to take input from a user then You should write this in your views.py file.
But First you have to tell Django Which Fields input you want. So you have to create a serializer.py file in the Django APP.
from rest_framework import serializers
class userdetailSerializer(serializers.Serializer):
username = serializers.CharField(max_length=20)
password = serializers.CharField(max_length=20)
year = serializers.CharField(max_length=8)
start = serializers.IntegerField()
end = serializers.IntegerField()
reference_id = serializers.CharField(max_length=50)
I need all the Above values from the User that's why i have added all. If you want Only certain values you can delete it from your serializer file.
After this goto your Views.py file and get the response entered by user using below code.
Serializer will autovalidate the input and if the input is not correct it will give a dictionary of error(In else Statement)
def post(self,request, *args, **kwargs):
# self.http_method_names.append("post")
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
username = serializer.validated_data.get('username')
password = serializer.validated_data.get('password')
year = serializer.validated_data.get('year')
start = serializer.validated_data.get('start')
end = serializer.validated_data.get('end')
reference_id = serializer.validated_data.get('reference_id')
response_status = main_function(user=username, pasd=password,year=year,start=start,end=end,reference_id=reference_id)
return Response("The File Upload Response Will Come HEre",response_status)
# return Response({'name':username, 'pass':password,'year':year,'start':start,'end':end,'reference_id':reference_id})
else:
return Response(serializer.errors)
The POST API will LOOk Like this.
If you guys are facing an error here then Plese comment.
Upvote if it helps.

Related

Django ModelViewSet PATCH request return model fields updated

class MerchantStampCardViewSet(viewsets.ModelViewSet):
'''
A view set for listing/retrieving/updating/deleting stamp cards for the current
merchant
'''
permission_classes = (IsMerchantAndAuthenticated, )
def get_queryset(self):
if len(MerchantProfile.objects.filter(user=self.request.user)) > 0:
merchant_profile = MerchantProfile.objects.get(user=self.request.user)
if merchant_profile.merchant:
return StampCard.objects.filter(merchant=merchant_profile.merchant)
return None
def get_serializer_class(self):
if self.request.method == 'GET':
return StampCardSerializerWithRewards
else:
return StampCardSerializer
I'm trying to make this code return the fields changed in the response body. The model class has a couple fields like name, city, province, zip code and address and through the front-end the user can only change one at a time, but I want the body of the 200 response to contain the field name changed and the new value just to confirm that a change was successful and nothing went wrong.
So for example if the user changes the name to Billy. The response should be 200 and the body should say {name : 'Billy'}
How do I do this?
You can try like this:
class YourViewSet(...):
def update(self, request, *args, **kwargs):
instance = self.get_object()
current_data = self.get_serializer(instance).data # collect current data
# next few lines of the code is from default implementation
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
instance._prefetched_objects_cache = {}
updated_data = serializer.data # now we get the updated data
response_dict = dict()
for key, value in current_data:
# find the differences
if updated_data.get(key) != value:
response_dict[key] = updated_data.get(key)
return Response(response_dict) # send the difference through response
Here I have put a override on update method. Then I have collected the dictionary data from current object and updated object. Then compared them and sent differences in a dictionary as response. FYI its an untested code.

Django 1.11: "global name 'user' is not defined"

I have a survey app - you create a Survey and it saves the Response. It's registered in Django Admin. I can see the Survey and submit a Response. When I click Response in Admin, I get the following error:
ValueError at /admin/django_survey/response/
Cannot query "response 5f895af5999c49929a522316a5108aa0": Must be "User" instance.
So I checked the SQL database and for django_survey_response I can see that there is a response, but the column user_id is NULL.
I suspected that there's an issue with my Views and/or Forms and I'm not saving the logged in User's details, so I've tried to address that.
However, now I get
NameError at /survey/1/
global name 'user' is not defined
How do I resolve this? I want the form to save Response with the logged in user's ID.
The Traceback:
django_survey\views.py
def SurveyDetail(request, id):
survey = Survey.objects.get(id=id)
category_items = Category.objects.filter(survey=survey)
categories = [c.name for c in category_items]
print 'categories for this survey:'
print categories
if request.method == 'POST':
form = ResponseForm(request.POST, survey=survey) <.........................
if form.is_valid():
response = form.save()
return HttpResponseRedirect("/confirm/%s" % response.interview_uuid)
else:
form = ResponseForm(survey=survey)
print form
django_survey\forms.py
def __init__(self, *args, **kwargs):
# expects a survey object to be passed in initially
survey = kwargs.pop('survey')
self.survey = survey
self.user = user <.........................
super(ResponseForm, self).__init__(*args, **kwargs)
self.uuid = random_uuid = uuid.uuid4().hex
# add a field for each survey question, corresponding to the question
# type as appropriate.
data = kwargs.get('data')
It might be worth noting that previously, instead of user, the model's field was called interviewee. I changed this and ran migrations again.
I am also using userena.
The error message in this instance is python trying to tell you that you are attempting to access a variable user that has not been defined in the scope of your method.
Let's look at the first few lines of the __init__() method:
def __init__(self, *args, **kwargs):
# expects a survey object to be passed in initially
survey = kwargs.pop('survey')
self.survey = survey
self.user = user
We can see where the survey variable is defined: survey = kwargs.pop('survey'). It is passed into the form as a keyword argument and extracted in the forms __init__. However underneath you attempt to do the same thing with user but haven't actually defined it above. The correct code would look like:
def __init__(self, *args, **kwargs):
# expects a survey object to be passed in initially
survey = kwargs.pop('survey')
user = kwargs.pop('user')
self.survey = survey
self.user = user
However, this still won't work because we aren't passing the user variable to the form via kwargs. To do that we pass it in when we initialise the form in your views.py. What isn't clear is what user object you are expecting to pass in. the request.user? or does the Survey object have a user attribute? in which case you would not need to pass user in and would just use survey.user etc.
django_survey\views.py
def SurveyDetail(request, id):
survey = Survey.objects.get(id=id)
category_items = Category.objects.filter(survey=survey)
categories = [c.name for c in category_items]
print 'categories for this survey:'
print categories
if request.method == 'POST':
form = ResponseForm(request.POST, survey=survey, user=request.user)
if form.is_valid():
response = form.save()
return HttpResponseRedirect("/confirm/%s" % response.interview_uuid)
else:
form = ResponseForm(survey=survey, user=request.user)
print form
In your view when you initialize your form you need to pass it the user (current user in this case)? similar to this form = ResponseForm(request.POST, survey=survey, user=request.user). Then in the __init__ of your form pop the user object user = kwargs.pop('user'). I believe that will resolve your issue.

Django REST Framework - unittest client failing to resolve hyperlinks relation for POST

I have this test:
class AttributeTest(APITestCase):
def setUp(self):
user1 = User.objects.create(pk=1, username='pepa', email='ads#asasd.cz', is_active=True, is_staff=True)
user1.set_password('mypass')
user1.save()
self.c1 = Campaign.objects.create(pk=1, owner=user1, project_name='c1')
def test(self):
campaign_url = 'http://testserver/api/campaigns/{}/'.format(self.c1.pk)
self.client.login(username='pepa', password='mypass')
data = {
"label": "something_here",
"parent_campaign": campaign_url,
}
# campaign clearly exists (created in setUp) and GET retrieve it:
assert self.client.get(campaign_url).json()['project_name'] == 'c1'
# I can even try it myself using pdb
# but this doesn't work - response return 400 Bad Request
# complaining about the very same hyperlink I can GET above
response = self.client.post('/api/keys', data, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
but when run, it fails with {'parent_campaign': ['Invalid hyperlink - No URL match.']}.
When I try using curl or browsable API (outside the test environment), everything works as expected.
My serializer corresponding to the /api/keys:
class AttributeSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='api:key-detail')
parent_campaign = serializers.HyperlinkedRelatedField(
view_name='api:campaign-detail',
lookup_field='cid',
queryset=Campaign.objects.all())
def _get_user_campaigns(self):
user = self.context['view'].request.user
return Campaign.objects.filter(owner=user)
def get_fields(self, *args, **kwargs):
fields = super(AttributeSerializer, self).get_fields(*args, **kwargs)
fields['parent_campaign'].queryset = self._get_user_campaigns()
return fields
class Meta:
model = Key
fields = ("id", 'url', "label", 'parent_campaign')
Using serializer directly:
(Pdb) from api.attribute.serializers import AttributeSerializer
(Pdb) ser = AttributeSerializer(data=data)
(Pdb) ser.is_valid()
True
(Pdb) ser.save()
<Key: Something1 | MAROO | CID: lrvyw93>
Try reversing your url name and passing c1.pk as a url parameter, not just formatting it into your url:
from rest_framework.reverse import reverse
campaign_url_name = 'api:campaign-detail' # Use URL name instead of raw URL path
response = self.client.get(reverse(campaign_url_name, kwargs={'pk': self.c1.pk}))
I don't know why, but the results of tests had to be somehow cached. I restarted the PC and it worked with exactly the same commit. Solved.

Make BooleanField required in Django Rest Framework

I've got a model with a boolean field that I'd like to deserialize with the Django rest framework and I want the serializer to complain when a field is missing in the post request. Yet, it doesn't. It silently interprets a missing boolean as False.
class UserProfile(models.Model):
"""
Message between two users
"""
user = models.OneToOneField(User, verbose_name="django authentication user", related_name='user_profile')
newsletter = models.BooleanField(null=False)
research = models.BooleanField(null=False)
The model is created with a Serialiser like this:
class UserProfileSerializer(serializers.ModelSerializer):
research = BooleanField(source='research', required=True)
newsletter = BooleanField(source='newsletter', required=True)
class Meta:
model = UserProfile
fields = ('research', 'newsletter')
In my view I'm also creating a user, so I have some manual steps:
def post(self, request, format=None):
userprofile_serializer = UserProfileSerializer(data=request.DATA)
reg_serializer = RegistrationSerializer(data=request.DATA)
phone_serializer = PhoneSerializer(data=request.DATA)
errors = {}
if userprofile_serializer.is_valid() and reg_serializer.is_valid() and phone_serializer.is_valid():
user = reg_serializer.save()
data = reg_serializer.data
user_profile = userprofile_serializer.object
user_profile.user = user
userprofile_serializer.save()
return Response(data, status=status.HTTP_201_CREATED)
errors.update(reg_serializer.errors)
# ...
return Response(errors, status=status.HTTP_400_BAD_REQUEST)
However, the following test case fails, because the rest framework doesn't complain about the missing param but instead inserts a False in from_native
def test_error_missing_flag(self):
data = {'username': "test", 'password': "123test", 'email': 'test#me.com',
'newsletter': 'true', 'uuid': self.uuid}
response = self.client.post(reverse('app_register'), data)
# should complain that 'research' is not found
self.assertTrue('research' in response.data)
If I replace my 'research' field with an Integer field that the serializer fails as expected. Any ideas?
There was an issue with Boolean fields and the required argument. Should now be fixed in master.
See this issue: https://github.com/tomchristie/django-rest-framework/issues/1004
Add
your_field = serializers.NullBooleanField(required=False)
in serializer.
That's it. It'll work :)
For anyone who has read #Tom's accepted answer from 2013 and finds that this still doesn't work, it's because this behavior is intended for HTML form inputs. Here's the original issue.
To use serializers.BooleanField with a JSON payload, convert your request.POST to a Python dict by doing request.POST.dict() and pass it to your serializer while initializing.
Create a new custom class:
from rest_framework import serializers
class RequirableBooleanField(serializers.BooleanField):
default_empty_html = serializers.empty
Now, you can use:
research = RequirableBooleanField(required=True)
or
research = RequirableBooleanField(required=False)

Django Issue Setting Empty String

I currently have a form that allows the user to edit their profile (django-profiles). The form is working great, except that I've put a switch in place for States and Provinces depending on whether ot not the user picks Canada or the US.
In my model, I have both State and Province set so that in the table there are 2 separate fields.
In my save method I have the following:
def user_created(sender, user, request, **kwargs):
form = RegistrationFormZ(request.POST)
....stuff here.....
if form.data["country"] == 'US':
data.state = form.data["state"]
data.provinces = None
if form.data["country"] == 'CA':
data.provinces = form.data["provinces"]
data.state = None
....stuff here....
data.save()
user.first_name = form.data['first_name']
user.last_name = form.data['last_name']
user.save()
from registration.signals import user_registered
user_registered.connect(user_created)
The if statements are not working as I expected they would on save.
What's happening is that it correctly saves the value of the option that was selected (state/province) but then fails to update the opposing one to an empty value.
So for example let's say I had a user who had a Canadian address, and then updated it to the US address. The US state gets properly saved, but province does not get updated to None.
Perhaps a more elegant way to accomplish this is using dictionaries. As an aside, are you sure your field name is provinces (plural) instead of singular? Here's an example how to do this with dictionaries, note you could add more fields for different countries without any more if statements and you could even write a separate getter function to return these dictionaries:
def user_created(sender, user, request, **kwargs):
form = RegistrationFormZ(request.POST)
....stuff here.....
data.state = None
data.provinces = None
state_field = {}
state_field['US'] = {}
state_field['US']['src'] = form.data['state']
state_field['US']['dest'] = data.state
state_field['CA'] = {}
state_field['CA']['src'] = form.data['province']
state_field['CA']['dest'] = data.province
state_field[form.data['country'].upper()]['dest'] = state_field[form.data['country'].upper()]['src']
....stuff here....
data.save()
user.first_name = form.data['first_name']
user.last_name = form.data['last_name']
user.save()
from registration.signals import user_registered
user_registered.connect(user_created)