How can I accept Json object from post request? - django

I'm trying to receive a post from a server in the format:
{
"event_type": "test",
"event_info": {
"key": "value",
"more unknown key":"Value pairs"...
}
}
Then serialize it into a sqlite db.
I'm not finding a good way to do this. It doesn't like {} I get returned "Not a valid string." I haven't found a good way to accept the json object. I could just store the json as a string but I can't figure out how to convert it to a string either.
I have views.py:
from django.shortcuts import render
from .models import Subscription
from .Serializers import SubscriptionSerializer
from rest_framework import viewsets
from django.http import HttpResponse
class SubscriptionViewSet(viewsets.ModelViewSet):
queryset = Subscription.objects.all().order_by('-date_created')
serializer_class = SubscriptionSerializer
http_method_names = ['get', 'post', 'delete']
models.py
from __future__ import unicode_literals
from django.db import models
class Subscription(models.Model):
event_type = models.TextField(default='Failed to set')
event_info = models.TextField(default='Failed to set')
date_created = models.DateTimeField(auto_now=True)
Serializers.py
from rest_framework import serializers
from .models import Subscription
class SubscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Subscription
fields = ('id', 'event_type', 'event_info')

Try using nested serializer:
class KeySerializer(serializers.Serializer):
key = serializers.CharField(max_length=30)
class EventSerializer(serializers.Serializer):
event_type = serializers.CharField(max_length=30)
event_info = KeySerializer()
EventSerializer should be able to parse/validate json you posted.

Related

Django get the current email of the user by default in a different serializer based on the selected "userid"

