So I have a django web app called notes where I have a viewset. The viewset is composed of CRUD commands. When I created a test to the api it keeps redirecting to a nonexistent url instead of the Json String. Here is my code:
For tests.py:
def test_list(self):
e = Employee.objects.get(first_name="Dylan")
organ = Organization.objects.get(name='test')
cl = APIClient()
# cl = APIClient()
c = Customer.objects.get(first_name="John")
cl.credentials(HTTP_AUTHORIZATION='Token ' + organ.token.key)
response = cl.post('/api/notes/list', {'customer_id': c.uuid}, follow=True)
#cl.login(username = "Dylan", password="Snyder")
pprint("list: %s" % response.data)
for api.py:
def list(self, request):
c = Customer.objects.get(pk=request.POST['customer_id'])
n = Note.objects.get(customer=c, retired=False)
notes = NoteListSerializer(n)
return HttpResponse(notes)
The results I get:
.'list: {\'detail\': \'Method "GET" not allowed.\'}'
in the command prompt I use. I never made a directory or have anything that contains /details/ in it
Related
I develop a DJango project and have develop few tests
One of these tests used to pass but do not anymore and I do not understand why
class IndexPageTestCase(TestCase):
def setUp(self):
self.client = Client()
self.pays = Pays.objects.create(pay_ide = 1,pay_nom = 'Côte d\'Ivoire',pay_abr = 'CIV')
self.region = Region.objects.create(reg_ide = 1,pay = self.pays,reg_nom = 'Region 1',reg_abr = 'RE1')
self.site = Site.objects.create(sit_ide = 1,reg=self.region,sit_typ = 'National',sit_nom = 'PA',sit_abr = 'PA')
self.user = User.objects.create_user('user','user#user.com','password')
self.profile = Profile.objects.create(user=self.user,site = self.site)
def test_index_page(self):
self.client.post('/registration/login/', {'username': 'user', 'password': 'password'})
response = self.client.get(reverse('randomization:index'))
print(response)
self.assertEquals(response.status_code,200)
print show it is not the good url (/accounts/login/?next=/randomization/ instead of /registration/login/):
<HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/accounts/login/?next=/randomization/">
The url you pass returns a 302 which means redirect.
Your URL is good but is just redirected. set your assertEquals status code to 302
I have a function which displays the live location of a vehicle on demand. There are two functions and thus buttons, when clicked should run a code internally, and display the result on this same view/page. I am not able to understand how to create those two functions, without reloading my page or going to another page. I do not have to pass any kind of arguments in the function from the template, as of now. Is this possible in django??
Code
Here is my function in my view.py
#method_decorator([login_required, teacher_required], name='dispatch')
class QuizResultsView(DetailView):
model = Quiz
context_object_name = 'quiz'
template_name = 'classroom/teachers/quiz_results.html'
tripId = str()
def start_trip(self):
quiz = self.get_object()
lr_object = get_object_or_404(LR, lr_quiz=quiz.id)
# ----------Call api to fetch location code -----------#
My api fetch code will return a json object which I have to parse and enter the same in a Google Iframe, and it takes about 2-3 seconds to populate the iframe.
I have many 'mini-functions' or whatever they are called in my same function, just like :
def get_queryset (self):
return self.request.user.quizzes.all()
Hence, is there a way to create more functions just like these? Also, how will I call such functions from my templates? Is there any other way of creating a view, where I can have multiple buttons, with individual functions to each button, and it does not reload the page or go to any other page ?
Complete Code
def start_trip (self):
quiz = self.get_object()
lr_object = get_object_or_404(LR, lr_quiz=quiz.id)
print("#271 lr_object.driver_name", lr_object.driver_name)
gmaps = googlemaps.Client(key=###)
driver_num = lr_object.driver_name
origin = lr_object.lr_consignee_address # origin from lR
dest = lr_object.lr_consignor_address # dest from LR
geocode_result_orig = gmaps.geocode(origin)
geocode_result_orig = gmaps.geocode(origin)
lat_orig = str(geocode_result_orig[0]["geometry"]["location"]["lat"])
lon_orig = str(geocode_result_orig[0]['geometry']['location']['lng'])
geocode_result_dest = gmaps.geocode(dest)
lat_dest = str(geocode_result_dest[0]["geometry"]["location"]["lat"])
lon_dest = str(geocode_result_dest[0]['geometry']['location']['lng'])
url = 'https://sct.intutrack.com/api/prod/trips/start'
payload = 'tel=' + driver_num + '&src=' + lat_orig + '%2C%20' + lon_orig + '&dest=' + lat_dest + '%2C%20' + lon_dest + '&srcname=Intugine' \
'%20Technologies&destname=Bangalore&client=XYZ&vendor=Rivigo&vehicle=' \
'MH01x123&invoice=123ZXYZ&eta_hrs=75'
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
username = '###'
password = '###'
user_pass = (username, password)
response = requests.request('POST', url, headers=headers, data=payload, auth=user_pass)
response = response.json()
tripId = response["tripId"]
UPDATE:
I am thinking this can only be done with javascript, so I guess my question becomes 'How to pass django variables to a javascript function (without reloading the page ofc.)?'
I'm new to Django's testing and trying to write test functions for views, but I don't get the whole test view thing, I've seen a lot of examples but it seems to be to difficult, I need an example on something I wrote to get the idea.
here's a function I want to test its post and get:
def ForgetPasswordRegistry(self, request):
forgetpassword = True
if request.method == 'GET':
if 'UserID' in request.session:
forgetpassword = False
return request, forgetpassword
elif request.method == 'POST':
email = request.POST['email']
if self.IsUserExist(email):
forget = models.ForgetPasswordRegistry()
forget.UserID = self.GetUser(email)
forget.Access = 1
session = random.randint(999999999999999, 9999999999999999999999999999999999)
forget.Session = str(session)
link = 'http://127.0.0.1:8000/home/resetpassword/' + forget.Session
self.SendEmail('Reset-Password', link, [forget.UserID.Email])
forget.save()
FeedBack = ' Check Your Mail You Got A Recovery Mail'
AlertType = 'alert-success'
return request, AlertType, None, FeedBack, None, None
else:
AlertType = 'alert-danger'
ModalFeedBack = 'This Email Dose Not Exist'
EmailErrors = 'This Email Dose Not Exist'
return request, AlertType, forgetpassword, None, EmailErrors, ModalFeedBack
Don't let the concept of views confuse you!! Essentially there is a python function that you need to exercise certain conditionals of.
It looks like there are 4 main branches:
GET not in session
GET in session
POST exists
POST does not exist
so there should probably be at least 4 different tests. I'll post the first one because it is pretty straightforward:
def ForgetPasswordRegistry(self, request):
forgetpassword = True
if request.method == 'GET':
if 'UserID' in request.session:
forgetpassword = False
return request, forgetpassword
def test_forget_get_user_id_in_session(self):
request = Mock(session={'UserID': 'here'}, method='GET')
# instantiate your view class
yourview = YourView()
sefl.assertEqual(yourview.ForgetPasswordRegistry(request), (request, False))
The post user exists branch is a little more complicated because there are more things that the test needs to account for, and potentially provide stub implementations for:
SendEmail
GetUser
models.ForgetPasswordRegistry()
Use Django's test client: https://docs.djangoproject.com/en/stable/topics/testing/tools/#the-test-client
Example:
from django.test import Client, TestCase
class ViewTestCase(TestCase):
def test_post_creation(self):
c = Client() # instantiate the Django test client
response = c.get('/forgetpassword/')
self.assertEqual(response.status, 200)
TL;DR
I am looking for a way to clear the cache after a request, or completely disable it when running tests. Django REST Framework seems to cache the results and I need a way around this.
Long version and code
Well this turned out to behave very weird as I kept testing it. In the end, I got it to work, but I really don't like my workaround and in the name of knowledge, I have to find out why this happens and how to solve this problem properly.
So, I have an APITestCase class declared like this:
class UserTests(APITestCase):
Inside this class, I have a test function for my user-list view, as I have a custom queryset depending on the permissions. To clear things up:
a superuser can get the whole users list (4 instances returned),
staff members cannot see superusers (3 instances returned),
normal users can only get 1 result, their own user (1 instance returned)
The test function version that works:
def test_user_querysets(self):
url = reverse('user-list')
# Creating a user
user = User(username='user', password=self.password)
user.set_password(self.password)
user.save()
# Creating a second user
user2 = User(username='user2', password=self.password)
user2.set_password(self.password)
user2.save()
# Creating a staff user
staff_user = User(username='staff_user', password=self.password, is_staff=True)
staff_user.set_password(self.password)
staff_user.save()
# Creating a superuser
superuser = User(username='superuser', password=self.password, is_staff=True, is_superuser=True)
superuser.set_password(self.password)
superuser.save()
# SUPERUSER
self.client.logout()
self.client.login(username=superuser.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# All users contained in list
self.assertEqual(response.data['extras']['total_results'], 4)
# STAFF USER
self.client.logout()
self.client.login(username=staff_user.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Superuser cannot be contained in list
self.assertEqual(response.data['extras']['total_results'], 3)
# REGULAR USER
self.client.logout()
self.client.login(username=user2.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Only 1 user can be returned
self.assertEqual(response.data['extras']['total_results'], 1)
# User returned is current user
self.assertEqual(response.data['users'][0]['username'], user2.username)
As you see, I am testing user permissions in this order: superuser, staff, normal user. And this works, so...
Funny thing:
If I change the order of the tests, and start with normal user, staff, superuser, the tests fail. The response from the first request gets cached, and then I get the same response when I log in as staff user, so the number of results is again 1.
The version that doesn't work:
it's exactly the same as before, only the tests are made in reverse order
def test_user_querysets(self):
url = reverse('user-list')
# Creating a user
user = User(username='user', password=self.password)
user.set_password(self.password)
user.save()
# Creating a second user
user2 = User(username='user2', password=self.password)
user2.set_password(self.password)
user2.save()
# Creating a staff user
staff_user = User(username='staff_user', password=self.password, is_staff=True)
staff_user.set_password(self.password)
staff_user.save()
# Creating a superuser
superuser = User(username='superuser', password=self.password, is_staff=True, is_superuser=True)
superuser.set_password(self.password)
superuser.save()
# REGULAR USER
self.client.logout()
self.client.login(username=user2.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Only 1 user can be returned
self.assertEqual(response.data['extras']['total_results'], 1)
# User returned is current user
self.assertEqual(response.data['users'][0]['username'], user2.username)
# STAFF USER
self.client.logout()
self.client.login(username=staff_user.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Superuser cannot be contained in list
self.assertEqual(response.data['extras']['total_results'], 3)
# SUPERUSER
self.client.logout()
self.client.login(username=superuser.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# All users contained in list
self.assertEqual(response.data['extras']['total_results'], 4)
I am working in python 2.7 with the following package versions:
Django==1.8.6
djangorestframework==3.3.1
Markdown==2.6.4
MySQL-python==1.2.5
wheel==0.24.0
UPDATE
I am using the default django cache, meaning I haven't put anything about cache in the django settings.
As suggested, I tried disabling the default Django cache:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
)
}
The problem stands on.
Even though I don't think the problem is located here, this is my UserViewSet:
api.py (the important part)
class UserViewSet(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet
):
queryset = User.objects.all()
serializer_class = UserExpenseSerializer
permission_classes = (IsAuthenticated, )
allowed_methods = ('GET', 'PATCH', 'OPTIONS', 'HEAD')
def get_serializer_class(self):
if self.action == 'retrieve':
return UserExpenseSerializer
return UserSerializer
def get_queryset(self):
if(self.action == 'list'):
return User.objects.all()
if self.request.user.is_superuser:
return User.objects.all()
if self.request.user.is_staff:
return User.objects.exclude(is_superuser=True)
return User.objects.filter(pk = self.request.user.id)
def list(self, request):
filter_obj = UsersFilter(self.request)
users = filter_obj.do_query()
extras = filter_obj.get_extras()
serializer = UserSerializer(users, context={'request' : request}, many=True)
return Response({'users' : serializer.data, 'extras' : extras}, views.status.HTTP_200_OK)
filters.py
class UsersFilter:
offset = 0
limit = 50
count = 0
total_pages = 0
filter_params = {}
def __init__(self, request):
if not request.user.is_superuser:
self.filter_params['is_superuser'] = False
if (not request.user.is_superuser and not request.user.is_staff):
self.filter_params['pk'] = request.user.id
# Read query params
rpp = request.query_params.get('rpp') or 50
page = request.query_params.get('page') or 1
search_string = request.query_params.get('search')
# Validate
self.rpp = int(rpp) or 50
self.page = int(page) or 1
# Set filter
set_if_not_none(self.filter_params, 'username__contains', search_string)
# Count total results
self.count = User.objects.filter(**self.filter_params).count()
self.total_pages = int(self.count / self.rpp) + 1
# Set limits
self.offset = (self.page - 1) * self.rpp
self.limit = self.page * self.rpp
def get_filter_params(self):
return self.filter_params
def get_offset(self):
return self.offset
def get_limit(self):
return self.limit
def do_query(self):
users = User.objects.filter(**self.filter_params)[self.offset:self.limit]
return users
def get_query_info(self):
query_info = {
'total_results' : self.count,
'results_per_page' : self.rpp,
'current_page' : self.page,
'total_pages' : self.total_pages
}
return query_info
UPDATE 2
As Linovia pointed out, the problem was not cache or any other DRF problem, but the filter. Here's the fixed filter class:
class UsersFilter:
def __init__(self, request):
self.filter_params = {}
self.offset = 0
self.limit = 50
self.count = 0
self.total_pages = 0
self.extras = {}
if not request.user.is_superuser:
# and so long...
Actually you create a new user which should make 2 users and you assert the length against 3. Not going to work even without caching.
Edit:
So you actually have you issue because the use of mutables objects at the class level.
Here's the evil code:
class UsersFilter:
filter_params = {}
def __init__(self, request):
if not request.user.is_superuser:
self.filter_params['is_superuser'] = False
Which should actually be:
class UsersFilter:
def __init__(self, request):
filter_params = {}
if not request.user.is_superuser:
self.filter_params['is_superuser'] = False
Otherwise UsersFilter.filter_params will be kept from one request to another and never resets. See http://www.toptal.com/python/python-class-attributes-an-overly-thorough-guide for more details about this.
You can disable caching in debug mode by adding this to your settings.py
if DEBUG:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
https://docs.djangoproject.com/en/dev/topics/cache/?from=olddocs/#dummy-caching-for-development
You can then disable caching by toggling DEBUG in settings.py or by having separate develop/test and /deploy settings.py files.
If you want to avoid separate files or toggling, then you can set DEBUG to true for certain tests with the override_settings decorator:
from django.test.utils import override_settings
from django.test import TestCase
from django.conf import settings
class MyTest(TestCase):
#override_settings(DEBUG=True)
def test_debug(self):
self.assertTrue(settings.DEBUG)
I am working off of a Miguel Grinberg tutorial on social authentication.
On the homepage template I have this code, and I removed the twitter portion from the tutorial:
<h2>I don't know you!</h2>
<p>Login with Facebook</p>
{% endif %}
So when you click that link, you pass Facebook as the provider through this view function:
#app.route('/authorize/<provider>')
def oauth_authorize(provider):
if not current_user.is_anonymous():
return redirect(url_for('index'))
oauth = OAuthSignIn.get_provider(provider)
return oauth.authorize()
Now, in a different file, oauth.py, I have the following and my issue is this. I keep getting an error when I click the Facebook link UNLESS the TwitterSignIn class is removed. I guess I am curious as to why the TwitterSignIn class needs to be removed for this to work, because no data is being passed to it, right? Even if Facebook wasn't the only option, why would clicking the Facebook sign-in link pass any data to the TwitterSignIn class?
from rauth import OAuth1Service, OAuth2Service
from flask import current_app, url_for, request, redirect, session
class OAuthSignIn(object):
providers = None
def __init__(self, provider_name):
self.provider_name = provider_name
credentials = current_app.config['OAUTH_CREDENTIALS'][provider_name]
self.consumer_id = credentials['id']
self.consumer_secret = credentials['secret']
def authorize(self):
pass
def callback(self):
pass
def get_callback_url(self):
return url_for('oauth_callback', provider=self.provider_name,
_external=True)
#classmethod
def get_provider(self, provider_name):
if self.providers is None:
self.providers = {}
for provider_class in self.__subclasses__():
provider = provider_class()
self.providers[provider.provider_name] = provider
return self.providers[provider_name]
class FacebookSignIn(OAuthSignIn):
def __init__(self):
super(FacebookSignIn, self).__init__('facebook')
self.service = OAuth2Service(
name='facebook',
client_id=self.consumer_id,
client_secret=self.consumer_secret,
authorize_url='https://graph.facebook.com/oauth/authorize',
access_token_url='https://graph.facebook.com/oauth/access_token',
base_url='https://graph.facebook.com/'
)
def authorize(self):
return redirect(self.service.get_authorize_url(
scope='email',
response_type='code',
redirect_uri=self.get_callback_url())
)
def callback(self):
if 'code' not in request.args:
return None, None, None
oauth_session = self.service.get_auth_session(
data={'code': request.args['code'],
'grant_type': 'authorization_code',
'redirect_uri': self.get_callback_url()}
)
me = oauth_session.get('me').json()
return (
'facebook$' + me['id'],
me.get('email').split('#')[0], # Facebook does not provide
# username, so the email's user
# is used instead
me.get('email')
)
class TwitterSignIn(OAuthSignIn):
def __init__(self):
super(TwitterSignIn, self).__init__('twitter')
self.service = OAuth1Service(
name='twitter',
consumer_key=self.consumer_id,
consumer_secret=self.consumer_secret,
request_token_url='https://api.twitter.com/oauth/request_token',
authorize_url='https://api.twitter.com/oauth/authorize',
access_token_url='https://api.twitter.com/oauth/access_token',
base_url='https://api.twitter.com/1.1/'
)
def authorize(self):
request_token = self.service.get_request_token(
params={'oauth_callback': self.get_callback_url()}
)
session['request_token'] = request_token
return redirect(self.service.get_authorize_url(request_token[0]))
def callback(self):
request_token = session.pop('request_token')
if 'oauth_verifier' not in request.args:
return None, None, None
oauth_session = self.service.get_auth_session(
request_token[0],
request_token[1],
data={'oauth_verifier': request.args['oauth_verifier']}
)
me = oauth_session.get('account/verify_credentials.json').json()
social_id = 'twitter$' + str(me.get('id'))
username = me.get('screen_name')
return social_id, username, None # Twitter does not provide email
Some additional information-
The specific error is this:
File "/Users/metersky/code/mylastapt/app/oauth.py", line 29, in get_provider
provider = provider_class()
File "/Users/metersky/code/mylastapt/app/oauth.py", line 73, in __init__
super(TwitterSignIn, self).__init__('twitter')
File "/Users/metersky/code/mylastapt/app/oauth.py", line 10, in __init__
credentials = current_app.config['OAUTH_CREDENTIALS'][provider_name]
KeyError: 'twitter'
And this is where the I think the issue might be happening:
app.config['OAUTH_CREDENTIALS'] = {
'facebook': {
'id': 'XXX',
'secret': 'XXXX'
}
}
The problem is in OAuthSignIn.get_provider.
#classmethod
def get_provider(self, provider_name):
if self.providers is None:
self.providers = {}
for provider_class in self.__subclasses__():
provider = provider_class()
self.providers[provider.provider_name] = provider
return self.providers[provider_name]
The first time you call it from within your view
oauth = OAuthSignIn.get_provider(provider)
the method caches the providers you've defined. It does this by checking for all of OAuthSignIn's subclasses.
for provider_class in self.__subclasses__():
When you include TwitterSignIn, it will be included as a subclass. You'll then instantiate an instance of the class
provider = provider_class()
Inside OAuthSignIn.__init__, you load the provider's settings with current_app.config['OAUTH_CREDENTIALS'][provider_name]. Since Twitter isn't included, you get the KeyError.
If you don't want to support Twitter, just remove the class. If you want to protect your application a little more so that providers can be removed from your settings without updating code, you'll need to check for the exception. You could do the check inside OAuthSignIn.__init__, but there probably isn't much value to including an unsupported provider in OAuthSignIn.providers. You're better off putting the check in OAuthSignIn.get_provider.
#classmethod
def get_provider(cls, provider_name):
if cls.providers is None:
cls.providers = {}
for provider_class in cls.__subclassess__():
try:
provider = provider_class()
except KeyError:
pass # unsupported provider
else:
cls.providers[provider.provider_name] = provider
return cls.providers[provider_name]