Django - Rest-framework: After Deployment to a remote server wrong results - python

It's a bit frustating:
I had to overwrite the list method in the ModelViewSet, because I need to work with Query Paramters:
...
queryset = ABC.objects.all().filter(Y = 1, X = 1)
self.object_list = self.filter_queryset(queryset)
page = self.paginate_queryset(self.object_list)
if page is not None:
serializer = self.get_pagination_serializer(page)
else:
serializer = self.get_serializer(self.object_list, many=True)
return Response(serializer.data)
Locally it works fine. But after the deployment to a remote server I dont get any results using the same URL. I dont think, that this is a CORS-issue, but after hours of searching I absolutely cannot find the reason.

Usually with problems in a different environment the issue is in the different URL's. For instance your url's might not be resolving because of the different host prefix etc.
At least my problems similar to it have always been the result of url conf.

Related

405 method not allowed, Django + ngrok, only on my local machine

This is a legacy project that I'm working with other guys in my current job.
and is doing a very strange behavior that I cannot understand.
It's returning 405 http response status, which does not make sense, because this view already accepts POST requests
I would share a couple of snippets, I just detected that happens just in the comment that I would mark.
this is the view file, that actually accepts both methods GET and POST
#csrf_exempt
#load_checkout
#validate_cart
#validate_is_shipping_required
#require_http_methods(["GET", "POST"])
def one_step_view(request, checkout):
"""Display the entire checkout in one step."""
this is the decorator that modifies the response, and returns 405.
def load_checkout(view):
"""Decorate view with checkout session and cart for each request.
Any views decorated by this will change their signature from
`func(request)` to `func(request, checkout, cart)`."""
#wraps(view)
#get_or_empty_db_cart(Cart.objects.for_display())
def func(request, cart):
try:
session_data = request.session[STORAGE_SESSION_KEY]
except KeyError:
session_data = ''
tracking_code = analytics.get_client_id(request)
checkout = Checkout.from_storage(
session_data, cart, request.user, tracking_code)
response = view(request, checkout, cart) # in this response it returns 405.
if checkout.modified:
request.session[STORAGE_SESSION_KEY] = checkout.for_storage()
return response
return func
Any idea or clue when I can start to find out the problem?.
for the record: I didn't code this, this was working a couple of days ago, and its just happening in my local environment, on stage and production, and even the local of others developers are working just fine. I have all the requirements and the dependencies, and are updated.
BTW I'm using ngrok for tunneling
--
if your front-end use different HOST && PORT
you need to add CROS in Django app

Django Rest Framework gives 302 in Unit tests when force_login() on detail view?

