Jsonify flask-sqlalchemy many-to-one relationship in flask - python

I was trying to make rest apis with flask-restful where i am using flask-sqlalchemy as the ORM.Here is my model classes.
class Post(db.Model):
__tablename__ = 'post'
postid = db.Column(db.Integer,primary_key=True)
post = db.Column(db.String(64))
userid = db.Column(db.Integer,db.ForeignKey('user.userid'))
#serialize property used for serializing this class to JSON
#property
def serialize(self):
return {
'postid': self.postid,
'post': self.post,
'author':self.author
}
and
class User(db.Model):
userid = db.Column(db.Integer,primary_key=True)
username = db.Column(db.String(30))
email = db.Column(db.String(20))
posts = db.relationship('Post',backref="author",lazy='dynamic')
#serialize property used for serializing this class to JSON
#property
def serialize(self):
return {
'username': self.username,
'email': self.email
}
And the database is populated.now i am trying to make the json out of this
class PostList(Resource):
def get(self):
posts = DL_models.Post.query.all()
posts = [post.serialize for post in posts]
return { 'posts': posts }
api.add_resource(PostList, '/twitter/api/v1.0/posts', endpoint = 'posts')
This works perfect when i change serialize method in Post to
#property
def serialize(self):
return {
'postid': self.postid,
'post': self.post,
'author':self.postid
}
This returns expected json output but when i am changing to 'author':self.author i am getting a error
TypeError: <app.DL_models.User object at 0x7f263f3adc10> is not JSON serializable
I understand that i have to call serialize on those nested objects as well but i could not figure out how to do it.
Or please share your experience to encode relationships in sqlalchemy.

Since you're already using Flask-Restful, have you considered using their built in data marshaling solution?
However, for marshaling complex data structures, I find that Marshmallow does the task about a thousand times better, even making nesting serializers within others easy. There's also a Flask extension designed to inspect endpoints and output URLs.

This is a punt but have you tried the below?
'author': self.author.username
From the error message I'm guessing it's getting to User but doesn't know what you want from it.

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.

Return custom JSON response from ListAPIView Django 3.0 Rest Framework

I have created an API using DRF That is able to list and view particular records based on the URL pattern specified. For example:
for the request:
curl -v http://127.0.0.1:8000/get_details/120001/
I am able to get a response:
[
{
"subject": "Data Structures",
"course": "CSE"
},
{
"subject": "Thermodynamics",
"course": "Chemistry"
},
{
"subject": "Organic Chemistry",
"course": "Chemistry"
},
{
"subject": "Optics",
"course": "Physics"
}
]
Where '120001' is the user_id the database is searched against.
But the I want the response in the following format:
{'Chemistry': ['Thermodynamics', 'Organic Chemistry'], 'CSE': ['Data Structures'], 'Physics': ['Optics']}
(content wise, I am not considering indentation and other factors)
While I am able to write code for the logic of how to create and populate this dictionary, I am unable to figure out how to return this as response and from where.
I am using generics.ListAPIView as the view class.
Here is my model (models.py):
class Subject(models.Model):
user_id = models.CharField(null = False, max_length=10)
subject = models.CharField(max_length=50)
course = models.CharField(max_length=50)
def __str__(self):
return self.subject
Serializer (serializers.py):
class SubjectSerializer(serializers.ModelSerializer):
class Meta:
model = Subject
fields = ['subject', 'course']
and, views.py (for the first output in default format):
class SubjectView(generics.ListAPIView):
serializer_class = SubjectSerializer
def get_queryset(self):
username = self.kwargs['user_id']
return Subject.objects.filter(user_id = username).only('subject','course')
I have written a logic to create the dictionary to send as response (as described in my desired output) by extracting values using Subject.objects.values(....) and then looping through the results to create my dictionary but I just don't get where (that is, which function) to write it in and return from.
Is there any function provided by the generics.ListAPIView class that can allow me to do this? And if not, then what other alternative approach can I try?
I am an absolute beginner at Django and any help will be appreciated. Also, it will be of great help if anyone can suggest me a practical guide/tutorial/playlist from where I can learn DRF through code examples to speed up my learning process.
Thank you!
You need to override to_representation method of Serializer
from docs
There are some cases where you need to provide extra context to the
serializer in addition to the object being serialized.
class SubjectSerializer(serializers.ModelSerializer):
class Meta:
model = Subject
fields = ['subject', 'course']
def to_representation(self, instance):
data = super(SubjectSerializer, self).to_representation(instance)
# manipulate data here
return data

