Django Download csv after post request - django

I want to create an endpoint, in this endpoint its possible to send POST request, if the POST request
is validated, then the page download a csv
I created the serializer form to make a easy validation of the data received
My problem is that the csv its easy downloaded in a HttpResponse, but i need neccesary to make a endpoint and a validation of the data in a post request.
My principal problem is that i can´t return the export function next of the validation
This are my files
#urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^hello-view/', views.HelloApiView.as_view()),
]
Serializers
#serializers.py
from rest_framework import serializers
class HelloSerializer(serializers.Serializer):
"""Serializes a name field """
name = serializers.CharField(max_length=100)
seller_id = serializers.CharField(max_length=100)
def validate_name(self, dob):
UnitOfMeasureName = ["Each", "Grams", "Ounces", "Pounds", "Kilograms", "Metric Tons"]
if dob in UnitOfMeasureName:
return dob
else:
raise serializers.ValidationError('Wrong username')
And the views files
In this file i created the export function to try to export the csv data, but doesnt works
import csv
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework import viewsets
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from . import serializers
class HelloApiView(APIView):
def export(self, request):
response = HttpResponse(content_type='text/csv')
writer = csv.writer(response)
writer.writerow(['First name', 'Last name', 'ID'])
response['Content-Disposition'] = 'attachment; filename="one.csv"'
return response
serializer_class = serializers.HelloSerializer
def get(self, request, format=None):
an_apiview = [
'Uses HTTP METHOD as function',
'Its is similar to a traditional django view'
]
return Response({'message': 'Hello', 'anapi': an_apiview})
def post(self, request):
serializer = serializers.HelloSerializer(data = request.data)
if serializer.is_valid():
in this place i want to return the file
else:
return Response(
serializer.errors, status = status.HTTP_400_BAD_REQUEST)

Add a new endpoint to your urls.py file
#urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^hello-view/', views.HelloApiView.as_view()),
url(r'^csv_download/$', views.csv_download, name="csv_download"),
]
Then in your views.py file, add a function called csv_download and move all of your stuff there (This might be unnecessary, but it sure makes for cleaner, more readable code)
# views.py
import csv
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework import viewsets
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from . import serializers
class HelloApiView(APIView):
def get(self, request, format=None):
an_apiview = [
'Uses HTTP METHOD as function',
'Its is similar to a traditional django view'
]
return Response({'message': 'Hello', 'anapi': an_apiview})
def csv_download(request):
if request.method == 'POST':
serializer_class = serializers.HelloSerializer
serializer = serializers.HelloSerializer(data = request.data)
if serializer.is_valid():
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="one.csv"'
writer = csv.writer(response,delimiter=',') # I always like to specify the delimeter
writer.writerow(['First name', 'Last name', 'ID'])
#Then you may actually want to write some data to the CSV file, currently, you've only defined the headers (first row). An example would be like:
for value in list_of_objects:
writer.writerow([
value.first_name,
value.last_name,
value.id
])
return response
else:
return Response(
serializer.errors, status = status.HTTP_400_BAD_REQUEST)
else:
# redirect them or do something here if they hit this URL without a POST request

Related

Django Testing for Login and token collection

Trying to write test for Django login. I want to request to login, collect token and access different view functions. How do I write a test in python for this.
It depends on your authentication system, if you use Django default auth, you can just use its helper method login(**credentials) or force_login https://docs.djangoproject.com/en/4.1/topics/testing/tools/#django.test.Client.login
If you use Django Rest Framework with additional 3rd auth system you can use its testing class
from rest_framework.test import APITestCase
class TestMyProtectedView(APITestCase):
def setUp(self) -> None:
self.client.login(user, password)
def test_view(self):
# now you can access your view with logged-in user in setUp
response = self.client.get('/api/views/')
Just like described in Django and DRF documentations. It would be something like:
tests.py
from django.test import TestCase
from django.urls import reverse
from django.contrib.auth import get_user_model
from rest_framework.authtoken.models import Token
from rest_framework.test import APIClient
from rest_framework import status
# Create your tests here.
class UserLoginTestCase(TestCase):
def setUp(self):
self.client = APIClient()
self.admin_user = get_user_model().objects.create_superuser(
email='admin#example.com',
password='testpass123',
)
def test_user_create_or_collect_token(self):
"""User check token created if in post save signal format"""
token = Token.objects.get(user__email=self.admin_user.email)
self.assertTrue(token.key)
def test_user_authenticated(self):
"""Check if user is authenticated"""
token = Token.objects.get(user__email=self.admin_user.email)
self.client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)
r = self.client.get(reverse('user:me'))
self.assertEqual(r.status_code, status.HTTP_200_OK)
def test_user_login(self):
"""test user login"""
url = reverse('user:login')
data = {
'username': 'admin#example.com',
'password': 'testpass123'
}
r = self.client.post(url, data, format='json')
self.assertEqual(r.status_code, status.HTTP_200_OK)
self.assertTrue(r.data['token'])
urls.py
from .api.views import CustomAuthToken, UserAuthenticated
from django.urls import path
app_name = 'user'
urlpatterns = [
path('login/', CustomAuthToken.as_view(), name='login'),
path('me/', UserAuthenticated.as_view(), name='me'),
]
views.py
from rest_framework.views import APIView
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework import authentication, permissions
from core.models import User
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_name': user.name,
'email': user.email
})
class UserAuthenticated(APIView):
queryset = User.objects.all()
authentication_classes = [authentication.TokenAuthentication]
permission_classes = [permissions.IsAdminUser]
def get(self, request):
""" Check if user is authenticated by checking token against db """
is_authenticated = True
return Response({'isAuthenticated': is_authenticated})
Usually, tests are written for both positive and negative outcomes.
Note that in this case in particular I have a custom user model, and also extended the built-in ObtainAuthToken view.

