I have two separate methods:
to load and validate a csv file FileUploadView(APIView) [PUT]
to add new objects to the database based on their uploaded file data
CsvToDatabase [POST]
file upload
class FileUploadView(APIView):
parser_classes = (MultiPartParser, FormParser)
permission_classes = (permissions.AllowAny,)
def put(self, request, format=None):
if 'file' not in request.data:
raise ParseError("Empty content")
f = request.data['file']
filename = f.name
if filename.endswith('.csv'):
file = default_storage.save(filename, f)
r = csv_file_parser(file)
status = 204
else:
status = 406
r = "File format error"
return Response(r, status=status)
create instances
class CsvToDatabase(APIView):
def post(self, request, format=None):
r_data = request.data
...
#some logic
...
serializer = VendorsCsvSerializer(data=data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
except ValidationError:
return Response({"errors": (serializer.errors,)},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(request.data, status=status.HTTP_200_OK)
urls.py
urlpatterns = [
path('csv_upload/', FileUploadView.as_view(), name='csv_upload'),
path('from_csv_create/', CsvToDatabase.as_view(), name='csv_vendor_create'),]
At the moment I am simply passing manually to Postman the result of the PUT method of FileUploadView class (Response(r, status=status) get me json ) to POST method of CsvToDatabase class.
Of course, this option is not suitable for a working application. I would like to know if there is a way to call the POST method automatically inside FileUploadView class after the .csv file is processed and json is received.
I know I can transfer a POST method to a class FileUploadView and call the self.POST method inside the PUT. But combining functionality will make testing much more complicated, so I want to use different url. If that's possible, of course.
Related
I have this code of 2 views in Django. You will notice that each REST API call has a verify_login() function call that ensures that the request contains a verified JWT token. I'm wondering if there's a better way to implement this so that I don't have to have these lines specifically in every REST endpoint
verify_response = verify_login(request)
if verify_response not None:
return verify_response
I'm trying to follow the D.R.Y. (Do Not Repeat Yourself) principle of coding. It'd be nice if there was a cleaner way to represent this. I thought about maybe creating a module extending APIView that automatically has this and then all my Views extend that, but runs into the issue of having to call super().API_REQUEST() and then having to do the same if-statement check to see if it's None or not.
class PostView(APIView):
"""
View for Post object
* requires token authentication
"""
# Create post
#swagger_auto_schema(
request_body=PostSerializer,
operation_description="Create a post object"
)
def post(self, request):
verify_response = verify_login(request)
if verify_response not None:
return verify_response
serializer = PostSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# get all posts
#swagger_auto_schema(
operation_description="Get all posts from the DB"
)
def get(self, request):
verify_response = verify_login(request)
if verify_response not None:
return verify_response
posts = Post.objects.all()
serializer = PostSerializer(posts, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
You can use authentication classes alongside permission classes. If you want the authentication check to happen for all the APIs of your application, put your classes in settings.REST_FRAMEWORK. If you want it for specific APIView's, put them in permission_classes & authentication_classes class variables. Check out the doc for more details.
Example,
class JWTAuthenticataion(BaseAuthentication):
def authenticate(self, request):
... # write your JWT implementation here
# settings.py:
REST_FRAMEWORK = {
...
"DEFAULT_AUTHENTICATION_CLASSES": (
"path.to.JWTAuthentication",
),
"DEFAULT_PERMISSION_CLASSES": (
"rest_framework.permissions.IsAuthenticated",
)
}
# or
# api.py
class YourAPIView(APIView):
permission_classes = (IsAuthenticated, )
authentication_classes = (JWTAuthentication, )
...
How to send the json data using post request in django python.
I have a code for get the data and I don't use any html file.
class employeeList(APIView):
def get(self, request):
employee1 = employees.objects.all()
serializer = employeeSerializer(employee1, many=True)
return Response(serializer.data)
def post(self,request):
pass
can you please help for post request.Now I want to create post function to send the data
You can use this basically,
def post(self, request):
serializer = employeeSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({'message':'Data davet succesfully'})
You can simply dump query set into Json format like:
def post(self, request):
data = list(employees.objects.values())
return JsonResponse(data, safe=False)
# or
return JsonResponse({'data': data})
I have sub-classed Generic DetialView class in views.py and trying to figure out a way to return data in JSON format based on an argument received in the url. Here's what I have tried doing...
# views.py
from django.views.generic import DetailView
from django.http import JsonResponse
class ExtendedView(DetailView):
context_object_name = 'post'
model = StorageModel
template_name='posts.html'
def get_context_data(self, **kwargs):
data = super(HacksViewPost, self).get_context_data(**kwargs)
if bool(self.request.GET):
data__ = JsonForm(request.GET)
if data__.is_valid():
json = data__.cleaned_data['json']
if json == 'true':
return JsonResponse({'data': 'data'})
return data
But this gave me TypeError as it should be:
TypeError at /category/extended-slug/
context must be a dict rather than JsonResponse.
The url that activates the ExtendedView class is:
/category/extended-slug?json=true
So, the question is how could i send data in JSON Format from a Generic View Class and are there any better ways of acheiving this?
I think you patch it at the wrong level. The get_context_data is used by the get function to render it. As a result, the get_context_data object has no control about what is done with the result, in order to construct a server response,
You can however patch the get(..) function like:
class ExtendedView(DetailView):
"""A base view for displaying a single object."""
def get(self, request, *args, **kwargs):
self.object = self.get_object()
data = self.get_context_data(object=self.object)
if self.request.GET:
data__ = JsonForm(request.GET)
if data__.is_valid():
json = data__.cleaned_data['json']
if json == 'true':
return JsonResponse({'data': data})
return self.render_to_response(data)
The same holds for post, put, and other requests.
If we take a look at the DetailView source code we see:
class BaseDetailView(SingleObjectMixin, View):
"""A base view for displaying a single object."""
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
Hence the get(..) calls the get_context_data(..) function. But it does not immediately returns the result, it wraps it into a rendered response.
You can't return a JsonResponse inside the get_context_data method. The get_context_data method allows you to send extra information (context) to the template and is expected to return a dict, not a JsonResponse.
If you want to return a JsonResponse, do that in the get or post method of your class.
I'm using django.rest_framework. I have a get_or_create method for a particular view,
class LocationView(views.APIView):
def get_or_create(self, request):
try:
location = Location.objects.get(country=request.data.get("country"), city=request.data.get("city"))
Response(location, status=status.HTTP_200_OK)
except Location.DoesNotExist:
serializer = LocationSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
This is the location Model,
class Location(models.Model):
country = models.CharField(max_length=255)
city = models.CharField(max_length=255, unique=True)
latitude = models.CharField(max_length=255)
longitude = models.CharField(max_length=255)
class Meta:
unique_together = ('country', 'city')
This is my url,
url(r'^location/$', LocationView.as_view(), name='location'),
When I call this endpoint in the following way,
http://127.0.0.1:8000/api/v1/bouncer/location/?country=USA&&city=Sunnyvale&&latitude=122.0363&&longitude=37.3688
This is what I get,
{
"detail": "Method \"GET\" not allowed."
}
What am I missing here.
The Method not allowed error is because, it searches for a get() method inside your API class, and it couldn't find a one.
The general format of the API class is as below
class LocationView(views.APIView):
def get(self, request):
#do something with 'GET' method
return Response("some data")
def post(self, request):
#do something with 'POST' method
return Response("some data")
If you want to invoke the get_or_create() method at some point, you can do it as any other methods,
class LocationView(views.APIView):
def get_or_create(self, request):
# do some "get or create" stuff
return "some data"
def get(self, request):
if condition:
self.get_or_create(request)
# do some stuff
return Response(" some special data related to get or create")
return Response("some data")
You need to provide separate methods for get and post. If your get method also creates an instance, then you can just call your current get_or_create inside both get and post methods.
class LocationView(views.APIView):
def get_or_create(self, request):
# your current definition here
def get(self, request):
return self.get_or_create(request)
def post(self, request):
return self.get_or_create(request)
For my situation, I was sending a request properly as my view would expect, a POST. But the issue was on http/s. I had set a permanent redirect, from http to https on my app, and so sending a POST to the http version resulted in a GET somehow when the server was redirecting.
TL;DR
HTTP instead of HTTPS
I am newbie on Django and I would like to implement a request that allow to make upload file.
I wrote some code for this, but when I opened the file in local, my computer says it may be damaged.
I don't understand why because the size file is the same to another when i sent with postman.
here is my code :
view.py
def handle_uploaded_file(f):
with open(f.name, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
class FileUploadView(APIView):
parser_classes = (FileUploadParser,)
def put(self, request, filename, format="png"):
file_obj = request.data['file']
handle_uploaded_file(file_obj)
return Response(filename, status.HTTP_201_CREATED)
Not sure if you're writing the files into the system manually but Django already has a way to handle the uploading of files - and DRF just builds on top of that. All you have to do is create a model with a FileField or any field that extends from it.
class Upload(models.Model):
user_upload = models.FileField(upload_to='path/to/upload')
Bear in mind that the database does not store the file - it only stores the path to the file. The file is directly uploaded to the path you've specified in the field. More info about upload_to here.
To upload using DRF - all you have to do is create a serializer using ModelSerializer and use a generic API view like CreateAPIView unless you have other requirements.
Your ModelSerializer can be something like:
class UploadFileSerializer(serializers.ModelSerializer):
class Meta:
model = Upload # reference the model above
fields = '__all__'
And in your views:
class UploadFileView(CreateAPIView):
serializer_class = UploadFileSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, files=request.FILES) # <------ note the request.FILES
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
That should do the trick!