I was wondering what the correct way is to get the current email of the user by default in a different serializer based on the selected "userid".
I have tried many examples from the ModelSerializer docs but without success.
serializers.py
from rest_framework import serializers
from ticker.models import Ticker
from users.models import NewUser
from rest_framework.permissions import IsAuthenticated
from alerts.models import SectionAlerts
from users.serializers import UserlistSerializer
from rest_framework.fields import CurrentUserDefault
class TickerSerializer(serializers.ModelSerializer):
class Meta:
model = Ticker
fields = "__all__"
class UserlistSerializer(serializers.ModelSerializer):
class Meta:
model = NewUser
fields = "__all__"
class AlertsSerializer(serializers.ModelSerializer):
ticker = TickerSerializer(read_only=True)
email = UserlistSerializer(read_only=True)
ticker_id = serializers.SlugRelatedField(
queryset=Ticker.objects.all(), source="ticker", slug_field='crypto', write_only=True
)
class Meta:
model = SectionAlerts
fields = "__all__"
models.py
from django.db import models
from ticker.models import Ticker
from django.conf import settings
from import_export.resources import ModelResource
from import_export.fields import Field
from users.models import NewUser
from django.core.mail import EmailMessage
class SectionAlerts(models.Model):
id = models.AutoField(primary_key=True) # auto increment field
valuenow = models.FloatField(null=True, blank=True, default=None)
valuealarm = models.FloatField(null=True, blank=True, default=None)
user = models.CharField(max_length = 40,blank=True, null=True)
userid = models.ForeignKey(
NewUser, related_name='userid',
blank=True,
null=True,
on_delete=models.CASCADE,
)
email = models.ForeignKey(NewUser, blank=True, null=True, on_delete=models.CASCADE)
ticker = models.ForeignKey(Ticker, blank=True, null=True, on_delete=models.CASCADE)
The goal: anytime that the "Current Value" of a crypto is lower than the previously set "Value Alert", the backend is sending an email notification to the user.
The problem: at the moment all users receive that email notification, while just each individual logged in user that created
their own alerts should receive it.
Question: how can i add just the email of the user that created the individual alert?
views.py
from email import message
from rest_framework import generics
from rest_framework import status
from .serializers import AlertsSerializer, TickerSerializer, UserlistSerializer
from users.serializers import CustomUserSerializer
from .models import SectionAlerts, Ticker, NewUser
import requests
import logging
from itertools import chain
from importlib import reload
import sys
import csv
from django.http import HttpResponse
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.settings import api_settings
from rest_framework.response import Response
from .utils import Util
from django.urls import reverse
from django.shortcuts import redirect
class CreateSectionAlerts(generics.CreateAPIView):
def perform_create(self, serializer):
serializer.save(user=self.request.user)
print("New alert is saved")
serializer_class = AlertsSerializer
class ListSectionAlerts(generics.ListAPIView):
queryset = SectionAlerts.objects.all()
serializer_class = AlertsSerializer
# ------- EMAIL STARTS HERE
def get_queryset(self):
print("****ALERTS START get_querysetdavid")
queryset = SectionAlerts.objects.filter(userid=self.kwargs["pk"])
for b in SectionAlerts.objects.filter(userid=self.kwargs["pk"]):
# print("b---Print just mail", b.email)
print("b---Print SectionAlerts", b.id, b.valuenow, b.userid, b.ticker.c_0)
for c in NewUser.objects.all():
if b.ticker.c_0 < b.valuealarm:
print("Alert is achieved and must be sent")
email_body = 'Hi ' + c.user_name + ', you received this message because the value of the crypto ' + str(b.ticker) + ' is now ' + str(b.ticker.c_0) +'€.' + ' and reached the alert value of ' + str(b.valuealarm) +'€.' + '\n'+ 'This email confirms this event. \n' + 'Click here https://www.cryptothlon.com to signin in your account and know more. \n' +'\n'+ 'Your Cryptothlon Team'
data={'email_body': email_body, 'to_email': c.email,
'email_subject': 'Crypto alert'}
Util.send_email(data)
print("Email sent")
try:
record = SectionAlerts.objects.get(id = b.id )
record.delete()
print("Record deleted successfully!")
except:
print("Record doesn't exists")
# email method ends here
return queryset
# ------- EMAIL ENDS HERE
class DeleteSectionAlerts(generics.RetrieveDestroyAPIView):
queryset = SectionAlerts.objects.all()
serializer_class = AlertsSerializer
You can use SerializerMethodField. It will allow you to create a custom field in the serializer. By default SerializerMethodField looks for a method get_<field name>, and performs the according logic:
from users.models import NewUser
class AlertsSerializer(serializers.ModelSerializer):
...
email = serializers.SerializerMethodField()
class Meta:
model = SectionAlerts
fields = "__all__"
def get_email(self, obj):
user_id = self.initial_data['userid'] # get the `userid` from the request body
user = NewUser.objects.get(pk=user_id) # fetch the user from DB
return UserlistSerializer(instance=user).data
You don't show the NewUser model so not sure exactly what field you need, but you can use dot notation for the source of a serializer field.
From DRF docs:
The name of the attribute that will be used to populate the field. May be a method that only takes a self argument, such as URLField(source='get_absolute_url'), or may use dotted notation to traverse attributes, such as EmailField(source='user.email').
See this section of the docs: https://www.django-rest-framework.org/api-guide/fields/#core-arguments

How to POST multiple data in DRF and React with Axios

