Django-Rest-Framework POST Object Field Required - python

I'm using djangorestframework (which I love) and I am attempting to POST some data from the front end to the REST view/serializer waiting to accept it.
When I log into the REST API back end (that django rest provides for users to be able to test their queries), I can submit this information, and it will successfully pass the information to the back end and save the object:
{
"user": 1,
"content": "this is some content",
"goal":
{
"competencies[]": [
32
],
"active": false,
"completed": false,
"user": 1
}
}
But, when I run the POST request, it fails, stating that:
{"goal": ["This field is required."]}
So that's interesting. It works from the back end, but not the front.
Here's my code for added help:
//the ajax request
$.ajax({
// obviates need for sameOrigin test
crossDomain: false,
//adds a CSRF header to the request if the method is unsafe (the csrfSafeMethod is in base.html)
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
//needed because we're setting data, I think.
type: "POST",
//target API url
url: '/api/goal-status/add',
data: this_instead,
//on success, reload the page, because everything worked
success: function(){
location.reload();
alert("In the goal-add click listener");
},
//we'll have to find something better to do when an error occurs. I'm still thinking through this. There will probably just have to be some standardized way of going about it.
error: function(){
alert('An error ocurred!');
}
});
And this is the view that is responding to the request:
class AddGoalStatus(generics.CreateAPIView):
serializer_class = GoalStatusSerializer
permission_classes = (
permissions.IsAuthenticated,
)
And the corresponding models:
class Goal(TimeStampedModel):
"""A personalized Goal that a user creates to achieve"""
completed = models.BooleanField(default=False)
user = models.ForeignKey(User)
competencies = models.ManyToManyField(CoreCompetency)
def __unicode__(self):
return self.user.get_full_name()
class GoalStatus(TimeStampedModel):
"""As goals go on, users will set different statuses towards them"""
content = models.TextField(max_length=2000)
goal = models.ForeignKey(Goal, related_name="goal_statuses")
def __unicode__(self):
return self.goal.user.get_full_name() + ": " + self.content
class Meta:
verbose_name_plural = "Statuses"
verbose_name = "Goal Status"
And here's the serializer for completeness:
class GoalSerializer(serializers.ModelSerializer):
competencies = serializers.PrimaryKeyRelatedField(many=True, read_only=False)
class Meta:
model = Goal
class GoalStatusSerializer(serializers.ModelSerializer):
goal = GoalSerializer()
class Meta:
model = GoalStatus

As Tom Christie says in here:
django rest framework create nested objects "Models" by POST
Django Rest Framework does not allow you to write to a nested serializer.
It looks like there is work being done on building out this functionality, but I don't know if it is done yet:
https://github.com/tomchristie/django-rest-framework/tree/writable-nested-modelserializer
In the meantime see this thread for ideas on how to work around this limitation:
https://groups.google.com/forum/#!topic/django-rest-framework/L-TknBDFzTk

Related

How to process Ajax Data in Python (Django)