serializer import error in django rest-framework

#serializers.py
from rest_framework import serializers
class CommunitysStaffSerializer(serializers.ModelSerializer):
role = serializers.CharField(max_length=100,default='management')
community = serializers.PrimaryKeyRelatedField(read_only=True)
user = serializers.PrimaryKeyRelatedField(read_only=True)
#views.py
from rest_framework.response import Response
from admindashboard.serializers import CommunitysStaffSerializer
#api_view(['POST'])
def Communitystaff(request,pk=None):
if request.method == 'POST':
serializer = CommunitysStaffSerializer(data = request.data)
if serializer.is_valid():
Staff = CommunityStaff.objects.get(id = request.data['id'])
Staff.community=serializer.validated_data['community']
Staff.role=serializer.validated_data['role']
Staff.save()
return Response({'detail': 'entry successfully'})
else:
return Response({'error': 'unable to entry staff'}, status=status.HTTP_400_BAD_REQUEST)
else:
return Response (serializer.data)
#error
File "C:\Users\Pinakee.Somyadarshee\Desktop\mykommunity\admindashboard\urls.py", line 2, in
from admindashboard.views import Login_post,City_post,City_list,City_update,City_remove,Community_update,Community_list,Community_post,Community_detail,Admindash_list,Admindash_update,Admindash_remove,Admindash_post,Admindash_detail,Communitystaff
File "C:\Users\Pinakee.Somyadarshee\Desktop\mykommunity\admindashboard\views.py", line 11, in
from admindashboard.serializers import CommunitysStaffSerializer
ImportError: cannot import name 'CommunitysStaffSerializer' from 'admindashboard.serializers' (C:\Users\Pinakee.Somyadarshee\Desktop\mykommunity\admindashboard\serializers.py)
If serializers.py and views.py are in the same folder, use:
from .serializers import CommunitysStaffSerializer
Anyway, if you are using serializer.ModelSerailizer, you should define the model or, otherwise, you could use other serializer:
https://www.django-rest-framework.org/api-guide/serializers/#modelserializer

I am Unable to Send My email in django backend app

When i can send post request for contact view so try block can't run and except block should be run.
Here i cant unable to send mail.
Here error in send_mail function.
So please Help me.
In Here my setting.py code
settings.py
EMAIL_BACKEND='django.core.mail.backend.smtp.EmailBackend'
EMAIL_HOST='smtp.gmail.com'
EMAIL_PORT=587
EMAIL_HOST_USER='kalp2002prajapati#gmail.com'
EMAIL_HOST_PASSWORD='******'
EMAIL_USE_TLS = True
EMAIL_USE_SSL = 0
view.py
from rest_framework import permissions
from rest_framework.views import APIView
from .models import Contact
from django.core.mail import
send_mail,EmailMessage
from rest_framework.response import Response
class ContactCreateView(APIView):
permission_classes = (permissions.AllowAny, )
def post(self, request, format=None):
data = self.request.data
print(data)
try:
send_mail(
data['subject'],
'Name: '+ data['name']
+ '\nEmail: '+data['email']+'\nMessage:'+ data['message'],
'kalpprajapati2002#gmail.com',
['kalp2002prajapati#gmail.com'],
fail_silently=False
)
contact = Contact(name=data['name'], email=data['email'], subject=data['subject'], message=data['message'])
contact.save()
return Response({'success': 'Message sent successfully'})
except:
return Response({'error': 'Message failed to send'})
urls.py
from django.urls import path
from .views import ContactCreateView
urlpatterns = [
path('', ContactCreateView.as_view())
]
models.py
from django.db import models
from datetime import datetime
class Contact(models.Model):
name=models.CharField(max_length=200)
email=models.CharField(max_length=100)
subject=models.CharField(max_length=100)
message=models.TextField(blank=True)
contact_date=models.DateTimeField(default=datetime.now,blank=True)
def __str__(self):
return self.email
just delete all the self from the view, you need only the request.
if this doesn't make your code work, post the error you are getting

How to return actual file data instead of file URL from Django Rest Framework FileField object