I have 2 models named Recipe and Step..
I have serialized both to make an API for GET request.. I want to know is there a way to create for POST request so that I can send both the data (steps and recipe) in the same request?
models.py:
from django.db import models
class Recipe(models.Model):
title = models.CharField( max_length=50)
uuid = models.CharField( max_length=100)
def __str__(self):
return f'{self.uuid}'
class Step(models.Model):
step = models.CharField(max_length=300)
uuid = models.ForeignKey(Recipe, on_delete=models.CASCADE)
def __str__(self):
return f'{self.step} - {self.uuid}'
serializers.py:
from rest_framework import serializers
from .models import *
class RecipeSerializer(serializers.ModelSerializer):
class Meta:
model = Recipe
fields = ['title', 'uuid']
class StepSerializer(serializers.ModelSerializer):
class Meta:
model = Step
fields = ['step', 'uuid']
views.py:
from django.shortcuts import render
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .serializers import *
from .models import *
#api_view(['GET'])
def apiOverview(request):
api_urls = {
'List':'/recipe-list/',
'Detail View':'/recipe-detail/<str:pk>/',
'Create':'/recipe-create/',
'Update':'/recipe-update/<str:pk>/',
'Delete':'/recipe-delete/<str:pk>/',
'Steps' : '/steps/<str:pk>'
}
return Response(api_urls)
#api_view(['GET'])
def recipeList(request):
recipes = Recipe.objects.all()
serializer = RecipeSerializer(recipes, many=True)
return Response(serializer.data)
#api_view(['GET'])
def recipeDetail(request, pk):
recipe = Recipe.objects.get(uuid=pk)
recipe_serializer = RecipeSerializer(recipe, many=False)
steps = Step.objects.filter(uuid=pk)
steps_serializer = StepSerializer(steps, many=True)
return Response({
'recipe' : recipe_serializer.data,
'steps' : steps_serializer.data
})
How can I create a view for POST and handle both the models?
Try:
from rest_framework import generics
from .models import *
class StepAndRecipe(generics.CreateAPIView):
queryset = Step.objects.all()
queryset = Recipe.objects.all()
serializer_class = StepSerializer
serializer_class = RecipeSerializer
Add in urls.py:
from django.urls import path
from .views import StepAndRecipe
urlpatterns = [
path('steprecipepost', StepAndRecipe.as_view(), name='steps_recipes')
This will only work with POST! And one more thing: take care with the raw data and the HTML form, maybe theses get a little confused since you are using two models in the same view.

How to upload a CSV file in Django using the REST API?

How can I send a 220,000-line CSV file to Django using the Rest Framework API? Thank you.
#Botta- How about something like this
from django.db import models
Create your models here.
class MyFile(models.Model):
file = models.FileField(blank=False, null=False,upload_to='images/')
description = models.CharField(null=True,max_length=255)
uploaded_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'MyFiles'
class MyFileView(APIView):
# MultiPartParser AND FormParser
# https://www.django-rest-framework.org/api-guide/parsers/#multipartparser
# "You will typically want to use both FormParser and MultiPartParser
# together in order to fully support HTML form data."
parser_classes = (MultiPartParser, FormParser)
def post(self, request, *args, **kwargs):
file_serializer = MyFileSerializer(data=request.data)
if file_serializer.is_valid():
file_serializer.save()
return Response(file_serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
from rest_framework import serializers
from .models import MyFile
class MyFileSerializer(serializers.ModelSerializer):
class Meta:
model = MyFile
fields = ('file', 'description', 'uploaded_at')
#Stacy Hi. I made the changes, but it didn't work. I would like the fields in the CSV file to go automatically to my models and to the REST API. I would like to import my CSV data when uploading the file. Thanks.
models.py
from django.db import models
class MyFile(models.Model):
file = models.FileField(blank=False, null=False,upload_to='images/')
description = models.CharField(null=True,max_length=255)
uploaded_at = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=150)
sex = models.CharField(max_length=150)
age = models.CharField(max_length=50)
height = models.CharField(max_length=150)
def __str__(self):
return self.description
class Meta:
verbose_name_plural = 'MyFiles'
serializers.py
from rest_framework import serializers
from .models import MyFile
class MyFileSerializer(serializers.ModelSerializer):
class Meta:
model = MyFile
fields = ("file", "description", "uploaded_at", "name", "sex", "age", "height",)
#Stacy- Hello. I was able to upload the csv file and pass it to my API as .CSV, however, I would like all fields in my CSV file to be transferred to API Rest. For example ID, Name, Age and Profile. It's possible? Thank you. Thank you.
serializers.py
from rest_framework import serializers
from .models import MyFile
class MyFileSerializer(serializers.ModelSerializer):
class Meta:
model = MyFile
fields = ('file', 'description', 'uploaded_at')
models.py
from django.db import models
class MyFile(models.Model):
file = models.FileField(blank=False, null=False,upload_to='images/')
description = models.CharField(null=True,max_length=255)
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.description
class Meta:
verbose_name_plural = 'MyFiles'
views.py
from rest_framework.parsers import MultiPartParser, FormParser
from .models import MyFile
from .serializers import MyFileSerializer
from rest_framework import generics
from rest_framework import viewsets
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.exceptions import NotFound
from rest_framework.response import Response
from rest_framework.views import APIView
class MyFileViewSet(viewsets.ModelViewSet):
queryset = MyFile.objects.all()
serializer_class = MyFileSerializer
urls.py
from .views import MyFileViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'', MyFileViewSet)
urlpatterns = router.urls