I want to push front end data (Form inputs) to the server via Ajax. For this, I created an Ajax post request but I'm very unsteady...
At my first attemps, I constantly receive errors by python
Ajax call:
//Get journey time for the stated address
jQuery.ajax({
type: 'post',
url: 'http://127.0.0.1:8000/termin/get-journey-time/',
data: {
'method': 'get_journey_time',
'mandant_id': 1,
'customer_address': customer_address,
'staff_group': staff_group_id
},
error: function (jqXHR, textStatus, errorThrown) {
console.log("Error")
},
timeout: 120000,
});
I've created a view in Python, in which I want to do something (views.py)
class get_journey_time(generics.ListAPIView):
"""
Handle Ajax Post to calculate the journey time to customer for the selected staff group
"""
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
print(request)
In my url route file I have this code lines (urls.py)
urlpatterns = [
XXXXXXXXXXXXXXXXXXXXXXXXX,
path('termin/get-journey-time/', views.get_journey_time.as_view()),
XXXXXXXXXXXXXXXXXXXXXXXXX,
XXXXXXXXXXXXXXXXXXXXXXXXX,
]
I got the Error code 500:
Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view, but received a `<class 'NoneType'>`
Is there a mistake in my approach, did I miss anything or is it completely crap?
Define renderer classes and parser classes in settings.py.
Note: You can define many of these (based on requirements and needs) but here we only need JSON related.
As a reference, you can check my repo's this file https://github.com/hygull/p-host/blob/master/src/pmt_hostel_app/views.py. I have used function based views, just ignore the code inside it and focus on request.data and also check related HTML files.
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
)
}
In this way you will be able to access the posted data in the form of dictionary which can be obtained using request.data in views.
Finally, return Response from the post() method. By default return type of function/method is None and you are just printing the request.
Check the below mentioned links, it will help you a lot.
https://www.django-rest-framework.org/api-guide/renderers/
https://www.django-rest-framework.org/api-guide/parsers/
In client code, I mean in JavaScript code, define a success callback as well (you have just defined error callback).
Please comment, if you are stuck.
you can do it like this
from rest_framework.response import Response
from rest_framework.views import APIView
class get_journey_time(APIView):
# ListAPIView is used for read-only endpoints
#
"""
Handle Ajax Post to calculate the journey time to customer for the selected staff group
"""
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
# you can get the posted data by request.data
posted_data = request.data
data = {"test": "test"}
return Response(data)
you can get the posted data and use serializers. you can start learning playing with serializers from here
Example serializer code can be like this
from rest_framework import serializers
class DummySerializer(serializers.Serializer):
name = serializers.CharField()
mobile_number = serializers.CharField(required=False)
and then use it in post method of your get_journey_time class

Sending model updates from db to clients with WSGI

What is the most lightweight way to notify clients of changes to a model table they are viewing?
I've used Django Rest Framework to set up an API that serves a templated table of items to clients, and allows them to change buyers on the fly.
Currently, I use a recurring jQuery AJAX request with a setTimeout for 2 seconds. This sends a ton of requests and data even when there are no changes, and the webpage size keeps growing.
I've had to disable caching as some users might have IE11.
I started looking for a way to push the updates to the clients and started exploring Django Channels and Server-Sent-Events.
Django Channels
Built the demo chat app
Very fast
Websockets are supported by all my target browsers
Seems like overkill for what I'm trying to achieve.
Lots of configuration
Requires Redis or some other datastore
I don't really need the two way communication
My app is hosted on Pythonanywhere, which doesn't allow ASGI and
doesn't seem to have any plans to do so (1, 2).
Server Sent Events
Very little information on how to configure this for Django
No native support in IE11 or Edge, but there are polyfills available
Found and tested a working example from stackoverflow, not
exactly sure what it's doing though. The webpage is updated every 5 seconds, not sure where that is controlled.
Very little set up, seems almost magical
Seems like it would be ideal to use this with the post_save
signal in Django, but I can't figure out how to set this up.
Current, AJAX based set-up:
models.py
...
...
class Buyer(models.Model):
name = models.CharField(unique=True, max_length = 20)
class Item(models.Model):
name = models.CharField(unique=True, max_length = 50)
active = models.BooleanField(default=True)
bought_by = models.ForeignKey(Buyer, null=True, blank=True, to_field="name",)
views.py
...
...
class ItemViewSet(viewsets.ModelViewSet):
queryset = models.Item.objects.select_related("bought_by")
serializer_class= serializers.ItemSerializer
filterset_fields = ("bought_by")
renderer_classes = [renderers.JSONRenderer, renderers.BrowsableAPIRenderer, renderers.TemplateHTMLRenderer]
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
if request.accepted_renderer.format == "html":
items = list()
for item in queryset:
items.append({"serializer": self.get_serializer(item), "item": item})
return Response(
{
"items_info": items,
"style": {"template_pack": "rest_framework/inline/"},
},
template_name="myapp/items_list.html",
)
else:
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
...
...
(list method was modified to make each item editable by the clients)
handler.js
...
...
$.ajaxSetup({
cache: false
});
var tableUpdater = null;
var updateRequest = null;
// helper that can be called to cancel active timer/ajax in the
// case of interaction with buttons/selects on the page or
// in the case of a new request
function stopUpdate() {
if (tableUpdater || updateRequest) {
clearTimeout(tableUpdater);
updateRequest.abort();
}
}
// Update data table
function tableUpdate() {
stopUpdate();
updateRequest = $.ajax({
type: "GET",
url: "myapp/items/?format=html",
success: function(data) {
$("#activeRequests").html(data);
// schedule another AJAX request
tableUpdater = setTimeout(tableUpdate, 2000);
}
});
}
...
...
I had a go with Django Channels, but then switched to pusher.com That has worked very well for me, so I think it would be worth looking at for you.

