I have models as below:
class Bus(models.Model):
bid = models.CharField(_("Bus unique id"), max_length=45)
plate_number = models.CharField(_("Plate number"), max_length=45)
class BusData(models.Model):
bus = models.ForeignKey(Bus, on_delete=models.CASCADE)
timestamp = models.DateTimeField()
speed = models.IntegerField(_("Speed"), null=True, blank=True)
I want to retrieve the bus object with the busdata together, thus I use below serializer classes.
class BusSerializer(serializers.ModelSerializer):
class Meta:
model = Bus
fields = ('bid', 'plate_number', 'busdata_set')
class BusDataSerializer(serializers.ModelSerializer):
class Meta:
model = BusData
So far, the BusSerializer output looks like this:
In [8]: bus1 = Bus.objects.create(bid='1',plate_number='X101')
In [10]: from django.utils import timezone
In [15]: bus_data1 = BusData.objects.create(bus=bus1,timestamp=timezone.now(),speed=100)
In [18]: bus_data2 = BusData.objects.create(bus=bus1,timestamp=timezone.now(),speed=200)
In [20]: bus_s = BusSerializer(bus1)
In [21]: bus_s.data
Out[21]:
OrderedDict([('bid', u'1'),
('plate_number', u'X101'),
('busdata_set', [10, 11])])
As you can see, the busdatas associated with this bus are already taken in busdata_set fields.
However, what I want here is just get the latest busdata record to return (sort by the timestamp field desc, and return the first record), not all the busdata record.
Something looks like this,
OrderedDict([('bid', u'1'),
('plate_number', u'X101'),
('busdata', 11)])
I've no idea how to achieve this.
Any thoughts and code snippets would be appreciate.
Thanks so much!
There are many ways to do this. The simplest would be to have a property on the Bus class that points to the latest BusData object, and then retrieve it in the serializer.
class Bus(models.Model):
bid = models.CharField(_("Bus unique id"), max_length=45)
plate_number = models.CharField(_("Plate number"), max_length=45)
#property
def bus_data(self):
return self.busdata_set.latest('timestamp')
Then make the change in the serializer:
class BusSerializer(serializers.ModelSerializer):
bus_data = serializers.Field()
class Meta:
model = BusData
As Django-Rest-Framework Serialize Relations docs says You can define the queryset in your PrimaryKeyRelatedField. So you could try like this:
class BusSerializer(serializers.ModelSerializer):
busdata_set = serializers.PrimaryKeyRelatedField(queryset=BusData.objects.create(bus=bus1,timestamp=timezone.now(),speed=200), many=True)
class Meta:
model = Bus
fields = ('bid', 'plate_number', 'busdata_set')
Related
I want to calculate the average rating by using SerializerMethodField().
The error in the following code is AttributeError: 'FeedbackModel' object has no attribute 'aggregate'
I think _set is missing but I don't know where to put it..!
class FeedbackSerializer(serializers.ModelSerializer):
feedback_by_user_profile_pic = serializers.ImageField(source='feedback_by.profile_pic')
average_rating = serializers.SerializerMethodField()
def get_average_rating(self,instance):
return instance.aggregate(average_rating=Avg('rating'))['average_rating']
class Meta:
model = FeedbackModel
fields = ['feedback_text','rating','date','feedback_by_user_profile_pic','average_rating']
Feedback Model
class FeedbackModel(models.Model):
feedback_text = models.CharField(max_length=1000)
rating = models.IntegerField()
date = models.DateField(auto_now=True)
feedback_by = models.ForeignKey(UserModel,on_delete=models.CASCADE)
business_account = models.ForeignKey(BusinessAccountModel,on_delete=models.CASCADE)
class Meta:
db_table = 'feedback'
BusinessAccountModel
class BusinessAccountModel(models.Model):
business_title = models.CharField(max_length=70)
business_description = models.CharField(max_length=500)
status = models.CharField(max_length=100)
note = models.CharField(max_length=200)
user = models.OneToOneField(UserModel,on_delete=models.CASCADE)
class Meta:
db_table = 'wp_business_acc'
BusiAccSerializer
class BusiAccSerializer(serializers.ModelSerializer):
class Meta:
model = BusinessAccountModel
fields = '__all__'
I think you need to add the average_rating field in the BusiAccSerializer, not in the FeedbackSerializer.
First you have to set the related_name attribute in the FeedbackModel.
class FeedbackModel(models.Model):
...
# here I added the `related_name` attribute
business_account = models.ForeignKey(BusinessAccountModel,on_delete=models.CASCADE, related_name="feedbacks")
And then in the BusiAccSerializer,
class BusiAccSerializer(serializers.ModelSerializer):
average_rating = serializers.SerializerMethodField(read_only = True)
def get_average_rating(self, obj):
return obj.feedbacks.aggregate(average_rating = Avg('rating'))['average_rating']
class Meta:
model = BusinessAccountModel
fields = (
'business_title', 'business_description', 'status', 'note', 'user', 'average_rating',
)
First of all, you've indicated that you need an average rating for a business account, but you can not get an average rating for an account without having a concrete business account, so you need to do it in the business account serializer.
David Lu has already answered how to do it in the BusiAccSerializer, but I have something to add:
What you've trying to do is to use a serializer method field to add some aggregated data to the output. This way of solving your problem has a major drawback: when you will try to serialize a list of BusinessAccountModels, the serializer will do a separate database call for each business account and it could be slow. You better need to specify an annotated queryset in your view like this:
BusinessAccountModel.objects.all().annotate(average_rating=Avg('feedbacks__rating'))
Then you will be able to use the result of calculation as a regular field in your serializer:
class BusiAccSerializer(serializers.ModelSerializer):
...
average_rating = serializers.FloatField(read_only=True)
This way there will be no additional database queries done by the serializer.
I have two models in my models.py. I need to return a json response which includes data from two tables.
How should my view and serializer look like?
class Device(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
device_name = models.CharField(max_length=200, null=True, blank=True )
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.device_name)
class StatusActivity(models.Model):
OFFLINE = 1
ONLINE = 2
STATUS = (
(OFFLINE, ('Offline')),
(ONLINE, ('Online')),
)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
device_id = models.ForeignKey(Device, related_name='StatusActivity', on_delete=models.CASCADE)
changed_to = models.PositiveSmallIntegerField(choices=STATUS)
modified_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.device_id)
Expected Response:
{
"device_id":"",
"device_name":"",
"changed_to":"",
"modified_at":"",
}
UPDATE:
I set my views.py and serializer.py as below. I am checking
Serializer.py
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = '__all__'
class StatusActivitySerializer(serializers.ModelSerializer):
class Meta:
model = StatusActivity
fields = '__all__'
class ListSerializer(serializers.Serializer):
# devices = DeviceSerializer(many=True)
# activities = StatusActivitySerializer(many=True)
class Meta:
model = [Device, StatusActivity]
fields = ['device_id', 'device_name', 'changed_to', 'modified_at']
Views.py
class DeviceListView(generics.ListAPIView):
queryset = Device.objects.all()
serializer_class = ListSerializer
class StatusActivityListView(generics.ListAPIView):
queryset = StatusActivity.objects.all()
serializer_class = StatusActivitySerializer
Actually you don't need to have two separated views for this, because you can easily serialize relations from one serializer class.
Take a look at this useful answer: How do I include related model fields using Django Rest Framework?
For your case you can write something like this:
class StatusActivitySerializer(serializers.ModelSerializer):
device_name = serializers.CharField(source='device_id.device_name')
class Meta:
model = StatusActivity
fields = ('changed_to', 'modified_at', 'device_id', 'device_name')
Something that worth to note:
it's a good idea for ForeignKey field use device name instead of
device_id;
related_name arg should have a name for reverse access. Keep it
meaningful, e.g. status_activities is a good choice.
filter the status activity. you can refer the foreignkey.
eg:
items = []
for statusact in StatusActivity.objects.all():
items.append({
"device_id":statusact.device_id.id,
"device_name":statusact.device_id.device_name,
"changed_to":statusact.changed_to,
"modified_at":statusact.modified_at,
})
nested serializer showing null data
from rest_framework import serializers
from .models import PlayerTable, ChildTable
class ChildTableSerializer(serializers.ModelSerializer):
# x= ChildTable.objects.all().values
class Meta:
model = ChildTable
fields = ('season','goals','fk')
# fields =('fk',)
class PlayerTableSerializer(serializers.ModelSerializer):
player_details = ChildTableSerializer(many=True, read_only=True)
class Meta:
model = PlayerTable
fields = ('player_details',)
please help data getting by serializer is null
what is the field 'player-details'? It's not a field on your PlayerTable model. You need to use the name of the related field. In your case since you have a ForeignKey relationship ChildTable --> PlayerTable and you haven't specified the related_name, it's childtable_set. So if you do this it should work:
class PlayerTableSerializer(serializers.ModelSerializer):
childtable_set = ChildTableSerializer(many=True, read_only=True)
class Meta:
model = PlayerTable
fields = ('childtable_set',)
Alternatively, change your models naming to be more aligned with Django conventions:
class PlayerDetail(models.Model):
player = models.ForeignKey(Player, db_column="fk", related_name="player_details", null=True, blank=True, on_delete=models.CASCADE)
...
class Meta:
managed = False
db_table = "child_table"
class Player(models.Model):
name = models.CharField(db_column="player_name", ...)
class Meta:
db_table = "player_table"
then your serializer would work because the relation is player_details. This also has the advantage that when you do details.player you get the player object (now, you have to do details.fk but that actually doesn't return the foreign key value, it returns the Player object). Also your models have more pythonic names (Player not PlayerTable). Your code will be much more readable.
I want to make a common filter to my models because I need to filter all my objects to return a gap between time_start and time_end, but apparently it doesn't work.
I'm not sure if it's even possible(But I hope so, because it won't by DRY otherwise).
models.py
class Time(models.Model):
time = models.TimeField()
class Meta:
abstract=True
class Mark(Time):
value = models.IntegerField(verbose_name="mark")
teacher = models.CharField(max_length=20)
subject = models.CharField(max_length=20)
serializers.py
class MarkSerializer(serializers.ModelSerializer):
class Meta:
model = Mark
fields = ('id', 'time','value', 'teacher', 'subject')
filers.py
class DataFilter(django_filters.FilterSet):
start_time = django_filters.TimeFilter(name="time", lookup_expr='gte')
end_time = django_filters.TimeFilter(name="time", lookup_expr='lte')
class Meta:
model = Time
fields = ['start_time', 'end_time']
views.py
class MarkViewSet(viewsets.ModelViewSet):
serializer_class = MarkSerializer
queryset = Mark.objects.all()
filter_class = DataFilter
I try to get needed marks through:
127.0.0.1:8000/api/v0/marks/?time_start=11:40:00&time_end=12:00:00
but it returns all the objects that I have not the filtered ones.
Thanks in advance.
You have passed the filter params wrong, it should be the name of the field you described in the filter class DataFilter.
Hit this endpoint in the browser,
127.0.0.1:8000/api/v0/marks/?start_time=11:40:00&end_time=12:00:00
I want to fetch the foreign key values in PUT and GET but while using the many=True I am getting error TypeError object is not iterable.
Here are following the my snippets.
I have two models called MasterStatus and MasterType. In MasterType I have foreign key values of MasterStatus.
models.py
class MasterType(models.Model):
id = models.BigIntegerField(primary_key=True)
type_name = models.CharField(max_length=255, blank=True, null=True)
fk_status = models.ForeignKey(MasterStatus)
def __unicode__(self):
return u'%s' % (self.type_name)
class Meta:
managed = False
db_table = 'master_type'
In serializer I am using the many=True to get the nested values of foreignkey. Here I have used PrimaryKeyRelatedField serializer.
serializer.py
class MasterTypeSerializer(serializers.HyperlinkedModelSerializer):
fk_status = serializers.PrimaryKeyRelatedField(queryset=MasterStatus.objects.all(),many=True)
class Meta:
model = MasterType
fields = ('id', 'type_name', 'fk_status', 'last_modified_date', 'last_modified_by')
depth = 2
ForeignKey links to a single MasterStatus instance, therefore it is not many.
Your serializers should look something like this:
class MasterTypeSerializer(serializers.HyperlinkedModelSerializer):
fk_status = serializers.PrimaryKeyRelatedField(
queryset=MasterStatus.objects.all())
class Meta:
model = MasterRepaymentType
class MasterStatusSerializer(serializers.HyperlinkedModelSerializer):
fk_type = serializers.PrimaryKeyRelatedField(
queryset= MasterRepaymentType.objects.all(), many=True)
class Meta:
model = MasterStatus
Note that many is used on the fk_type field as a MasterStatus has many MasterRepaymentType.
Hope this helps.