Within my CreateView and UpdateView I am using a form for the main model and multiple forms to directly create/update related models:
class MyModelCreateView(CreateView):
model = MyModel
form_class = MyModelForm
MyModelForm instantiates the required forms for the related fields I mentioned within itself.
My problem is that when I serialize the data and send it to the view, it doesn't know how to handle the data from the extra forms. When I access to request.POST this data gets discarded. I am serializing the forms like this:
let data = $('#main-form').serializeArray();
$('.other-form').each(function() {
data.push({name: 'id-' + $(this).data('id'), value: $(this).serializeArray()});
});
This sends the following array to the server (I stringified it here for a clear display):
[
{
"name": "name",
"value": "some name"
},
{
"name": "id-194",
"value": [
{
"name": "prop1",
"value": "on"
},
{
"name": "prop2",
"value": "some prop"
},
{
"name": "prop3",
"value": "other prop"
}
]
},
{
"name": "id-195",
"value": [
{
"name": "prop2",
"value": "some prop"
},
{
"name": "prop3",
"value": "other prop"
}
]
}
]
However the contents of request.POST are these:
<QueryDict: {u'name': [u'some name']}>
Notice how all other data is ignored. I can get to send it to the server the following way:
let data = $('#main-form').serializeArray();
$('.other-form').each(function() {
data.push({name: 'id-' + $(this).data('id'), value: $(this).serialize()});
});
But this produces the following:
<QueryDict: {u'id-195': [u'prop1=on&prop2=some+prop&prop3=other+prop'], u'id-194': [u'displayable=on&prop2=some+prop&prop3=other+prop']}
Which is of course not what we want because all we get is a string, not prepared data. We need a dictionary I believe to initialize the forms appropriately.
This is my ajax function:
$.ajax({
method: 'POST',
url: editURL,
data: data,
success: function (html) {
$('#mydiv').html(html);
}
});
jQuery's serializeArray method generates a data structure suitable for converting to JSON. But you haven't done that, you're trying to send it as form-encoded data.
You should probably make it simpler by actually sending JSON, and parsing that at the other end.
$.ajax({
method: 'POST',
url: editURL,
data: JSON.stringify(data),
contentType: 'application/json',
success: function (html) {
$('#mydiv').html(html);
}
});
...
class MyModelCreateView(CreateView):
model = MyModel
form_class = MyModelForm
def get_form_kwargs(self):
return {'data': json.loads(request.body)}
Note, I don't know what you are doing in your form to "instantiate the required forms for the related fields", but that's probably the wrong way to do it; you should be using a form and an inline related formset. Although, since this is Ajax posting to Django, it may be even better to switch to Django REST Framework and use serializers for this logic; those can deal easily with nested objects.
You are going to want to use Formsets. If the forms on your page are all the same form (IE you are just trying to make multiple records in the same model at once or whatever) you will probably find formset_factory quite useful.
Related
I am fairly new to python and mongodb and I'm facing an issue already.
I am trying to "translate" a nodejs backend restapi into flask, using mongodb as a data source.
Using the flask documentation, I was able to configure my app in order to connect to my local mongod.
And I am able to obtain values from the users collection like this
def getUser():
usr = Users.objects(email="mail#mail.com")
return {
"user": usr,
}
Which returns the following JSON when I'm calling the API through Postman
{
"user": [
{
"__v": 0,
"_id": {
"$oid": "5da86dc651eac87d2a82e2e2"
},
"createdAt": {
"$date": 1571319238918
},
"email": "mail#mail.com",
"password": "$2b$10$hoH57R5GL1MrwqpuW4yEJ.wwLlyNgyfxQm2Mxb19wioYTPPsU9z7y",
"profil": {
"_id": {
"$oid": "5da86dc651eac87d2a82e2e3"
},
"checked": false,
"clapList": [],
"followerList": [],
"followingList": [],
"playpoint": 0
},
"updatedAt": {
"$date": 1571319477959
}
}
]
}
As you can see, I have an array with one user in it. When I try to get only one object, like this:
def getUser():
usr = Users.objects(email="mail#mail.com").first()
return {
"user": usr,
}
I have a 500 status returned in Postman and the following error in my debug console: mongoengine.errors.FieldDoesNotExist: The fields "{'__v'}" do not exist on the document "Users"
This is my Users model
import mongoengine as me
class Users(me.Document):
phone = me.StringField()
email = me.StringField()
password = me.StringField()
accountType = me.StringField()
createdAt = me.DateTimeField()
updatedAt = me.DateTimeField()
profil = me.EmbeddedDocumentField(Profil)
I have already tried adding __v as an InfField(), but I still have the same error.
What is that __v anyway and should I retry making a new database from scratch?
Additional info:
The mongodb database and collection was generated using the nodejs API
The users in the database were generated using the nodejs API
So I added a meta property to it and I'm now able to use the class
meta = {
'strict': False,
}
I don't really know yet what transpired in there but I'm not touching anything if it works
I try to return a list of select options for countries using django-countries and django rest framework. I use JWT_AUTH for the authentication.
When I try a options request:
curl \
-H "Authentication: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFsYmVydG9fdmVudGEiLCJ1c2VyX2lkIjoyLCJlbWFpbCI6IiIsImV4cCI6MTUwODE2Mzg4Mn0.svxqTThCahSl1Vu27sMjuJyd1PRLk28-Xgn2OKKb5-g"\
-X OPTIONS \
-v http://127.0.0.1:8000/api/v1/core/perfilViajeroUserPass/
The response is:
{
"name":"Perfil Viajero User Pass Create",
"description":"",
"renders":["application/json","text/html"],
"parses":[
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
]
}
But I think that it should be something like this by default:
{
"name": "To Do List",
"description": "List existing 'To Do' items, or create a new item.",
"renders": [
"application/json",
"text/html"
],
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"actions": {
"POST": {
"note": {
"type": "string",
"required": false,
"read_only": false,
"label": "title",
"max_length": 100
}
}
}
}
Someone could help me? thanks.
If you want to change some of the content:
name is the view's get_view_name which is the view's name slightly reworked.
description is the view's get_view_description which reworks the view's docstring.
Otherwise if you want something more complex, you'll probably want to customize the view's metadata as explained in http://www.django-rest-framework.org/api-guide/metadata/#custom-metadata-classes
I have found the solution.
I change my view class type from APIView to generics.CreateAPIView and know it works. Thank you very much.
Adding another answer since I recently ran into the same issue and found it a bit mystifying -- when making an OPTIONS request, Django Rest Framework uses the view's Metadata class to construct a response. The default Metadata class is SimpleMetadata, as mentioned in the docs. However, SimpleMetadata only adds the actions key to the response body if the view in question defines the method get_serializer(). I'm not sure why this is the case, but see here for the relevant code.
rest_framework.generics.GenericAPIView defines a get_serializer() method, so (authenticated) OPTIONS requests made to these views will return a response body with the actions key. But rest_framework.views.APIView does not define this method, so the actions key will always be absent.
If you have to use rest_framework.views.APIView, you could work around this by defining a get_serializer() method on your APIView class. Which feels a little hacky, but I tested it and it works:
class MyView(views.APIView):
def get_serializer(self):
return MySerializer()
def post(self):
...
I tried to disable concurrency-control on Eve and trying to add another new id_field: "new_field", but I am not able to make it work. I look through various post in StackOverflow but still I am not able to fix it. Could someone please help?
I disabled : IF_MATCH = False in the global config and:
schema = {
"node_name": {
"type": "string",
"unique": True,
"required": True,
"node_type": {
"type": "string",
"required": True,
}
}
Config:
config = {
'item_title': 'new_title',
'additional_lookup': {
'url': 'regex("[\w]+")',
'field': 'node_name',
},
'id_field': "node_name",
'schema': schema,
}
And here is the url i am trying to send PATCH request:
url: http:localhost:5000/api/v1/resource/end_point/
Here
resource: my resource name
end_point: id_field value.
Could someone please help.
Have you enabled PATCH for your resource? By default, document and collection endpoints are read-only. Try adding the following to your configuration:
ITEM_METHODS = ['GET', 'PATCH', 'DELETE', 'PUT']
Also, you don't want to set an additional_lookup on the same field serving as id_field, as additional lookups are read-only.
Since you are not using a ObjectID as your unique key, you also probably need to change the default URL for the document endpoint. Try setting ITEM_URL to the correct regex:
ITEM_URL: URL rule used to construct default item endpoint URLs. Can be overridden by resource settings. Defaults regex("[a-f0-9]{24}") which is MongoDB standard Object_Id format.
It seems that performing a PATCH on an endpoint with a many-to-many relation updates the object but doesn't return back the updated data until the next response vs returning it back in the PATCH response.
Example with original object:
{
"id": 35,
"interests": [
1,
2
],
"personal_statement": "Hello World",
"photo": "",
"resume": "",
"user": 2
}
PATCH request setting interests=[1,2,3,4,5] ... Example response:
{
"id": 35,
"interests": [
1,
2
],
"personal_statement": "Hello World",
"photo": "",
"resume": "",
"user": 2
}
Example of next GET Response:
{
"id": 35,
"interests": [
1,
2,
3,
4,
5
],
"personal_statement": "Hello World",
"photo": "",
"resume": "",
"user": 2
}
This is using Django v1.7.4 and Django REST Framework v2.4.3
My first assumption is that since it's a many to many relation it is saving the parent object first and returning back that data before saving the many to many relation data, but I'm not entirely sure. Any help would be appreciated. Thanks.
EDIT
The issue is actually an open issue on Django REST Framework with some possible solutions. It was being caused by prefetch_related in my ViewSet queryset:
https://github.com/tomchristie/django-rest-framework/issues/1556
https://github.com/tomchristie/django-rest-framework/issues/2442
According to REST API patterns, PATCH request performs a partial update, so Django-Rest-Framework just returns updated data.
May be you want to override PATCH view to return complete serializer data or change your PATCH request to PUT one.
I encountered the same while adding prefetch_related to a many to many field I had. I solved it by simply adding a custom update to my serializer:
def update(self, instance, validated_data):
super(SimpleClientModelResource, self).update(instance, validated_data)
return self.Meta.model.objects.get(id=instance.id)
It seems to me that there should be an automatic way to query the results of a Django Rest Framework call and operate it like a dictionary (or something similar). Am I missing something, or is that not possible?
i.e.,
if the call to http://localhost:8000/api/1/roles/
yields
{"count": 2, "next": null, "previous": null, "results": [{"user": {"username": "smithb", "first_name": "Bob", "last_name": "Smith"}, "role_type": 2, "item": 1}, {"user": {"username": "jjones", "first_name": "Jane", "last_name": "Jones"}, "role_type": 2, "item": 1}]}
I would think something akin to http://localhost:8000/api/1/roles/0/user/username should return smithb.
Does this functionality exist or do I need to build it myself?
It appears to be something you will have to build yourself. That said Django makes this kind of thing very easy. In URLS you can specify parts of the url path to pass to the view. You can catch the values using regex and then pass them into your views function.
Urls:
url(regex=r'^user/api/1/roles/(?P<usernumber>\w{1,50})/(?P<username>\w{1,50})/$', view='views.profile_page')
a request for http://domain/user/api/1/roles/0/username/
View:
def someApiFunction(request, usernumber=None ,username=None):
return HttpResponse(username)
Some additional Resources:
https://docs.djangoproject.com/en/1.7/intro/tutorial03/#writing-more-views
Capturing url parameters in request.GET