TastyPie obj create method not being called - django

Am a newbie in TastyPie. I have a very simple resource and am overriding the obj_create method in following way.
Ajax Call :-
var data2 ={
"crave": data1,
"uid": "100",
"access_token": "AAA"
};
$.ajax({
url: "http://localhost:8000/restapi/v1/icrave/",
type: 'POST',
data: data2,
contentType: 'application/json',
dataType: 'json',
success: function (res) {
console.log(res);
},
});
In the resource
class IcravesResource(ModelResource):
person = fields.ForeignKey(UserResource, 'person')
class Meta:
queryset = Icrave.objects.filter(anonymous_or_not = False,is_active = True).order_by('-datetime')
resource_name = "icrave"
allowed_methods = ['get','post']
authentication = GetAuthentication()
authorization = GetAuthorization()
def obj_create(self,bundle,request=None, **kwargs):
print "Check if code reached here !!!"
return super( IcravesResource, self ).obj_create( self, bundle, request, **kwargs )
The code is not reaching here. What am i doing wrong ? I have checked the authorization and authentication they are both returning true. How can I debug this issue ?

You could use the Python Debugger. ( http://docs.python.org/library/pdb.html )
Find your copy of tastypie (possibly in your virtualenv), open the file resources.py and find the method *post_list*. This is the method that gets called when a POST request to a list-resource URL is send to Django.
You'll find a call to *obj_create* somewhere in that method. Now you can set a breakpoint by adding the lines:
import pdb
pdb.set_trace()
in that method. Maybe as the first statement.
Now, when you start your devserver and issue your ajax-call the execution should stop at the set_trace() and you should see a python prompt in the shell you did start the devserver.
Now you can explore the runtime-environment of the request. You can can for example inspect local variables by entering them at the prompt.
You can see the listing of the method you're in by typing 'l' (little L), execute the next line with 'n', step into a function with 's'.
This should help you make sense of what is happening. Take some time to learn how to use pdb, it's well worth it.
For more info on pdb and django, see:
http://ericholscher.com/blog/2008/aug/31/using-pdb-python-debugger-django-debugging-series-/

Can you verify based on the server logs, that the server actually got the POST request?
If so, you can also try specifying the allowed methods list, by putting:
list_allowed_methods = ['post', 'get']
instead of general allowed_methods one.
Here is a code snippet, which worked for me:
class EntryDetailsResource(CommonResource):
class Meta:
queryset = Entry.objects.all()
detail_allowed_methods = ['put','get','delete']
list_allowed_methods = ['post', 'get']
authorization = DjangoAuthorization()
validation = EntryDetailsValidation()
def obj_create(self, bundle, request=None, **kwargs):
import sys
print sys.stderr, 'aa'
return super(CommonResource, self).obj_create(bundle, request, user=request.user)

Related

Django test Client can only login once?

Our team is currently writing tests for our application. I am currently writing code to acces the views. These views are behind a login-screen, so our test first have to login and than peform the rest of the test. I've run into a very strange error. Basically My tests can only login once.
As you can see in the example below, both classes are doing the exact same thing, yet only one of them succeeds with the login, the other gives a '302 doest not equal 200' assertion error.
If I comment out the bottom one, the one at the top works, and vice versa.
Code that is testing different views also doesnt work, unless I comment out all other tests.
It doesnt matter if I login like shown below, or use a different variant (like self.client.login(username='test', password='password')).
Me and my team have no idea why Django is behaving this way and what we are doing wrong. Its almost as if the connection remains open and we would have to add code to close it. But the django-documentation doesnt mention any of this. DOes anyone know what we are doing wrong?
class FunctieListView_tests(TestCase):
"""Function listview only shows the data for the current_user / tenant"""
def setUp(self):
self.tenant = get_tenant()
self.function = get_function(self.tenant)
self.client = Client(HTTP_HOST='tc.tc:8000')
self.user = get_user(self.tenant)
def test_correct_function_context(self):
# Test if the view is only displaying the correct context data
self.client.post(settings.LOGIN_URL, {
'username': self.user.username,
'password': 'password'
}, HTTP_HOST='tc.tc:8000')
response = self.client.get(reverse('functie_list'))
self.assertEqual(response.status_code, 200)
self.assertTrue(response.context['functie_templates'] != None)
self.assertEqual(response.context['functie_templates'][0],
FunctieTemplate.objects.filter(linked_tenant=self.tenant)[0])
class FunctieListView_2_tests(TestCase):
"""Role Listview only shows the data for the current_user / tenant"""
def setUp(self):
self.tenant = get_tenant()
self.function = get_function(self.tenant)
self.client = Client(HTTP_HOST='tc.tc:8000')
self.user = get_user(self.tenant)
def test_correct_function_context_second(self):
#login
# Test if the view is only displaying the correct context data
self.client.post(settings.LOGIN_URL, {
'username': self.user.username,
'password': 'password'
}, HTTP_HOST='tc.tc:8000')
response = self.client.get(reverse('functie_list'))
self.assertEqual(response.status_code, 200)
self.assertTrue(response.context['functie_templates'] != None)
self.assertEqual(response.context['functie_templates'][0],
FunctieTemplate.objects.filter(linked_tenant=self.tenant)[0])
The users, tenants and functions are defined in a seperate utils file like so:
def get_user(tenant, name='test'):
u = User.objects.create_user(name, '{}#test.test'.format(name), 'password')
u.save()
u.profile.tenant = tenant
u.profile.tenant_role = generis.models.TENANT_OWNER
u.profile.save()
return u
def get_function(tenant):
userfunction = UserFunction.objects.create(name='test_functie', linked_tenant=tenant)
userfunction.save()
return userfunction
def get_tenant(slug_var='tc'):
f = elearning.models.FontStyle(font='foobar')
f.save()
c = elearning.models.ColorScheme(name='foobar', title='foo', text='fleeb', background='juice', block_background='schleem', box='plumbus')
c.save()
t = elearning.models.Tenant(name='tc', slug=slug_var, default_font_style=f, default_color_scheme=c)
t.save()
return t
My guess is that it happens because you are instantiating the Client yourself in setUp. Although it looks fine the outcome is obviously different from the regular behavior. I never had problems with login using the preinitialized self.client of django.test.TestCase.
Looking at django.test.client.Client, it says in the inline documentation:
Client objects are stateful - they will retain cookie (and thus session) details for the lifetime of the Client instance.
and a still existing cookie would explain the behavior you describe.
I cannot find HTTP_HOST in django.test.client.py, so I'm not sure whether you are really using that Client class at all. If you need access to a live server instance during tests, you could use Django's LiveServerTestCase.

Django rest framework: unit testing database issue

I am doing unit testing of the rest Apis. I am using django rest framework.
Apis are saving data into and getting data from the database. Both of the operations are not working or if it is working i am not able to see that in the databases. Apis are also using django-fsm, because of which i need same data from the db for the other tests. Tests depends on previous tests due to django-fsm. There is always state changing with the api. But now i am not able to see any data in database during test runs. Don't know where it is saving the data or in which database.
Below is my test settings:-
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': join(PROJECT_ROOT, 'run', 'db_for_testing.sqlite3'),
'TEST': {
'NAME': 'test_db_for_testing',
},
},
}
below is my api:-
class DummyView(CreateAPIView):
def post(self, request, *args, **kwargs):
data = request.data.copy()
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
order = self.model(book=serializer.data.get('book'))
order.save()
data = {
'status_code': 200,
'message': 'successfully.'
}
return Response(data, status=status.HTTP_200_OK)
As my tests depends on the previous test saving the data to db, so the other tests also fails. I am using APITestCase of rest_framework.
Help guys.
thanks in advance.
If I'm understanding your question correctly, Django "clear" database after each test (either rolling back or truncating.) So you need to write your tests accordingly.
See: https://docs.djangoproject.com/en/1.10/topics/testing/tools/#transactiontestcase
TL;DR - Solution: Use SimpleTestCase - See example below
Explanation
The thing is that the recommended test classes provided by Django for tests involving database queries, TransactionTestCase and the subclass TestCase, wraps every test in a transaction to speed up the process of resetting the database after each test. Source: Django TransactionTestCase docs
It is possible to avoid this behaviour by using SimpleTestCase which is the parent class of TransactionTestCase. You then have to specify explicitly that you want to allow database queries by setting allow_database_queries to True-
Also note that you are then responsible for any cleaning that needs to be done after the test. You can do that by overriding the tearDownClass class method. Similarly there's a setUpClass class method for any initialization prior to running the test. Remember to call the super methods. See full details in the docs
Example
from django.test import SimpleTestCase
class MyTestCase(SimpleTestCase):
allow_database_queries = True
#classmethod
def setUpClass(cls):
# Do your pre test initialization here.
super(MyTestCase, cls).setUpClass()
#classmethod
def tearDownClass(cls):
# Do your post test clean uphere.
super(MyTestCase, cls).tearDownClass()
def test_add_data_through_api(self):
# Add 'dat data
...
def test_work_with_data_from_previous_test(self):
# Work 'dat data
...
Use the --keepdb option when calling manage.py test:
https://docs.djangoproject.com/en/2.1/ref/django-admin/#cmdoption-test-keepdb
It's available since django 1.8.
Hope this helps.
Here my three tests are dependent with previous one. If I run them as separate test the previous test data deleted and later one failed for lost of that data.
So I make them in two different functions both are not test function. Finally call the dependent functions from one test function.
class test_profile(APITestCase):
def setUp(self):
super_user = default_service.create_super_user()
self.application = default_service.create_oath2_application(super_user.id)
self.content_type = "application/x-www-form-urlencoded"
self.username = "user#domain.com"
self.password = "pass123"
def create_profile(self):
url = reverse('EmailSignUp')
body = {
"email": self.username,
"password": self.password,
"fullname": "Mamun Hasan"
}
response = self.client.post(url, body, CONTENT_TYPE=self.content_type)
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = (json.loads(response.content))['data']
# print("Profile", data)
self.assertEqual(data['username'], self.username)
self.assertEqual(data['fullname'], data['fullname'])
def get_access_token(self):
url = reverse('oauth2_provider:token')
body = {
"username": self.username,
"password": self.password,
"grant_type": self.application.authorization_grant_type,
"client_id": self.application.client_id,
"client_secret": self.application.client_secret,
}
response = self.client.post(url, body, CONTENT_TYPE=self.content_type)
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = (json.loads(response.content))
# print("AccessToken", data)
self.assertEqual(data['token_type'], 'Bearer')
return data
def get_profile(self, oath2_token):
url = reverse('GetProfile')
authorization = oath2_token["token_type"] + ' ' + oath2_token["access_token"]
response = self.client.get(url, CONTENT_TYPE=self.content_type, HTTP_AUTHORIZATION=authorization)
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = (json.loads(response.content))['data']
# print("Profile", data)
self.assertEqual(data['username'], self.username)
def test_dependent(self):
self.create_profile()
oath2_token = self.get_access_token()
self.get_profile(oath2_token)
I did not find any solution to commit the previous API data. If anyone knows please comment. So I have done it this way. I don't know this is the best solution but it works and tested.

