I successfully implement with small cases. Then I started to work with bigger structure. And I got the error.
Error:
No file was submitted.
import tempfile
from unittest import skip
from django.conf import settings
from django.contrib.auth.models import User
from django.core.files import File
from django.core.files.uploadedfile import SimpleUploadedFile
from model_mommy import mommy
from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.test import APITestCase, APIClient
class CustomerFromExcelViewsetTest(APITestCase):
def setUp(self):
self.client = APIClient()
self.soken_staff = mommy.make(User, username='spearhead')
self.user = mommy.make(User, username='Justin')
settings.MEDIA_ROOT = tempfile.mkdtemp()
def test_upload_file(self):
"""Expect created_user, and updated_user correct set"""
file = File(open('./soken_web/apps/uploaded_files/complete-customer.xlsx', 'rb'))
uploaded_file = SimpleUploadedFile('new_excel.xlsx', file.read(), content_type='multipart/form-data')
data = {
file: uploaded_file,
}
self.client.force_authenticate(user=self.user)
response = self.client.post(reverse('api:customer_from_excel-list'), data, format='multipart')
response.render()
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
Here they are the models, serializers, and viewsets
models.py https://gist.github.com/elcolie/52daf2bd144af82b348f7353656be434
serializers.py
https://gist.github.com/elcolie/7f097642c4a752e76044c6938c49e097
viewsets.py
https://gist.github.com/elcolie/34fa66632209f14624899d997919d3fb
After a day I could not figure out where is the that bug.
References:
DRF APITestCase not use `multipart` with other param
looks like you missed quotes in data dict. It should be:
data = {
'file': uploaded_file,
}
Related
How can I verify the incoming webhook from Shopify? Shopify provides a python implementation (of Flask), but how can I do it in Django/DRF?
Set these two variables in the settings.py file
# settings.py
SHOPIFY_HMAC_HEADER = "HTTP_X_SHOPIFY_HMAC_SHA256"
SHOPIFY_API_SECRET = "5f6b6_my_secret"
Then, create a verify webhook function that accepts the Django request as it's parameter
# utils.py
import base64
import hashlib
import hmac
from django.conf import settings
from django.core.handlers.wsgi import WSGIRequest
def verify_shopify_webhook(request: WSGIRequest):
shopify_hmac_header = request.META.get(settings.SHOPIFY_HMAC_HEADER)
encoded_secret = settings.SHOPIFY_API_SECRET.encode("utf-8")
digest = hmac.new(
encoded_secret,
request.body,
digestmod=hashlib.sha256,
).digest()
computed_hmac = base64.b64encode(digest)
return hmac.compare_digest(computed_hmac, shopify_hmac_header.encode("utf-8"))
Then, create a view that accepts the incoming webhook and use the verify_shopify_webhook(...) function to verify the request.
# views.py
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from .utils import verify_shopify_webhook
#method_decorator(csrf_exempt, name="dispatch")
class ShopifyWebhookView(View):
def post(self, request, *args, **kwargs):
verified = verify_shopify_webhook(request=request)
return HttpResponse(status=200 if verified else 403)
If you're using Django REST Framework, you can also use APIView as
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .utils import verify_shopify_webhook
class ShopifyWebhookView(APIView):
def post(self, request, *args, **kwargs):
verified = verify_shopify_webhook(request=request)
return Response(status=200 if verified else 403)
I need to restrict access to the APIs I have defined in my view. Here is my views.py:
rom rest_framework import generics
from rest_framework import permissions
from .serializers import LocationSerializer, PartSerializer, PartLocationSerializer, SiteSerializer
from .models import Location, Part, PartLocation, Site, SPIUser
class SPIPermission(permissions.BasePermission):
"""
blah blah blah ...
"""
def has_permission(self, request, view):
try:
username = request.user.username
SPIUser.objects.get(username=username)
except SPIUser.DoesNotExist:
return False
if not request.user.is_authenticated:
return False
return True
class LocationList(generics.ListCreateAPIView):
# using get_queryset().order_by('id') prevents UnorderedObjectListWarning
queryset = Location.objects.get_queryset().order_by('id')
serializer_class = LocationSerializer
permission_classes = (SPIPermission,)
I want to demonstrate in my unit tests that your have to be an SPIUser to be able to access these api endpoints so I write a simple unit test like so:
from .models import Location, Part, PartLocation, Site, SPIUser
from .urls import urlpatterns
from my.APITestCase import RemoteAuthenticatedTest
from django.db.models import ProtectedError
from django.test import TransactionTestCase
from django.urls import reverse
from rest_framework import status
import django.db.utils
import os
class ViewTestCases(RemoteAuthenticatedTest):
def test_spi_permission(self):
url = reverse('spi:locationlist')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
SPIUser.objects.create(username=self.username)
response = self.client.get(url)
self.assertNotEquals(response.status_code, status.HTTP_403_FORBIDDEN)
This test fails with the this error message:
Failure
Traceback (most recent call last):
File "/apps/man/apman/spi/tests.py", line 21, in test_spi_permission
self.assertNotEquals(response.status_code, status.HTTP_403_FORBIDDEN)
AssertionError: 403 == 403
I noticed that the line in has_permission ...
username = request.user.username
... always sets the username to ''. So has_permission will always return False.
My unit test ViewTestCases inherits class RemoteAuthenticatedTest which is defined like so:
from rest_framework.test import APIClient,APITestCase
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
class RemoteAuthenticatedTest(APITestCase):
client_class = APIClient
def setUp(self):
self.username = 'mister_neutron'
self.password = 'XXXXXXXXXXX'
self.user = User.objects.create_user(username= self.username,
email='mister_neutron#example.com',
password=self.password)
#authentication user
self.client.login(username=self.username, password=self.password)
Token.objects.create(user=self.user)
super(RemoteAuthenticatedTest, self).setUp()
So I thought that request.user.username would be mister_neutron.
What am I doing wrong here?
Ah heck. I forgot that I am using RemoteUser authentication so when I make my I need to set REMOTE_USER like so:
response = self.client.get(url, REMOTE_USER=self.username)
I am trying to upload a file from postman to s3 and getting error on
k.set_contents_from_filename(file)
TypeError: invalid file:
Can you please take look? Thanks a lot.
serializers.py
from rest_framework import serializers
class ResourceSerializer(serializers.Serializer):
file = serializers.FileField(required=True, max_length=None, use_url=True)
name = serializers.CharField(required=True, max_length=500)
views.py
import logging
from boto.s3.key import Key
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import ResourceSerializer
from .utils import create_boto_connection
from django.conf import settings
class Resource(APIView):
def post(self, request, format=None):
serializer = ResourceSerializer(data=request.data)
if serializer.is_valid():
context = {}
file = serializer.validated_data['file']
name = serializer.validated_data['name']
ext = file.name.split('.')[-1]
new_file_name = '{file}.{ext}'.format(file=name, ext=ext)
file_name_with_dir = 'profile_photos/{}'.format(new_file_name)
# Create s3boto connection
conn = create_boto_connection()
try:
bucket = conn.get_bucket(settings.AWS_STORAGE_BUCKET_NAME)
k = Key(bucket)
k.key = file_name_with_dir
k.set_contents_from_filename(file)
k.make_public()
context['file'] = new_file_name
except Exception as e:
context['message'] = 'Failed to process request'
# Logging Exceptions
logging.exception(e)
logging.debug("Could not upload to S3")
return Response(context, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
utils.py
from boto.s3.connection import S3Connection
from django.conf import settings
def create_boto_connection():
conn = S3Connection(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY)
# conn = boto.connect_s3()
return conn
urls.py
from django.conf.urls import url
from s3boto import views
urlpatterns = [
# v1
url(r'^v1/s3boto/upload-resource/$', views.Resource.as_view(), name="upload-resource"),
]
postman:
You are passing a django InMemoryUploadedFile to the method set_content_from_filename, which expects a string.
From the boto documentation:
set_contents_from_filename(filename, headers=None, replace=True, cb=None, num_cb=10, policy=None, md5=None, reduced_redundancy=False, encrypt_key=False)
Store an object in S3 using the name of the Key object as the key in
S3 and the contents of the file named by ‘filename’. See
set_contents_from_file method for details about the parameters.
Either use set_content_from_file or save the file to a local temporary file and pass that filename to set_content_from_filename.
I am very new with django framework.
# Create your views here.
import urllib2
import json
import urllib
from .models import Apiclass
from django.shortcuts import render_to_response
from django.conf import settings as config
def home(request):
obj = Apiclass()
def postme(request):
url = config.API_PROTOCOL+config.API_DOMAIN+config.API_SECURE_USER_URL
# user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
values = {'username' : 'waheeda#auction.com',
'password' : '12345678'
}
# headers = { 'Content-Type' : "application/json" }
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
the_page = json.loads(response.read())
return render_to_response("home.html",{'postme':the_page})
And ApiClass is under models/Apiclass.py
I tried many ways to import the model ApiClass
such as
- from app1.models import Apiclass
- from .models import Apiclass
but it still does not work it gave me the errors like this
TypeError at /api
'module' object is not callable
Apiclass.py
I just comment all the implementation when ever I can create object that everything should be good. Here is the Apiclass code
import urllib2
import urllib
import json
from django.conf import settings as config
# Create your models here.
class Apiclass:
api_domain = config.API_DOMAIN
Your should have class Apiclass inside models.py. Also I recommend you to import from full module path .i.e from YOUR_APP.models import Apiclass
If you want folder structure:
models/
__init__.py
and inside __init__.py you could have Apiclass and import as from YOUR_APP.models import Apiclass as well.
Also make sure it is subclassed from django.db.models.Model if it touches database in any way as recommended below.
You haven't subclassed your ApiClass model correctly. It should subclass django Model
from django.db import models
class ApiClass(models.Model):
pass
It's barfing because you are trying to call your class with this:
Apiclass()
but ApiClass doesn't currently have a call method.
Hi I'm siting with my custom storage system 1 day. And now when I'm trying import it it gives me this Error.
I put in file models.py
from FTPStorage import FTPStorage
import datetime
from django.db import models
fs=FTPStorage()
class Upload(models.Model):
"""Uploaded files."""
file = models.FileField(upload_to='uploads', store=fs)
timestamp = models.DateTimeField(default=datetime.datetime.now)
notes = models.CharField(max_length=255, blank=True)
class Meta:
ordering = ['-timestamp',]
def __unicode__(self):
return u"%s" % (self.file)
#property
def size(self):
return filesizeformat(self.file.size)
here is my views.py:
from forms import UploadForm
from models import Upload
import ftplib
import os
import datetime
from django.forms import save_instance
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.http import HttpResponse
from FTPStorage import FTPStorage
from django.core.files.storage import Storage
def initial(request):
data = {
'form': UploadForm(),
}
return render_to_response('upload.html', data, RequestContext(request))
def upload(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
upload = Upload()
upload.timestamp = datetime.datetime.now()
save_instance(form, upload)
return HttpResponseRedirect(reverse('initial'))
and file custom storage system FTPStorage.py is in direectory app
I have this problem:
Request Method: GET
Request URL: http://localhost:2121/
Exception Type: ViewDoesNotExist
Exception Value:
Could not import app.views. Error was: cannot import name FTPStorage
Exception Location: C:\BitNami DjangoStack\apps\django\django\core\urlresolvers.py in _get_callback, line 134
Please help me. I confuse.
It seems to me that you need to update the PYTHONPATH for your runtime. Based on your error page I think you're using mod_python so try this setting in apache:
PythonPath "sys.path+['/mydir']"
Where /mydir is the full path to wherever the FTPStorage module resides.