Django Doesn't Serialize id of the model - python

I have a simple class :
class Item(models.Model):
name = models.CharField(max_length=250)
I need to serialize it to JSON with the ID as part of the JSON, but the ID never gets serialized when called in the view:
items_json = serializers.serialize('json', item_list)
The item_list json as received by the browser contains only name, not the ID.
I need the ID for the processing on the client side which returns to the server later.
1) I'd like to avoid creating another ID if possible
2) I haven't created a Serializer Class. I am not sure where and how to do that.

I found the solution. The pk field that is sent over is the id. The id will now show up in the fields. So in javascript you should do:
$.each( $.parseJSON(data), function(key, val) {
console.log("ID: " + val.pk)
});

Related

Python flask Graphene: Mapping fields with API response

I'm building a graphql api using python flask and python graphene.
basically my json file data looks like following.
{
"address":{
"streetAddress":"301",
"#city":"Los Angeles",
"state":"CA"
}
}
And my graphene schema looks like follow.
class Address(ObjectType):
streetAddress = String()
city = String()
state = String()
class Meta:
exclude_fields = ('#city',)
class Common(ObjectType):
data = Field(Address)
def resolve_data(self, info):
data = open("address.json", "r")
data_mod = json.loads(data.read())["address"]
return data_mod
So I am trying to map this #city json key value to my schema field called city.
I saw one of the articles and in that, it mentioned that using the meta class we can exclude original field name like this.
class Meta:
exclude_fields = ('#city',)
Still it didn't work. And I am using a common schema to fetch the json data to Address schema fields by using one resolver. Can someone tell me a solution to map these kind of fields to graphene schema fields.

In Django-Rest-Framework POST call which updates manytomany field works with JSON but not with form-data

I want to run a POST call that creates a "group". Assume that "persons" will all be existent. Even if they aren't, an error is not a problem.
class Group(models.Model):
title = models.CharField(max_length=70)
persons = models.ManyToManyField(to=Person, blank=True)
file = models.FileField(upload_to=file_location, null=True, blank=True)
class GroupSerializer(serializers.ModelSerializer):
persons = serializers.PrimaryKeyRelatedField(many=True, queryset=Person.objects.all())
class Meta:
model = Group
fields = '__all__'
If I send a JSON like
{
"title": "Drama Club",
"persons": [1,2,3]
}
it will work. But since I cannot upload a file, I use FORM-DATA.
title: Drama Club
persons: [1,2,3]
file: <whatever the format is>
Now here comes the problem. IT DOES NOT WORK. It returns this error
{
"persons": [
"Incorrect type. Expected pk value, received str."
]
}
Even if I remove everything else, and just send persons: [1,2,3] as form-data, it returns the same error.
I really cannot understand this behavior. (I am using POSTMAN to check this)
It turns out that form-data does not take an array. So, instead of
persons : [1,2,3]
I will have to send
persons : 1
persons : 2
persons : 3
Django-rest-framework will do the rest.
Putting this answer here, because it took me a very long time to figure it out.
(if there is a way to send an array in form-data, without special parsing on the backend, I would love to know)
If you want to upload a file and send the JSON payload as well, take a look at DRF MultipartParser

How to post to a Django REST Framework API with Related Models