django-nocaptcha-recaptcha always shows additional verification box

I installed django-nocaptcha-recaptcha and integrated it into my form:
from nocaptcha_recaptcha.fields import NoReCaptchaField
class ClientForm(forms.ModelForm):
captcha = NoReCaptchaField()
It shows up fine on the form, but whenever I click on it an additional dialog pops up asking to enter some text and verify. It happens every time. I tested it from another computer on another network and it still asks for additional verification after clicking the box.
This is what it looks like: additional verification dialog box
Here's how I'm handling the form:
#xframe_options_exempt
def registration(request):
if request.method == 'POST':
clientform = ClientForm(request.POST)
# check whether it's valid:
if clientform.is_valid():
new_client = clientform.save()
...
What am I doing wrong? Is it a problem with django-nocaptcha-recaptcha? Should I use something else?
P.S. I'm using django 1.7.1 with python 3.4
Another alternative: Minimalist and non framework dependant.
This is the code, in case you want to rewrite it.
'''
NO-CAPTCHA VERSION: 1.0
PYTHON VERSION: 3.x
'''
import json
from urllib.request import Request, urlopen
from urllib.parse import urlencode
VERIFY_SERVER = "www.google.com"
class RecaptchaResponse(object):
def __init__(self, is_valid, error_code=None):
self.is_valid = is_valid
self.error_code = error_code
def __repr__(self):
return "Recaptcha response: %s %s" % (
self.is_valid, self.error_code)
def __str__(self):
return self.__repr__()
def displayhtml(site_key, language=''):
"""Gets the HTML to display for reCAPTCHA
site_key -- The site key
language -- The language code for the widget.
"""
return """<script src="https://www.google.com/recaptcha/api.js?hl=%(LanguageCode)s" async="async" defer="defer"></script>
<div class="g-recaptcha" data-sitekey="%(SiteKey)s"></div>
""" % {
'LanguageCode': language,
'SiteKey': site_key,
}
def submit(response,
secret_key,
remote_ip,
verify_server=VERIFY_SERVER):
"""
Submits a reCAPTCHA request for verification. Returns RecaptchaResponse
for the request
response -- The value of response from the form
secret_key -- your reCAPTCHA secret key
remote_ip -- the user's ip address
"""
if not(response and len(response)):
return RecaptchaResponse(is_valid=False, error_code='incorrect-captcha-sol')
def encode_if_necessary(s):
if isinstance(s, str):
return s.encode('utf-8')
return s
params = urlencode({
'secret': encode_if_necessary(secret_key),
'remoteip': encode_if_necessary(remote_ip),
'response': encode_if_necessary(response),
})
params = params.encode('utf-8')
request = Request(
url="https://%s/recaptcha/api/siteverify" % verify_server,
data=params,
headers={
"Content-type": "application/x-www-form-urlencoded",
"User-agent": "reCAPTCHA Python"
}
)
httpresp = urlopen(request)
return_values = json.loads(httpresp.read().decode('utf-8'))
httpresp.close()
return_code = return_values['success']
if return_code:
return RecaptchaResponse(is_valid=True)
else:
return RecaptchaResponse(is_valid=False, error_code=return_values['error-codes'])
Restart the server and don't forget to clear your browser's cache. Hope this helps.