Swagger API documentation

I saw swagger documentation of Flask and Django. In Flask I can design and document my API hand-written.(Include which fields are required, optional etc. under parameters sections).
Here's how we do in Flask
class Todo(Resource):
"Describing elephants"
#swagger.operation(
notes='some really good notes',
responseClass=ModelClass.__name__,
nickname='upload',
parameters=[
{
"name": "body",
"description": "blueprint object that needs to be added. YAML.",
"required": True,
"allowMultiple": False,
"dataType": ModelClass2.__name__,
"paramType": "body"
}
],
responseMessages=[
{
"code": 201,
"message": "Created. The URL of the created blueprint should be in the Location header"
},
{
"code": 405,
"message": "Invalid input"
}
]
)
I can chose which parameters to include, and which not. But how do I implement the same in Django? Django-Swagger Document in
not good at all. My main issue is how do I write my raw-json in Django.
In Django it automates it which does not allows me to customize my json. How do I implement the same kind of thing on Django?
Here is models.py file
class Controller(models.Model):
id = models.IntegerField(primary_key = True)
name = models.CharField(max_length = 255, unique = True)
ip = models.CharField(max_length = 255, unique = True)
installation_id = models.ForeignKey('Installation')
serializers.py
class ActionSerializer(serializers.ModelSerializer):
class Meta:
model = Controller
fields = ('installation',)
urls.py
from django.conf.urls import patterns, url
from rest_framework.urlpatterns import format_suffix_patterns
from modules.actions import views as views
urlpatterns = patterns('',
url(r'(?P<installation>[0-9]+)', views.ApiActions.as_view()),
)
views.py
class ApiActions(APIView):
"""
Returns controllers List
"""
model = Controller
serializer_class = ActionSerializer
def get(self, request, installation,format=None):
controllers = Controller.objects.get(installation_id = installation)
serializer = ActionSerializer(controllers)
return Response(serializer.data)
My questions are
1) If I need to add a field say xyz, which is not in my models how do I add it?
2) Quiet similar to 1st, If i need to add a field which accepts values b/w 3 provided values,ie a dropdown. how do I add it?
3) How I add an optional field? (since in case of PUT request, I might only update 1 field and rest leave it blank, which means optional field).
4) Also how do I add a field that accepts the json string, as this api does?
Thanks
I can do all of these things in Flask by hardcoding my api. But in Django, it automates from my models, which does not(as I believe) gives me the access to customize my api. In Flask, I just need to write my API with hands and then integrate with the Swagger. Does this same thing exist in Django?
Like I just need to add the following json in my Flask code and it will answer all my questions.
# Swagger json:
"models": {
"TodoItemWithArgs": {
"description": "A description...",
"id": "TodoItem",
"properties": {
"arg1": { # I can add any number of arguments I want as per my requirements.
"type": "string"
},
"arg2": {
"type": "string"
},
"arg3": {
"default": "123",
"type": "string"
}
},
"required": [
"arg1",
"arg2" # arg3 is not mentioned and hence 'opional'
]
},
Would this work:
class TriggerView(APIView):
"""
This text is the description for this API
mykey -- My Key parameter
"""
authentication_classes = (BasicAuthentication,)
permission_classes = (IsAuthenticated,)
def post(self, request, format=None):
print request.DATA
return Response(status=status.HTTP_202_ACCEPTED)
the POST request:
Authorization:Basic YWRtaW46cGFzcw==
Content-Type:application/json
{"mykey": "myvalue"}

Django form with dynamic fields and manytomany relationschips

I am a beginner Django user and have been stuck with this problem for weeks now.
Consider the following model
class Post(models.Model):
title = models.CharField(max_length=65)
author = models.ForeignKey(User)
date = models.DateField(auto_now_add=True)
content = models.TextField()
tags = models.ManyToManyField(Tag)
images = models.ManyToManyField(Image)
def __unicode__(self):
return self.title
I would like the User to be able to upload pictures to the with his model. I would like to be able to validate if
The user has at least uploaded one image and limit the maximum number of images to a certain number.
Basically do the same for tags. But with tags I would also like Django to check if the tags already exists and if so add it to the model.
Let the user push a button that says add Tag / add Image to make a new field pop up.
Problems I have encountered so far.
I am required to save a model before I can add many to many relations. This is a bit annoying because if an error occurs halfway down the road is might be possible that half the model is saved and the other is invalid.
I know that I can add extra field to the DOM using js however I got no clue how to process those in the View function.
For problem 1, this makes sense because if you create a model which is refered to by other models, said model key must exist. What I'd suggest you do is look into saving multiple models with a transaction.
2 is pretty easy, just use jQuery/Javascript to show/hide the appropriate fields in the browser based on the user's selection event.
Based on your comment, here's an example of how I handle data to and from the server
//Submit data to server, assuming you have already extracted out the relevant data values
$("some_button").click(function(e){
$.ajax({
url : "someUURLLocation/",
type : "POST",
data : {"data" : JSON.stringify({"field1" : var1, "field2" :var2}),
dataType : "json",
success : function(results){
if (results.success == "true") {
//handle DOM insertion and other stuff
} else
alert(results.message);
}
});
}
urls.py:
from django.conf.urls import patterns, url
from $APPNAME import views
urlpatterns = patterns("",
...
...
url(r'^someURLLocation/$', views.handleDataSubmission),
...
)
views.py:
from django.http import HttpResponse
from django.utils import simplejson
def handleDataSubmission(request):
data = simplejson.loads(request.POST.get("data", None))
if data is not None:
newPost = Post.objects.create( field1 = data["field1"], field2 = data["field2"])
dataReturn = [{"val1" : newPost.title, "val2" : newPost.date}]
return HttpResponse(simplejson.dumps({"success" : "true", "data" : dataReturn}), mimetype = "application/json")
else:
return HttpResponse(simplejson.dumps({"success" : "false", "message" : "Invalid data received by server"}), mimetype = "application/json")

Get 404 error when using tastypie filtering

I'm trying to use tastypie filtering but when I try to get a resource through filtering I receive a 404.
code
class UserResource(ModelResource):
class Meta:
queryset = UsersCouchDb.objects.all()
resource_name = 'users/list'
fields = ['firstName', 'lastName', 'gender','status','date']
always_return_data = True
authorization= Authorization()
filtering = {
"firstName": ('exact', 'startswith'),
}
I'm using urlopen to access the resource:
info= urllib2.urlopen('http://127.0.0.1:8000/api/users/list/&firstName__exact=David').read()
How do I make it so I don't get a 404 when trying to get a resource through filtering with Tastypie?
If there are no results in a list that you query on, TastyPie would just send back Json with zero elements (something like the following):
{
meta: {
limit: 20,
next: null,
offset: 0,
previous: null,
total_count: 0
},
}
So it seems that if you're getting a 404, you don't have something set up correctly.
The following things could resolve your issue:
Make sure you have ?format=json appended to your url before the &
Make sure you've registered the APIResource
Make sure you've set up the appropriate urls.py if anything is different.

Categories