I have two related models (Events + Locations) with a serialzer shown below:
class Locations
title = models.CharField(max_length=250)
address = model.CharField(max_length=250)
class Events
title = models.CharField(max_length=250)
locations = models.ForeignKey(Locations, related_name='events'
class EventsSerializer(serializers.ModelSerializer):
class Meta:
model = Events
depth = 1
I set the depth to 1 in the serializer so I can get the information from the Locations model instead of a single id. When doing this however, I cant post to events with the location info. I can only perform a post with the title attribute. If I remove the depth option in the serializer, I can perform the post with both the title and location id.
I tried to create a second serializer (EventsSerialzerB) without the depth field with the intention of using the first one as a read-only response, however when I created a second serializer, viewset, and added it to the router, it would automatically override the original viewset.
Is it possible for me to create a serializer that outputs the related model fields, and allows you to post directly to the single model?
// EDIT - Here's what I'm trying to post
$scope.doClick = function (event) {
var test_data = {
title: 'Event Test',
content: 'Some test content here',
location: 2,
date: '2014-12-16T11:00:00Z'
}
// $resource.save() doesn't work?
$http.post('/api/events/', test_data).
success(function(data, status, headers, config) {
console.log('sucess', status);
}).
error(function(data, status, headers, config) {
console.log('error', status);
});
}
So when the serializers are flat, I can post all of these fields. The location field is the id of a location from the related Locations table. When they are nested, I can't include the location field in the test data.
By setting the depth option on the serializer, you are telling it to make any relation nested instead of flat. For the most part, nested serializers should be considered read-only by default, as they are buggy in Django REST Framework 2.4 and there are better ways to handle them in 3.0.
It sounds like you want a nested representation when reading, but a flat representation when writing. While this isn't recommended, as it means GET requests don't match PUT requests, it is possible to do this in a way to makes everyone happy.
In Django REST Framework 3.0, you can try the following to get what you want:
class LocationsSerializer(serializers.ModelSerializer):
class Meta:
model = Locations
fields = ('title', 'address', )
class EventsSerializer(serializers.ModelSerializer):
locations = LocationsSerializer(read_only=True)
class Meta:
model = Events
fields = ('locations', )
class EventViewSet(viewsets.ModelViewSet):
queryet = Event.objects.all()
serializer_class = EventsSerializer
def perform_create(self, serializer):
serializer.save(locations=self.request.data['locations'])
def perform_update(self, serializer):
serializer.save(locations=self.request.data['locations'])
A new LocationsSerializer was created, which will handle the read-only nested representation of the Locations object. By overriding perform_create and perform_update, we can pass in the location id that was passed in with the request body, so the location can still be updated.
Also, you should avoid having model names being plurals. It's confusing when Events.locations is a single location, even though Locations.events is a list of events for the location. Event.location and Location.events reads a bit more clearly, the Django admin will display them reasonably, and your fellow developers will be able to easily understand how the relations are set up.

Google App Engine return object with reference set

Hi I am kind of trying to get the concept behind DataStore as a No-SQL database, what I am trying to fetch is a list of object wich have been "reference" by another. As this
class Person(db.Model):
name = db.StringProperty(required=True)
class Contact(db.Model):
name = db.StringProperty(required=True)
email = db.StringProperty()
trader = db.ReferenceProperty(Person)
This works fine and they get to be saved when I use person.put() without any problem. But when I try to retrieve it and encoded as json it nevers shows me the contact as a list in fact it totally ignores it.
persons_query = Person.all()
persons = persons_query.fetch(50)
data = json.encode(persons)
I would expect person to have a collection of Contact but it doesn't any ideas on how to solve this problem?
To make it clearer currently i am getting something like this:
[
{
name: "John Doe"
}
]
I would like to be
[
{
name: "John Doe"
contacts: [{name:"Alex", email:'alex#gmail.com'}]
}
]
Edit
Thanks all you were right I needed to fetch the collection of contacts there was only one issue for this is that when Contact was being encoded it recursively tried to encode the Trader object and this it's contact and so on.
So I got an obvious error recursive error, the solution to this was clearly to remove the trader object from the Contact when it's being encoded.
Make a custom toJson function in your class
class Person(db.Model):
name = db.StringProperty(required=True)
def toJson(self):
contact = self.contact_set #this is the default collection name for your class
d = {"name":self.name,"contact":contact}
return json.dumps(d)
class Contact(db.Model):
name = db.StringProperty(required=True)
email = db.StringProperty()
trader = db.ReferenceProperty(Person)
then you may do the ff:
persons_query = Person.all()
persons = persons_query.fetch(50)
data = person.toJson()
To fetch all the contacts you will need to write a custom json encoder, which fetches all of the reverse of the reference property.
ReferenceProperties automatically get a reverse query. From the docs "collection_name is the name of the property to give to the referenced model class. The value of the property is a Query for all entities that reference the entity. If no collection_name is set, then modelname_set (with the name of the referenced model in lowercase letters and _set added) is used."
So you would add a method to resolve the reverse reference set query.
class Person(db.Model):
name = db.StringProperty(required=True)
def contacts(self):
return self.contact_set.fetch(50) # should be smarter than that
Then use it in your custom json encoder.
If you want to find all the contacts that include a person you will need to issue a query for it.
contacts = Contact.all().filter("trader =", person)

Serializing SQLAlchemy models for a REST API while respecting access control?

Currently, the way our, as well as most web frameworks', serialization works is there's some type of method invocation which dumps the model into some type of format. In our case, we have a to_dict() method on every model that constructs and returns a key-value dictionary with the key being the field name and the value being the instance variable.
All throughout our code, we have snippets like the following: json.dumps(**some_model_object.to_dict()) which will serialize a some_model_object to json. Recently, we've decided to expose some internal resources to our users, but some of these resources have specific private instance values that we do not want to transmit back during serialization if the requesting user is not a super user.
I'm trying to come up with a clean design that will allow easier serialization, as well as allow us to serialize to a format other than json. I think this is a pretty good use case for Aspect Oriented Design/Programming, where the aspects respect the requesting access controls and serialize the object based on the requesting user's persmissions.
Here's something similar to what I have now:
from framework import current_request
class User(SQLAlchemyDeclarativeModel):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(Unicode(255))
last_name = Column(Unicode(255))
private_token = Column(Unicode(4096))
def to_dict(self):
serialized = dict((column_name, getattr(self, column_name))
for column_name in self.__table__.c.keys())
# current request might not be bound yet, could be in a unit test etc.
if current_request and not current_request.user.is_superuser():
# we explicitly define the allowed items because if we accidentally add
# a private variable to the User table, then it might be exposed.
allowed = ['id', 'first_name', 'last_name']
serialized = dict((k, v) for k, v in serialized.iteritems() if k in allowed)
return serialized
As one can see, this is less than ideal because now I have to couple the database model with the current request. While this is very explicit, the request coupling is a code smell and I'm trying to see how to do this cleanly.
One way I've thought about doing it is to register some fields on the model like so:
class User(SQLAlchemyDeclarativeModel):
__tablename__ = 'users'
__public__ = ['id', 'first_name', 'last_name']
__internal__ = User.__exposed__ + ['private_token']
id = Column(Integer, primary_key=True)
first_name = Column(Unicode(255))
last_name = Column(Unicode(255))
private_token = Column(Unicode(4096))
Then, I would have a serializer class that is bound with the current request on every WSGI call that will take the desired serializer. For example:
import simplejson
from framework import JSONSerializer # json serialization strategy
from framework import serializer
# assume response format was requested as json
serializer.register_serializer(JSONSerializer(simplejson.dumps))
serializer.bind(current_request)
Then in my view somewhere, I would just do:
from framework import Response
user = session.query(User).first()
return Response(code=200, serializer.serialize(user))
serialize would be implemented as follows:
def serialize(self, db_model_obj):
attributes = '__public__'
if self.current_request.user.is_superuser():
attributes = '__private__'
payload = dict((c, getattr(db_model_obj, c))
for c in getattr(db_model_obj, attributes))
return self.serialization_strategy.execute(payload)
Thoughts on this approach's readability and clarity? Is this a pythonic approach to the problem?
Thanks in advance.
establish the "serialization" contract via a mixin:
class Serializer(object):
__public__ = None
"Must be implemented by implementors"
__internal__ = None
"Must be implemented by implementors"
def to_serializable_dict(self):
# do stuff with __public__, __internal__
# ...
keep it simple with the WSGI integration. "register", JSONSerializer as an object, and all that is some kind of Java/Spring thing, don't need that fanfare. Below is my pylons 1.0-style solution, I'm not on pyramid yet:
def my_controller(self):
# ...
return to_response(request, response, myobject)
# elsewhere
def to_response(req, resp, obj):
# this would be more robust, look in
# req, resp, catch key errors, whatever.
# xxx_serialize are just functions. don't need state
serializer = {
'application/json':json_serialize,
'application/xml':xml_serialize,
# ...
}[req.headers['content-type']]
return serializer(obj)

Categories

Resources