I'm using Django Rest Framework to serve an API. I've got a couple tests which work great. To do a post the user needs to be logged in and I also do some checks for the detail view for a logged in user. I do this as follows:
class DeviceTestCase(APITestCase):
USERNAME = "username"
EMAIL = 'a#b.com'
PASSWORD = "password"
def setUp(self):
self.sa_group, _ = Group.objects.get_or_create(name=settings.KEYCLOAK_SA_WRITE_PERMISSION_NAME)
self.authorized_user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASSWORD)
self.sa_group.user_set.add(self.authorized_user)
def test_post(self):
device = DeviceFactory.build()
url = reverse('device-list')
self.client.force_login(self.authorized_user)
response = self.client.post(url, data={'some': 'test', 'data': 'here'}, format='json')
self.client.logout()
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
# And some more tests here
def test_detail_logged_in(self):
device = DeviceFactory.create()
url = reverse('device-detail', kwargs={'pk': device.pk})
self.client.force_login(self.authorized_user)
response = self.client.get(url)
self.client.logout()
self.assertEqual(status.HTTP_200_OK, response.status_code, 'Wrong response code for {}'.format(url))
# And some more tests here
The first test works great. It posts the new record and all checks pass. The second test fails though. It gives an error saying
AssertionError: 200 != 302 : Wrong response code for /sa/devices/1/
It turns out the list view redirects the user to the login screen. Why does the first test log the user in perfectly, but does the second test redirect the user to the login screen? Am I missing something?
Here is the view:
class APIAuthGroup(InAuthGroup):
"""
A permission to allow all GETS, but only allow a POST if a user is logged in,
and is a member of the slimme apparaten role inside keycloak.
"""
allowed_group_names = [settings.KEYCLOAK_SA_WRITE_PERMISSION_NAME]
def has_permission(self, request, view):
return request.method in SAFE_METHODS \
or super(APIAuthGroup, self).has_permission(request, view)
class DevicesViewSet(DatapuntViewSetWritable):
"""
A view that will return the devices and makes it possible to post new ones
"""
queryset = Device.objects.all().order_by('id')
serializer_class = DeviceSerializer
serializer_detail_class = DeviceSerializer
http_method_names = ['post', 'list', 'get']
permission_classes = [APIAuthGroup]
Here is why you are getting this error.
Dependent Libraries
I did some searching by Class Names to find which libraries you were using so that I can re-create the problem on my machine. The library causing the problem is the one called keycloak_idc. This library installs another library mozilla_django_oidc which would turn out to be the reason you are getting this.
Why This Library Is Causing The Problem
Inside the README file of this library, it gives you instructions on how to set it up. These are found in this file. Inside these instructions, it instructed you to add the AUTHENTICATION_BACKENDS
AUTHENTICATION_BACKENDS = [
'keycloak_oidc.auth.OIDCAuthenticationBackend',
...
]
When you add this authentication backend, all your requests pass through a Middleware defined inside the SessionRefresh class defined inside mozilla_django_oidc/middleware.py. Inside this class, the method process_request() is always called.
The first thing this method does is call the is_refreshable_url() method which always returns False if the request method was POST. Otherwise (when the request method is GET), it will return True.
Now the body of this if condition was as follows.
if not self.is_refreshable_url(request):
LOGGER.debug('request is not refreshable')
return
# lots of stuff in here
return HttpResponseRedirect(redirect_url)
Since this is a middleware, if the request was POST and the return was None, Django would just proceed with actually doing your request. However when the request is GET and the line return HttpResponseRedirect(redirect_url) is triggered instead, Django will not even proceed with calling your view and will return the 302 response immediately.
The Solution
After a couple of hours debugging this, I do not the exact logic behind this middleware or what exactly are you trying to do to provide a concrete solution since this all started based off guess-work but a naive fix can be that you remove the AUTHENTICATION_BACKENDS from your settings file. While I feel that this is not acceptable, maybe you can try using another library that accomplishes what you're trying to do or find an alternative way to do it. Also, maybe you can contact the author and see what they think.
So i guess you have tested this and you get still the same result:
class APIAuthGroup(InAuthGroup):
def has_permission(self, request, view):
return True
Why do you use DeviceFactory.build() in the first test and DeviceFactory.create() in the second?
Maybe a merge of the two can help you:
def test_get(self):
device = DeviceFactory.build()
url = reverse('device-list')
response = self.client.get(url)
self.assertEqual(status.HTTP_200_OK, response.status_code)
Is this a problem with the setUp() method? From what I see, you may be setting self.authorize_user to a user that was already created on the first test.
Instead, I would create the user on each test, making sure that the user doesn't exist already, like so:
user_exists = User.objects.filter(username=self.USERNAME, email=self.EMAIL).exists()
if not user_exists:
self.authorize_user = User.objects.create_user....
That would explain why your first test did pass, why your second didn't, and why #anupam-chaplot's answer didn't reproduce the error.
Your reasoning and code looks ok.
However you are not giving the full code, there must be error you are not seeing.
Suspicious fact
It isn't be default 302 when you are not logged in.
(#login_required, etc redirects but your code doesn't have it)
Your APIAuthGroup permission does allow GET requests for non-logged-in user ( return request.method in SAFE_METHODS), and you are using GET requests (self.client.get(url))
So it means you are not hitting the endpoint that you think you are hitting (your get request is not hitting the DevicesViewSet method)
Or it could be the case you have some global permission / redirect related setting in your settings.py which could be DRF related..
eg :
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
Guess
url = reverse('device-detail', kwargs={'pk': device.pk})
might not point to the url you are thinking..
maybe there's another url (/sa/devices/1/) that overrides the viewset's url. (You might have a django view based url)
And I didn't address why you are getting redirected after force_login.
If it's indeed login related redirect, all I can think of is self.authorized_user.refresh_from_db() or refreshing the request ..
I guess some loggin related property (such as session, or request.user) might point to old instance .. (I have no evidence or fact this can happen, but just a hunch) and you better off not logging out/in for every test case)
You should make a seperate settings file for testing and add to the test command --settings=project_name.test_settings, that's how I was told to do.

Why would Django respond with differently to the same request

We've recently inherited a Django project which is new territory for me. In debugging an issue I've come across something I've never seen before in my 20 years of programming.
I have an API (written in Django) which has multiple end points (usual user profile stuff). When I make a GET request I get a response in the format I expect, however each time I make the request the subset of data is different.
I have attached 3 screenshots of the Postman request immediately following new user sign up, these are taken seconds apart with no changes to the data. I can continually make the requests and cycle through the 3 states.
This is the code for the endpoint:
class UserProfile(RetrieveUpdateDestroyAPIView):
serializer_class = SRUserSerializer
queryset = SRUser.objects
permission_classes = (IsAuthenticated,)
def retrieve(self, request, *args, **kwargs):
instance = request.user
instance.not_now_count = instance.not_now.all().count()
serializer = self.get_serializer(instance)
return Response(serializer.data)
The only thing I can assume is that the serializer is making additional data requests to the database but returning the data before they have necessarily returned. Would there be a way to identify this? What else can I provide to help identify the issue?
This is a Django 1.9 instance running on AWS using MySQL as a data store

