I have two modals designation and UserModal
class DesignationModal(models.Model):
designation=models.CharField(max_length=100)
def __str__(self):
return self.designation
class UserModal(AbstractUser):
username=models.CharField(max_length=300,unique=True)
password=models.CharField(max_length=300)
email=models.EmailField(max_length=300)
designation=models.ForeignKey(DesignationModal,on_delete=models.CASCADE,
related_name="desig",null=True)
def __str__(self):
return self.username
every user only have one designation. I wrote serializer for that.
class DesignationSerializer(serializers.Serializer):
class Meta:
model=DesignationModal
fields=['designation','id']
class UserSerializer(serializers.ModelSerializer):
designation=DesignationSerializer(read_only=True,many=False)
class Meta:
model=UserModal
fields=['id', 'username','designation']
I'm getting a JSON response like this
{
"status": true,
"data": [
{
"id": 3,
"username": "akhil",
"designation": {}
}
]
}
no values in the dictionary, when I rewrite the serializer code like this.
class UserSerializer(serializers.ModelSerializer):
designation=serializers.StringRelatedField()
class Meta:
model=UserModal
fields=['id', 'username','designation']
im getting designation values as string
{
"status": true,
"data": [
{
"id": 3,
"username": "akhil",
"designation": "agent"
}
]
}
why I'm not getting values in a previous way?
Using depth option will get the nested values.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = UserModal
fields = ['username', 'email', 'designation', ]
depth = 1
Bad inheritance for DesignationSerializer(serializers.Serializer) you need to change it like that
class DesignationSerializer(serializers.ModelSerializer):
class Meta:
model=DesignationModal
fields=['designation','id']
Related
I'm trying to serializer two nested models linked by a foreing key:
class Category(models.Model):
sp = models.ForeignKey('species.Sp', on_delete=models.CASCADE, related_name='species_category')
category_name = models.CharField(max_length=50)
class Catch(models.Model):
weight = models.IntegerField()
category = models.ForeignKey('species.Category', on_delete=models.CASCADE,)
I know is possible to use depth option, but it serialize all the fields of the related model, for example:
class CatchesSerializer(serializers.ModelSerializer):
class Meta:
model = Catch
fields = ['id', 'weight', 'category', ]
depth = 1
returns
[
{
"id": 335,
"weight": 4710,
"category": {
"id": 1,
"category_name": "1",
"sp": 41
}
},
...
]
How is the way to serialize only certains fields of the related model? for example:
[
{
"id": 335,
"weight": 4710,
"category": {
"sp": 41,
}
},
...
]
Serializer can be nested, you can try:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['sp']
class CatchesSerializer(serializers.ModelSerializer):
category = CategorySerializer()
class Meta:
model = Catch
fields = ['id', 'weight', 'category']
As per this example of nested and flat:
https://docs.python-guide.org/scenarios/serialization/
I want the foreign key to represented from the following JSON output
{
"cart": {
"cartid": "C0001",
"username": "myuser1"
},
"subtotal": 150.0,
"start_day": "2019-03-20T00:00:00"
},
to be represented like:
{
"C0001": {
"username": "myuser1"
},
"subtotal": 150.0,
"start_day": "2019-03-20T00:00:00"
},
Is there a simple way to have this output in django-rest-framework?
Here are my serializers:
class CartSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='shopper.username')
class Meta:
model = Cart
fields = ['shopper', 'cartid', 'username']
class CartProdSerializer(serializers.ModelSerializer):
cart = CartSerializer(read_only=True, label=Cart.cartid)
class Meta:
model = cart_prod
fields = ['cart', 'subtotal', 'start_day']
And my view:
class CartProdView(viewsets.ModelViewSet):
queryset = cart_prod.objects.all()
serializer_class = CartProdSerializer
I have 3 django models concatenated by ForeignKey:
# models.py
class Album(models.Model):
some_fields
class Track(models.Model):
some_fields
album = models.ForeignKey(
Album,
related_name='tracks',
on_delete=models.CASCADE,
)
class Comment(models.Model):
some_fields
track = models.ForeignKey(
Track,
related_name='comments',
on_delete=models.CASCADE,
)
I would like to serialize the Album model to view all comments of all its tracks. I have created serializer file like this:
# serializers.py
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Album
fields = (some_fields, 'comments')
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = (some_fields, 'tracks')
This way I get all the data but in nested lists. I would like to view all the comments of album tracks directly under album object.
# Output albums
[{
"some_fields": some_values,
"tracks": [{ "some_fields": some_values, "comments": [ comment1, comment2 ]},
{ "some_fields": some_values, "comments": []},
{ "some_fields": some_values, "comments": [ comment3 ]},]
},
{
"some_fields": some_values,
"tracks": [{ "some_fields": some_values, "comments": [ comment4, comment5 ]},
{ "some_fields": some_values, "comments": []},
{ "some_fields": some_values, "comments": [ comment6 ]},]
}]
# Desired output albums
[{
"some_fields": some_values,
"tracks": [{ "some_fields": some_values},
{ "some_fields": some_values},
{ "some_fields": some_values},],
"comments": [ comment1, comment2, comment3]
},
{
"some_fields": some_values,
"tracks": [{ "some_fields": some_values},
{ "some_fields": some_values},
{ "some_fields": some_values},],
"comments": [ comment4, comment5, comment6]
}]
I tried to flatten the list directly in serializers file but I get "TypeError: 'ListSerializer' object is not iterable".
# serializers.py
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
comments = [comment for track in tracks for comment in track.comments]
class Meta:
model = Album
fields = (some_fields, 'tracks', 'comments')
Is there any neat way how to output a flattened list directly with serializers? Or should I do it later in views.py? Now it looks simply like this:
# views.py
class AlbumMixin(object):
model = Album
raise_exception = True
serializer_class = AlbumSerializer
def get_queryset(self):
return Album.objects.all()
class AlbumList(AlbumMixin, generics.ListCreateAPIView):
pass
You can add a serializers.SerializerMethodField on AlbumSerializer and return the necessary comments.
Something like this:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = "__all__"
class AlbumSerializer(serializers.ModelSerializer):
comments = serializers.SerializerMethodField()
tracks = TrackSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = (some_fields, "comments", "tracks")
def get_comments(self, obj):
comments = Comment.objects.filter(track__in=obj.tracks.all())
return CommentSerializer(comments, many=True).data
# or you can get rid of CommentSerializer
# return comments.values("some_field")
See SerializerMethodField docs here
Update
You can improve the query by using the one from #c6754
comments = Comment.objects.filter(track__album_id=obj.id)
You could try using a serializerMethodField
# serializers.py
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
comments = serializers.SerializerMethodField()
class Meta:
model = Album
fields = (some_fields, 'tracks', 'comments')
def get_comments(self, obj):
comments = Comments.objects.filter(track__album_id=obj.pk)
return CommentSerializer(comments, many=True).data
Here are my models:
class Property(models.Model):
id = [...]
address = [...]
class Property_Value(models.Model):
id = models.ForeignKey(Property)
amount = [...]
last_updated = [...]
def __unicode__(self):
return '%s: %s' % (self.last_updated, self.amount)
My serializer are:
class Property_ValueSerializer(serializers.ModelSerializer):
class Meta:
model = Property_Value
fields = ('last_updated', 'amount')
class PropertySerializer(serializers.ModelSerializer):
1) property_values = Property_ValueSerializer(source='property_value_set', many=True)
2) property_values = serializers.RelatedField(many=True, read_only=True)
class Meta:
model = Property
fields = ('id', 'address', 'property_values')
I would like to display a json like this:
[
{
"id": "2",
"address": "123 Apple Lane",
"property_values": [
{
"10/13/2016": "1709195.00"
}
]
}
]
If I use option 1, I get:
[
{
"id": "2",
"address": "123 Apple Lane",
"property_values": [
{
"last_updated": "10/13/2016",
"amount": "1709195.00"
}
]
}
]
If I user option 2 (which is what was suggested by the Rest tutorial, I get an Attribution Error:
AttributeError at /api/property/
'Property' object has no attribute 'property_values'
I'm not quite sure what I am doing wrong. Can someone point out what I am doing wrong? Thanks.
You can use StringRelatedField which returns the __unicode__ representation for the related fields. In your case __unicode__ method already returns the value in the way you are expecting in serializer.
class PropertySerializer(serializers.ModelSerializer):
property_values = serializers.StringRelatedField(many=True)
class Meta:
model = Property
fields = ('id', 'address', 'property_values')
class Property_Value(models.Model):
id = models.ForeignKey(Property, related_filed="property_values")
amount = [...]
last_updated = [...]
def __unicode__(self):
return '%s: %s' % (self.last_updated, self.amount)
Given the following code I was wondering how to populate RecordsResource with each real record data:
models.py
class Record(models.Model):
content_type = models.ForeignKey(ContentType, editable=False, null=True)
user = models.ForeignKey(User, related_name='records')
issued = models.DateTimeField(auto_now_add=True)
date = models.DateField()
def save(self, *args, **kwargs):
if not self.content_type:
self.content_type = ContentType.objects.get_for_model(self.__class__)
super(Record, self).save(*args, **kwargs)
def as_leaf_class(self):
model = self.content_type.model_class()
if model == self.__class__:
return self
return model.objects.get(pk=self.id)
class Record1(Record):
# some fields
# ...
class RecordN(Record):
# some fields
api.py
class BaseModelResource(ModelResource):
class Meta(object):
authentication = ApiKeyPlusWebAuthentication()
authorization= Authorization()
cache = SimpleCache()
throttle = CacheDBThrottle(
throttle_at=350,
# 1 day
expiration=86400
)
if settings.DEBUG:
serializer = PrettyJSONSerializer()
def obj_create(self, bundle, request=None, **kwargs):
return super(BaseModelResource, self).obj_create(bundle, request, user=request.user)
def apply_authorization_limits(self, request, object_list):
return object_list.filter(user=request.user)
class BaseRecordResource(BaseModelResource):
class Meta(BaseModelResource.Meta):
filtering = {
'date': ALL
}
excludes = ['issued']
class RecordsResource(BaseRecordResource):
class Meta(BaseRecordResource.Meta):
resource_name = 'records'
queryset = Record.objects.all()
class Record1Resource(BaseRecordResource):
class Meta(BaseRecordResource.Meta):
resource_name = 'record1'
queryset = Record1.objects.all()
# ...
class RecordNResource(BaseRecordResource):
class Meta(BaseRecordResource.Meta):
resource_name = 'recordn'
queryset = RecordN.objects.all()
Ok, I just solved it. I've simplified the code.
Given the following code...
models.py
from django.db import models
from model_utils.managers import InheritanceManager
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
# https://github.com/carljm/django-model-utils#inheritancemanager
objects = InheritanceManager()
class Restaurant(Place):
custom_field = models.BooleanField()
class Bar(Place):
custom_field = models.BooleanField()
api.py
from core.models import Place, Restaurant, Bar
# http://django-tastypie.readthedocs.org/en/latest/cookbook.html#pretty-printed-json-serialization
from core.utils import PrettyJSONSerializer
from tastypie.resources import ModelResource
class PlaceResource(ModelResource):
class Meta:
queryset = Place.objects.select_subclasses()
resource_name = 'place'
serializer = PrettyJSONSerializer()
class RestaurantResource(ModelResource):
class Meta:
queryset = Restaurant.objects.all()
resource_name = 'restaurant'
serializer = PrettyJSONSerializer()
class BarResource(ModelResource):
class Meta:
queryset = Bar.objects.all()
resource_name = 'bar'
serializer = PrettyJSONSerializer()
Output
http://localhost:8000/api/v1/bar/?format=json
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 1
},
"objects": [
{
"address": "dawdaw",
"custom_field": true,
"id": "1",
"name": "dwdwad",
"resource_uri": "/api/v1/bar/1/"
}
]
}
OK
http://localhost:8000/api/v1/restaurant/?format=json
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 1
},
"objects": [
{
"address": "nhnhnh",
"custom_field": true,
"id": "2",
"name": "nhnhnh",
"resource_uri": "/api/v1/restaurant/2/"
}
]
}
OK
http://localhost:8000/api/v1/place/?format=json
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 2
},
"objects": [
{
"address": "dawdaw",
"id": "1",
"name": "dwdwad",
"resource_uri": "/api/v1/place/1/"
},
{
"address": "nhnhnh",
"id": "2",
"name": "nhnhnh",
"resource_uri": "/api/v1/place/2/"
}
]
}
What I want to achieve
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 2
},
"objects": [
{
"address": "dawdaw",
"custom_field": true,
"id": "1",
"name": "dwdwad",
"resource_uri": "/api/v1/bar/1/"
},
{
"address": "nhnhnh",
"custom_field": true,
"id": "2",
"name": "nhnhnh",
"resource_uri": "/api/v1/restaurant/2/"
}
]
}
Solution:
from core.models import Place, Restaurant, Bar
# http://django-tastypie.readthedocs.org/en/latest/cookbook.html#pretty-printed-json-serialization
from core.utils import PrettyJSONSerializer
from tastypie.resources import ModelResource
class RestaurantResource(ModelResource):
class Meta:
queryset = Restaurant.objects.all()
resource_name = 'restaurant'
serializer = PrettyJSONSerializer()
class BarResource(ModelResource):
class Meta:
queryset = Bar.objects.all()
resource_name = 'bar'
serializer = PrettyJSONSerializer()
class PlaceResource(ModelResource):
class Meta:
queryset = Place.objects.select_subclasses()
resource_name = 'place'
serializer = PrettyJSONSerializer()
def dehydrate(self, bundle):
# bundle.data['custom_field'] = "Whatever you want"
if isinstance(bundle.obj, Restaurant):
restaurant_res = RestaurantResource()
rr_bundle = restaurant_res.build_bundle(obj=bundle.obj, request=bundle.request)
bundle.data = restaurant_res.full_dehydrate(rr_bundle).data
elif isinstance(bundle.obj, Bar):
bar_res = BarResource()
br_bundle = bar_res.build_bundle(obj=bundle.obj, request=bundle.request)
bundle.data = bar_res.full_dehydrate(br_bundle).data
return bundle
In RecordsResource class, you need to add model field as well (see https://github.com/tomchristie/django-rest-framework/blob/master/djangorestframework/resources.py#L232-234)
class RecordsResource(BaseRecordResource):
model = Record
class Meta(BaseRecordResource.Meta):
resource_name = 'records'
queryset = Record.objects.all()
Explaining from the beginning:
There are three styles of inheritance that are possible in Django.
Often, you will just want to use the parent class to hold
information that you don't want to have to type out for each child
model. This class isn't going to ever be used in isolation, so
Abstract base classes are what you're after.
If you're subclassing an existing model (perhaps something from
another application entirely) and want each model to have its own
database table, Multi-table inheritance is the way to go.
Finally, if you only want to modify the Python-level behavior of a
model, without changing the models fields in any way, you can use
Proxy models.
The choice here is Multi-table inheritance
Multi-table inheritance
The second type of model inheritance supported by Django is when each model in the hierarchy is a model all by itself. Each model corresponds to its own database table and can be queried and created individually. The inheritance relationship introduces links between the child model and each of its parents (via an automatically-created OneToOneField) Ref
To go from Record to Recordx where 1 <= x <= n you do a_example_record = Record.objects,get(pk=3) and then check what type of Recordx it is by using something like below
if hasattr(a_example_record, 'record1'):
# ...
elif hasattr(a_example_record, 'record2'):
# ...
So now that we know how to get the children from the parent and we need to provide TastyPie with a queryset in its meta, you need to write a custom queryset backed by a custom manager on the Record model that takes all your records (More here Custom QuerySet and Manager without breaking DRY?), checks what type of child it is and appends it to a queryset or a list. You can read about appending here How to combine 2 or more querysets in a Django view?