Getting Primary Key from Tastypie Resource URI

I have a Django - tastypie Resource as follows. It has multiple fields that has Many to Many Relations.
I am trying to get the fields "workflow_initiators", "workflow_submitters" and "workflow_approvers" and add the user to a respective group namely initiators, submitters and approvers.
My JSON Request as follows :
{
"workflow_approvers": [
"/client/api/v1/user/44/",
"/client/api/v1/user/6/"
],
"workflow_dept": [
"/client/api/v1/departments/1/",
"/client/api/v1/departments/2/"
],
"workflow_initators": [
"/client/api/v1/user/44/",
"/client/api/v1/user/6/"
],
"workflow_name": "Hello Workflow",
"workflow_submitters": [
"/client/api/v1/user/43/",
"/client/api/v1/user/6/"
],
}
I want to get the primary key from resource uri of tastypie in a hydrate() or a obj_create() method. In order to get the pk i used a function get_pk_from_uri(). But it throws an error of the following
error : global name 'get_pk_from_uri' is not defined
My Resource as follows :
class WorkflowResource(ModelResource):
workflow_dept = fields.ToManyField(DepartmentsResource, 'workflow_dept', related_name='departments', full=True)
workflow_initators = fields.ToManyField(UserResource, 'workflow_initators', related_name='user')
workflow_submitters = fields.ToManyField(UserResource, 'workflow_submitters', related_name='user')
workflow_approvers = fields.ToManyField(UserResource, 'workflow_approvers', related_name='user')
def obj_create(self, bundle, **kwargs):
submitters = bundle.data.get('workflow_submitters', [])
for submitter in submitters:
print(get_pk_from_uri(submitter)) # Throws Error
#Adding User to Group Logic
# g = Group.objects.get(name='submitters')
# g.user_set.add(your_user)
class Meta:
queryset = WorkflowIndex.objects.filter(is_active=True)
resource_name = 'workflows'
list_allowed_methods = ['get', 'post']
detail_allowed_methods = ['get', 'post', 'put', 'delete', 'patch']
serializer = Serializer()
default_format = 'application/json'
authentication = Authentication()
authorization = DjangoAuthorization()
always_return_data = True
Is there any other method to get the primary key and other fields from resource uri ? I did see get_via_uri() method but was unsure on how to implement the same.
Kindly guide me in resolving this issue.
References :
Stackoverflow - Get model object from tastypie uri
Tastypie Documents - get_via_uri()
You should go back to this post: Get model object from tastypie uri?
The get_pk_from_uri(uri) method that you can see in this answer is not part of the source code of Tastypie, as you can check here.
I suppose the guy wrote it by himself, and you should do the same sothat you won't get the error : global name 'get_pk_from_uri' is not defined error. I didn't tested his method thought.
Solution
David was correct. The get_pk_from_uri() is not an in built tasty-pie method, which i was mistaken.
P.S : I'm just making the answer clear so that someone find it useful.
When needed to extract the resource name or pk from the resource uri of tastypie. we will be able to access them from **kwargs of the below section. The Kwargs contains the follows
kwargs
{u'api_name': 'v1', u'pk': '1', u'resource_name': 'workflows'}
Add the following code to your resources.py or utils.py and include it in your API's to a get this method get_pk_from_uri
from django.core.urlresolvers import resolve, get_script_prefix
def get_pk_from_uri(uri):
prefix = get_script_prefix()
chomped_uri = uri
if prefix and chomped_uri.startswith(prefix):
chomped_uri = chomped_uri[len(prefix) - 1:]
try:
view, args, kwargs = resolve(chomped_uri)
except Resolver404:
raise NotFound("The URL provided '%s' was not a link to a valid resource." % uri)
return kwargs['pk']