How can I stop TastyPie doing UPDATE queries for no reason?

I'm seeing some usual goings on in my application. For no reason my server slows down when I have little or no traffic. After lots of trial and error I found my problems disappeared when I removed the ToOneField on my TastyPie resource!
What I found was for some unknown reason TastyPie is doing DB UPDATES on these ToOneFields for no good reason! What the... moment!
I found a possible bug filed here which claims to have fixed the update issue. I have installed the latest version from pip but still see this problem.
Can anyone help?
class IncentiveResource(ModelResource):
product_introducer = fields.ToOneField(ProductResource, 'referrer_product', full=True)
product_friend = fields.ToOneField(ProductResource, 'referee_product', full=True)
class Meta:
queryset = Incentive.objects.all().order_by('-date_created')
resource_name = 'incentive'
allowed_methods = ['get']
authentication = MultiAuthentication(ClientAuthentication(), ApiKeyAuthentication())
authorization = Authorization()
filtering = {
"active": ALL,
}
always_return_data = True
cache = SimpleCache(cache_name='resources', timeout=10)
So little traffic here but becomes unusable.
I don't know if this will help you, but I've seen a slight performance increase in an app I worked on while using select_related in the queryset and full=True in the resource field.
Try queryset = Incentive.objects.select_related('product_introducer', 'product_friend').all().order_by('-date_created')
Can you reproduce the sql UPDATEs in a testing environment?
If yes, here is how I would debug it:
Modify the source where the sql command gets executed: insert an assert statement that no update gets done.
If the assert fails, you have the stacktrace of the strange UPDATE.
If this stacktrace does not help you, post it here.

django-piston: properly formatting POST data from a client application

I have a Django app running django-piston for the first time.
I've got a handler configured for a model and it's rigged for GET and POST.
GET works fine. Tried and true.
POST however is sucking. When I post data to it, I am getting this error
Responded: Piston/0.2.3rc1 (Django 1.2.4) crash report:
Method signature does not match.
Resource does not expect any parameters.
Exception was: cannot concatenate 'str' and 'dict' objects
I'm really not a pro at Python but some basic Googling reveals that this seems to be a rather generic Python TypeError
So here's some code.
urls.py (relevant parts)
auth = DjangoAuthentication()
ad = { 'authentication': auth }
word_handler = Resource(handler = WordHandler, **ad)
urlpatterns = patterns(
url(r'^word/(?P<word_id>[^/]+)/', word_handler),
url(r'^words/', word_handler),
)
handlers.py (relevant parts)
class WordHandler(BaseHandler):
allowed_methods = ('GET','POST',)
model = Word
fields = ('string', 'id', 'sort_order', ('owner', ('id',)),)
def read(self, request, word_id = None):
"""
Read code goes here
"""
def create(self, request):
if request.content_type:
data = request.data
print("Words Data: " + data)
existing_words = Word.objects.filter(owner = request.user)
for word in data['words']:
word_already_exists = False
for existing_word in existing_words:
if word["string"] == existing_word.string:
word_already_exists = True
break
if not word_already_exists:
Word(string = word["string"], owner = request.user).save()
return rc.CREATED
Basically it's not even getting to create() at all, or at least it seems not to. It just craps out with the aforementioned error, so I can't even see how it's receiving the data.
And just for the hell of it, here's the data I am trying to POST
{"words":[{"owner":{"id":1,"tags":null,"password":null,"words":null,"email":null,"firstname":null,"lastname":null,"username":null},"id":1,"sort_order":0,"tags":null,"string":"Another Test"},{"owner":{"id":1,"tags":null,"password":null,"words":null,"email":null,"firstname":null,"lastname":null,"username":null},"id":2,"sort_order":0,"tags":null,"string":"Here's a test"},{"owner":null,"id":0,"sort_order":0,"tags":null,"string":"Tampa"}]}
Any information at all would be extremely helpful.
I solved this for myself.
The relevant cause of the error is this line in the create method
if request.content_type:
which does not evaluate as True as I think the creators of the django-piston documentation intended. Even if the value is a string with a value.
Removing this line solved it. And I'm sure you can probably just do a string evaluation.
Not really sure what they were thinking there.

Categories

Resources