I am trying to build a simple API that returns XML file data to the client. I am using Django 3.0.4 and Django Rest Framework 3.11.0 and have based most of my work on this tutorial https://www.django-rest-framework.org/tutorial/1-serialization/
This is my model:
from django.db import models
class File(models.Model):
file = models.FileField(blank=False, null=False)
device = models.CharField(max_length=20, primary_key=True)
class Meta:
verbose_name_plural = "API"
def __str__(self):
return self.file.name
This is the views:
from django.shortcuts import render
from rest_framework.parsers import FileUploadParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from .serializers import FileSerializer
from .models import File
class FileList(APIView):
"""
List all files, or create a new file.
"""
def post(self, request, format=None):
serializer = FileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def get(self, request, format=None):
files = File.objects.all()
serializer = FileSerializer(files, many=True)
return Response(serializer.data)
class FileDetail(APIView):
"""
Retrieve, update or delete a file instance.
"""
def get(self, request, pk, format=None):
file = File.objects.get(pk=pk)
serializer = FileSerializer(file)
return Response(serializer.data)
This is the App URLs:
from django.urls import path
from .views import FileList
from .views import FileDetail
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('<str:pk>', FileDetail.as_view()),
path('', FileList.as_view()),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
I have this in SETTINGS:
STATIC_URL = '/static/'
MEDIA_URL = '/path/to/file:devices/'
MEDIA_ROOT = os.path.join(BASE_DIR, "path/to/file:devices")
Everything works well, I do not see any errors. I am able to POST a file and GET files (either as a list or as an individual file, based on pk) using Postman.
The problem I have is that when I GET an individual file I get this:
{
"file": "/path/to/file:devices/example.xml",
"device": "example.xml"
}
I want the response to be the file contents (actual XML data) not the URL to the file.
Any ideas would be appreciated.
OK. I figured this out for myself. Pretty simple really.
All I had to do was redirect the GET to the URL of where the actual file is stored in the file structure.
Here is the new FileDetail class view:
class FileDetail(APIView):
"""
Get or delete a file instance.
"""
def get(self, request, pk, format=None):
print("hitting detail get")
response = redirect(os.path.join(settings.MEDIA_URL, (pk +'.xml')))
return response

How do you return 404 when resource is not found in Django REST Framework

When a user inputs a url that is wrong, my Django app returns an HTML error. How can I get DRF to return a json formatted error?
Currently my urls is
from django.conf.urls import url
from snippets import views
urlpatterns = [
url(r'^snippets/$', views.snippet_list),
url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]
but if a user goes to 127.0.0.1:8000/snip They get the html formatted error rather than a json formatted error.
Simply way to do it, you can use raise Http404, here is your views.py
from django.http import Http404
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from yourapp.models import Snippet
from yourapp.serializer import SnippetSerializer
class SnippetDetailView(APIView):
def get_object(self, pk):
try:
return Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data, status=status.HTTP_200_OK)
You also can handle it with Response(status=status.HTTP_404_NOT_FOUND), this answer is how to do with it: https://stackoverflow.com/a/24420524/6396981
But previously, inside your serializer.py
from rest_framework import serializers
from yourapp.models import Snippet
class SnippetSerializer(serializers.ModelSerializer):
user = serializers.CharField(
source='user.pk',
read_only=True
)
photo = serializers.ImageField(
max_length=None,
use_url=True
)
....
class Meta:
model = Snippet
fields = ('user', 'title', 'photo', 'description')
def create(self, validated_data):
return Snippet.objects.create(**validated_data)
To test it, an example using curl command;
$ curl -X GET http://localhost:8000/snippets/<pk>/
# example;
$ curl -X GET http://localhost:8000/snippets/99999/
Hope it can help..
Update
If you want to handle for all error 404 urls with DRF, DRF also provide about it with APIException, this answer may help you; https://stackoverflow.com/a/30628065/6396981
I'll give an example how do with it;
1. views.py
from rest_framework.exceptions import NotFound
def error404(request):
raise NotFound(detail="Error 404, page not found", code=404)
2. urls.py
from django.conf.urls import (
handler400, handler403, handler404, handler500)
from yourapp.views import error404
handler404 = error404
Makesure your DEBUG = False
from rest_framework import status
from rest_framework.response import Response
# return 404 status code
return Response({'status': 'details'}, status=status.HTTP_404_NOT_FOUND)
An easier way is to use get_object_or_404 method in django:
as described in this link:
get_object_or_404(klass, *args, kwargs)
- Calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.
- klass: A Model class, a Manager, or a QuerySet instance from which to get the object.
As an example, pay attention to
obj = get_object_or_404(Snippet, pk=pk)
return obj
in the following code:
from django.shortcuts import get_object_or_404
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
class SnippetDetail(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
obj = get_object_or_404(Snippet, pk=pk)
return obj
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
...
Or simply, you can use the same structure of DRF, without losing I18N and keep the same DRF error message:
from rest_framework import viewsets, status, exceptions
from rest_framework.decorators import action
from rest_framework.response import Response
try:
codename = get_or_bad_request(self.request.query_params, 'myparam')
return Response(self.get_serializer(MyModel.objects.get(myparam=codename), many=False).data)
except MyModel.DoesNotExist as ex:
exc = exceptions.NotFound()
data = {'detail': exc.detail}
return Response(data, exc.status_code)