Convert POST to PUT with Tastypie

Full Disclosure: Cross posted to Tastypie Google Group
I have a situation where I have limited control over what is being sent to my api. Essentially there are two webservices that I need to be able to accept POST data from. Both use plain POST actions with urlencoded data (basic form submission essentially).
Thinking about it in "curl" terms it's like:
curl --data "id=1&foo=2" http://path/to/api
My problem is that I can't update records using POST. So I need to adjust the model resource (I believe) such that if an ID is specified, the POST acts as a PUT instead of a POST.
api.py
class urlencodeSerializer(Serializer):
formats = ['json', 'jsonp', 'xml', 'yaml', 'html', 'plist', 'urlencoded']
content_types = {
'json': 'application/json',
'jsonp': 'text/javascript',
'xml': 'application/xml',
'yaml': 'text/yaml',
'html': 'text/html',
'plist': 'application/x-plist',
'urlencoded': 'application/x-www-form-urlencoded',
}
# cheating
def to_urlencoded(self,content):
pass
# this comes from an old patch on github, it was never implemented
def from_urlencoded(self, data,options=None):
""" handles basic formencoded url posts """
qs = dict((k, v if len(v)>1 else v[0] )
for k, v in urlparse.parse_qs(data).iteritems())
return qs
class FooResource(ModelResource):
class Meta:
queryset = Foo.objects.all() # "id" = models.AutoField(primary_key=True)
resource_name = 'foo'
authorization = Authorization() # only temporary, I know.
serializer = urlencodeSerializer()
urls.py
foo_resource = FooResource
...
url(r'^api/',include(foo_resource.urls)),
)
In #tastypie on Freenode, Ghost[], suggested that I overwrite post_list() by creating a function in the model resource like so, however, I have not been successful in using this as yet.
def post_list(self, request, **kwargs):
if request.POST.get('id'):
return self.put_detail(request,**kwargs)
else:
return super(YourResource, self).post_list(request,**kwargs)
Unfortunately this method isn't working for me. I'm hoping the larger community could provide some guidance or a solution for this problem.
Note: I cannot overwrite the headers that come from the client (as per: http://django-tastypie.readthedocs.org/en/latest/resources.html#using-put-delete-patch-in-unsupported-places)
I had a similar problem on user creation where I wasn't able to check if the record already existed. I ended up creating a custom validation method which validated if the user didn't exist in which case post would work fine. If the user did exist I updated the record from the validation method. The api still returns a 400 response but the record is updated. It feels a bit hacky but...
from tastypie.validation import Validation
class MyValidation(Validation):
def is_valid(self, bundle, request=None):
errors = {}
#if this dict is empty validation passes.
my_foo = foo.objects.filter(id=1)
if not len(my_foo) == 0: #if object exists
foo[0].foo = 'bar' #so existing object updated
errors['status'] = 'object updated' #this will be returned in the api response
return errors
#so errors is empty if object does not exist and validation passes. Otherwise object
#updated and response notifies you of this
class FooResource(ModelResource):
class Meta:
queryset = Foo.objects.all() # "id" = models.AutoField(primary_key=True)
validation = MyValidation()
With Cathal's recommendation I was able to utilize a validation function to update the records I needed. While this does not return a valid code... it works.
from tastypie.validation import Validation
import string # wrapping in int() doesn't work
class Validator(Validation):
def __init__(self,**kwargs):
pass
def is_valid(self,bundle,request=None):
if string.atoi(bundle.data['id']) in Foo.objects.values_list('id',flat=True):
# ... update code here
else:
return {}
Make sure you specify the validation = Validator() in the ModelResource meta.