How to get username of user's post in Django REST API instead of number?

I'm trying to get username for iOS app through REST API.
I could get user number.
How do I get actual username?
The "author" should be username of user post.
http://127.0.0.1:8000/api/posts/
Result
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"author": 1,
"title": "Test Title Post",
"contents": "Test contents post"
}
models.py
User = settings.AUTH_USER_MODEL
class PostDetail(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="PostDetail.author+")
title = models.CharField('*Title', max_length=50)
contents = models.TextField('*Contents', max_length=450)
serializer.py
from rest_framework import serializers
from .models import PostDetail
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
class PostDetailSerializer(serializers.ModelSerializer):
class Meta:
model =PostDetail
fields = (author, title, 'contents', )
apis.py
from rest_framework import viewsets, routers
from blog.models import PostDetail
from blog.serializer import PostDetailSerializer
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
class PostViewSet(viewsets.ModelViewSet):
queryset = PostDetail.objects.all()
serializer_class = PostDetailSerializer
router = routers.DefaultRouter()
router.register(r'posts', PostViewSet)
I expect "author": 1, to be like "author": admin,.
You need to change your PostDetailSerializer to:
from rest_framework import serializers
from .models import PostDetail
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
class PostDetailSerializer(serializers.ModelSerializer):
author = serializers.CharField(source='author.username', read_only=True)
class Meta:
model =PostDetail
fields = (author, title, 'contents', )
You can use SerializerMethodField for this task. You need to define a function to get the username of the author.
This is how you do it:
class PostDetailSerializer(serializers.ModelSerializer):
author = serializers.SerializerMethodField()
class Meta:
model =PostDetail
fields = (author, title, 'contents', )
def get_author(self, obj):
return obj.author.username
The function should be named as get_<field_name>.
if you have to use for input value to, you can create the custom serializer fields
class ForeignKeyField(serializers.RelatedFields):
def to_representation(self, obj):
return obj.name
this fields can be use in the serializer
class PostDetailSerializer(serializers.ModelSerializer):
author = ForeignKeyField()
class Meta:
model =PostDetail
fields = (author, title, 'contents', )

How to test DRF serializer when using additional keyword arguments?

I have a simple model and a serializer which uses additional keyword arguments(extra_kwargs) to limit max and min value for a field. I want to write two test cases in my tests.py to both test valid and invalid values for that field (weight). How should I do this?
I am using Python 2.7.
Model
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=100)
weight = models.FloatField()
Serializer
from rest_framework import serializers
from .models import Person
class PersonSerializer(serializers.ModelSerializer):
model = Person
fields = ('name', 'weight')
extra_kwargs = {'weight': {'min_value': 5, 'max_value': 1000}}
tests.py
from django.test import TestCase
from .models import Person
class ModelTestCase(TestCase):
def setUp(self):
self.person = Person(name='Sam', weight=150)
def test_when_weight_isValid(self):
#e.g. when weight = 200
def test_when_weight_notValid(self):
#e.g. when weight = 3
import json
from django.urls import reverse
from django.contrib.auth import authenticate
from rest_framework import status
from rest_framework.test import APITestCase
from .models import *
class UserTests(APITestCase):
def setUp(self):
self.person = Person(name='Sam', weight=150)
def test_when_weight_isvalid(self):
url = reverse('person-detail', kwargs={'pk': self.person.id})
data = {'weight': 200}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
print json.dumps(response.data, ensure_ascii=False, indent=2)
Something like this,code is untest.