Django Tastypie create API which accepts POST data but not creates any entry in database

My question is exactly what it's subject says :
How to create Django Tastypie API which accepts the POST data, does some processing on it and returns some HTTP response, but does not creates any entry in database.
For Example for this sample API resource :
class NextNumberResource(ModelResource):
class Meta:
resource_name = 'next_number'
detail_allowed_methods = []
list_allowed_methods = ['post']
def obj_create(self, bundle, **kwargs):
#raise CustomBadRequest(code = "code ={c}".format(c=int(bundle.data["number"])*2))
next_number = int(bundle.data["number"]) * 2
data = json.dumps({"next_number":next_number})
return HttpResponse(data, content_type='application/json', status=200)
I am getting following error :
{"error_message": "'HttpResponse' object has no attribute 'pk'"}
I think it's better to handle this request in dispatch_* methods (e.g. dispatch_list).
For example here.
Explanation: If you handle a post request which doesn't create any instance, you have to process it before std workflow of the tastypie.

django serializers to json - custom json output format

I am quite new to django and recently I have a requirement of a JSON output, for which I use the following django code:
data = serializers.serialize("json", Mymodel.objects.all())
It works great, except that I get a output of:
[{"pk": 8970859016715811, "model": "myapp.mymodel", "fields": {"reviews": "3.5", "title": .....}}]
However, I would like the output to be simply either:
[{"reviews": "3.5", "title": .....}]
or,
[{"id": "8970859016715811", "reviews": "3.5", "title": .....}]
I was wondering if someone could point me to the right direction as to how to achieve this.
You can add 'fields' parameter to the serialize-function, like this:
data = serializers.serialize('xml', SomeModel.objects.all(), fields=('name','size'))
See: https://docs.djangoproject.com/en/dev/topics/serialization/
EDIT 1:
You can customize the serializer to get only the fields you specify.
From Override Django Object Serializer to get rid of specified model:
from django.core.serializers.python import Serializer
class MySerialiser(Serializer):
def end_object( self, obj ):
self._current['id'] = obj._get_pk_val()
self.objects.append( self._current )
# views.py
serializer = MySerialiser()
data = serializer.serialize(some_qs)
You'll need to write a custom Json serializer. Something like this should do the trick:
class FlatJsonSerializer(Serializer):
def get_dump_object(self, obj):
data = self._current
if not self.selected_fields or 'id' in self.selected_fields:
data['id'] = obj.id
return data
def end_object(self, obj):
if not self.first:
self.stream.write(', ')
json.dump(self.get_dump_object(obj), self.stream,
cls=DjangoJSONEncoder)
self._current = None
def start_serialization(self):
self.stream.write("[")
def end_serialization(self):
self.stream.write("]")
def getvalue(self):
return super(Serializer, self).getvalue()
The you can use it like this:
s = FlatJsonSerializer()
s.serialize(MyModel.objects.all())
Or you could register the serializer with django.core.serializers.register_serializer and then use the familiar serializers.serialize shortcut.
Take a look at the django implementation as a reference if you need further customization: https://github.com/django/django/blob/master/django/core/serializers/json.py#L21-62
I just came across this as I was having the same problem. I also solved this with a custom serializer, tried the "EDIT 1" method but it didn't work too well as it stripped away all the goodies that the django JSON encoder already did (decimal, date serialization), which you can rewrite it yourself but why bother. I think a much less intrusive way is to inherit the JSON serializer directly like this.
from django.core.serializers.json import Serializer
from django.utils.encoding import smart_text
class MyModelSerializer(Serializer):
def get_dump_object(self, obj):
self._current['id'] = smart_text(obj._get_pk_val(), strings_only=True)
return self._current
Sso the main culprit that writes the fields and model thing is at the parent level python serializer and this way, you also automatically get the fields filtering that's already built into django's JSON serializer. Call it like this
serializer = MyModelSerializer()
data = serializer.serialize(<queryset>, <optional>fields=('field1', 'field2'))
import json
_all_data = Reporter.objects. all()
json_data = json.dumps([{'name': reporter.full_name} for reporter in _all_data])
return HttpResponse(json_data, content_type='application/json')
Here Reporter is your Model

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