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
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.
#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
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
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
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)