I just recently started developing for Django and am building an API using Django REST Framework and class based views. I am looking for a way to combine models, sort them based on time and then return a subset of the fields to an API with the table name appended.
Currently I have the following:
views.py
class RunLog(APIView):
"""
List log for a specific run sorted in reverse chronological order
"""
def get(self, request, run_id, format=None):
# Combine and sort based on time (decreasing)
result_list = sorted(chain(Output.objects.filter(run=run_id),
Downtime.objects.filter(run=run_id)),
key=attrgetter('start_time'), reverse=True)
// Replace this with serializer??
response = Response(serializers.serialize('json', result_list), status=status.HTTP_200_OK)
return response
models.py
class Output(models.Model):
start_time = models.DateTimeField(default=datetime.now)
value = models.FloatField()
run = models.ForeignKey(Run, blank=True, null=True)
def __unicode__(self):
return str(self.id)
class Downtime(models.Model):
start_time = models.DateTimeField(default=datetime.now)
end_time = models.DateTimeField(null=True, blank=True)
reason = models.CharField(max_length=500)
run = models.ForeignKey(Run, blank=True, null=True)
I get the following JSON:
"[{\"model\": \"app.downtime\", \"pk\": 91, \"fields\": {\"start_time\": \"2016-07-20T14:46:21Z\", \"end_time\": null, \"reason\": \"reason1\", \"run\": 71}}, {\"model\": \"app.downtime\", \"pk\": 101, \"fields\": {\"start_time\": \"2016-07-20T14:46:21Z\", \"end_time\": null, \"reason\": \"reason2\", \"run\": 71}}]"
I would like to serialize this data in the following JSON format:
[
{
"id": 231,
"type": "speed",
"description": "Some description",
"time": "2016-07-21T21:26:26Z"
}
]
**Where type is the database table and description is concatenated columns from a model.
I have looked at the docs and this similar question without any luck.
As IanAuld suggested in the comments - ModelObj._meta.db_table got the name of the table. I then created a sorted list of dictionaries in views.py:
speedList = Speed.objects.filter(run=run_id)
type = Speed._meta.db_table.split('_', 1)[1]
type = type[0].upper() + type[1:]
for speed in speedList:
description = "Speed change to %.2f (units)" % speed.value
logList.append({'id':speed.id, 'type':type, 'description':description, 'time':speed.start_time})
# Sort list by decreasing time
resultList= sorted(logList, key=itemgetter('time'), reverse=True)
serializer = LogSerializer(resultist, many=True)
return Response(serializer.data)
serializers.py:
class LogSerializer(serializers.Serializer):
id = serializers.IntegerField()
type = serializers.CharField(max_length=100)
description = serializers.CharField(max_length=500)
time = serializers.DateTimeField()
Related
I'm trying to build a simple rest API in Django. I have a transaction, account and stock table. Each transaction points to 1 account and 1 stock(on seperate tables using foreign keys). I'm trying to have my API gets the stock ticker for each transaction and not the sql reference number. Does anyone know how to get simple views to return values instead of table keys?
The serializers I'm using:
class TransactionSerializer(serializers.ModelSerializer):
class Meta:
model = Transaction
fields = "__all__"
class StockSerializer(serializers.ModelSerializer):
class Meta:
model = Stock
fields = "__all__"
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = "__all__"
The models for transaction and stock:
class Stock(models.Model):
ticker = models.CharField(max_length=10, unique=True)
name = models.CharField(max_length=200)
price = models.FloatField()
def __str__(self):
return self.ticker
class Transaction(models.Model):
account = models.ForeignKey(Account, on_delete=models.CASCADE)
stock = models.ForeignKey(Stock, on_delete=models.CASCADE)
amount = models.IntegerField()
price = models.FloatField()
type = models.CharField(max_length=10)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.account
I'm leaving out the account/user model but its a mess and half tied into the django base user model.
The view I'm using is a basic APIView:
class TransactionView(APIView):
permission_classes = (IsAuthenticated,)
authentication_classes = (JWTAuthentication,)
def get(self, request, *args, **kwargs):
account = Account.objects.get(email=request.user.email)
transactions = Transaction.objects.filter(account=account)
serializer = TransactionSerializer(transactions, many=True)
return Response(serializer.data)
and the output I am getting from postman:
[
{
"id": 1,
"amount": 1,
"price": 146.1,
"type": "buy",
"date": "2022-10-05T04:12:10.414961Z",
"account": 1,
"stock": 1
},
{
"id": 2,
"amount": 1,
"price": 146.1,
"type": "buy",
"date": "2022-10-05T04:17:57.807945Z",
"account": 1,
"stock": 1
}
]
Also the url I am using for the endpoint:
path("transactions/", views.TransactionView.as_view(), name="transactions"),
Sorry for repetition but the basic issue I'm getting is when I call the API endpoint I am getting 1 for stock and 1 for account instead of the stock ticker and account name and I don't know how to get the view to return all the information without calling a second endpoint that returns the stock and a 3rd endpoint to return the account information.
I was under the impression that the model __str__ function would return information on the object but that isn't happening here.
You should define a "stock" field and an "account" field in your transaction serializer. (Assigning the relevant serializer as value).
Doing this will allow you to indicate to DRF how to serialize the related model.
class TransactionSerializer(serializers.ModelSerializer):
stock = StockSerializer()
account = AccountSerializer()
class Meta:
model = Transaction
fields = "__all__"
I am trying request.data.get('student_name') but it says that list has no attribute get. I just want to get the name of all students before passing to the serializer. I am sending the POST request data in the form of
[
{"student_name": "jack", "last_name": "cale", "fathers_name":"carlos"},
{"student_name": "alex", "last_name": "magasa", "fathers_name":"greg"},
{"student_name": "sia", "last_name": "gunns", "fathers_name":"brett"},
{"student_name": "jacob", "last_name": "woods", "fathers_name":"john"}
]
my views.py
#api_view(['POST'])
def add_students(request):
student_name = request.data.get('student_name')
fathers_name = request.data.get('fathers_name')
serializer = StudentsSerializer(data=request.data, many=True)
if serializer.is_valid():
serializer.save()
return Response("success")
else:
return Response(serializer.errors)
my serializers.py
class StudentsSerializer(serializers.ModelSerializer):
class Meta:
model = Students
fields = ('student_name', 'last_name', 'fathers_name')
my models.py
class Students(models.Model):
student_name = models.CharField(max_length=100, null=True)
last_name = models.CharField(max_length=100, null=True)
fathers_name = models.CharField(max_length=100, null=True)
You are trying to get value from list so you have to iterate through list
for data in request.data:
student_name = data.get('student_name')
father_name = data.get('father_name')
This seems like a question about the format of the request object. My two cents is that it's worth throwing in an import ipdb; ipdb.set_trace() at the beginning of the method, and then using print and dir to get an idea of what the object looks like. Since Django has so many django-specific objects, this helps in a lot of cases.
I am beginner in Django Rest framework. I want to implement One to Many object mapping like following json schema:
{
"from_date": "2017-08-06T12:30",
"to_date": "2017-08-06T12:30",
"coupon_name": "WELCOME100",
"min_booking_value": 150,
"applicable_days": [
{
"from_time": "13:00",
"to_time": "15:00",
"applicable_day": 2
},
{
"from_time": "16:00",
"to_time": "18:00",
"applicable_day": 3
}
]
}
For above json schema, I have created following Django Model classes:
class Coupon(models.Model):
coupon_id = models.AutoField(primary_key=True)
from_date = models.DateTimeField()
to_date = models.DateTimeField()
coupon_name = models.TextField()
min_booking_value = models.FloatField()
def __unicode__(self):
return 'Coupon id: ' + str(self.coupon_id)
class CouponApplicableDays(models.Model):
from_time = models.TimeField()
to_time = models.TimeField()
applicable_day = models.IntegerField()
And following serializer class above models:
class CouponApplicableDaysSerializer(serializers.ModelSerializer):
class Meta:
model = CouponApplicableDays
fields = ('from_time', 'to_time', 'applicable_day')
class CouponSerializer(serializers.ModelSerializer):
coupon_applicable_days = CouponApplicableDaysSerializer(required=True, many=True)
class Meta:
model = Coupon
fields = ('coupon_id', 'from_date', 'to_date', 'coupon_name', 'min_booking_value', 'coupon_applicable_days',)
def create(self, validated_data):
coupon_applicable_days_data = validated_data.pop("coupon_applicable_days")
coupon = Coupon.objects.create(**validated_data)
CouponApplicableDays.objects.create(coupon=coupon, **coupon_applicable_days_data)
return coupon
When I save data using coupon-serializer. It saves only in Coupon table not in CouponApplicableDays.
I know, I have messed up somewhere but I don't know where. Can you guys please look into above code and tell me how can I solve this?
You have a list here
coupon_applicable_days_data = validated_data.pop("coupon_applicable_days")
Either iterate over the list and create the objects, like this:
for applicable_day in coupon_applicable_days_data:
CouponApplicableDays.objects.create(coupon=coupon, **applicable_day)
or use the bulk_create method
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#bulk-create
CouponApplicableDays.objects.bulk_create(
[CouponApplicableDays(coupon=coupon, **aplicable_day)
for applicable_day in coupon_applicable_days_data]
)
Be aware that the bulk_create will not trigger pre_save/post_save signals.
I am working on implementing an API for my Django (v1.5) application using Tastypie. I would like to be able to filter/limit the related resources I get when the parent resource.
Here are my (simplified) models:
# myapp/models.py
class User(models.Model):
number = models.IntegerField()
device_id = models.CharField(verbose_name="Device ID", max_length=255)
timezone = models.CharField(max_length=255, blank=True)
def data(self, limit=0):
result = Data.objects.filter(patient_id = self.id).order_by('-datetime').values('datetime', 'value')
if limit != 0:
result = result[:limit]
return result
class Data(models.Model):
user = models.ForeignKey(User)
datetime = models.DateTimeField()
value = models.IntegerField()
My resources:
# api/resources.py
class DataResource(ModelResource):
class Meta:
queryset = Data.objects.all()
resource_name = 'cgm'
fields = ['value', 'datetime']
serializer = Serializer(formats=['json', 'xml'])
filtering = {
'datetime': ('gte', 'lte'),
}
include_resource_uri = False
def dehydrate(self, bundle):
bundle.data['timestamp'] = calendar.timegm(bundle.data['datetime'].utctimetuple())
return bundle
class UserResource(ModelResource):
data = fields.ToManyField(DataResource, attribute=lambda bundle: Data.objects.filter(patient_id=bundle.obj.id), full=True, related_name='data', null=True)
class Meta:
queryset = User.objects.all().order_by('number')
resource_name = 'user'
fields = ['number', 'timezone', 'device_id'],
serializer = Serializer(formats=['json', 'xml'])
filtering = {
'data': ALL_WITH_RELATIONS,
}
I would like to be able to filter the Data resources by 'datetime' inside the User resource using an URL parameter, e.g.:
127.0.0.1:8000/api/v1/user/1/?format=json&datetime__gte=2013-11-14%2012:00:00
or
127.0.0.1:8000/api/v1/user/1/?format=json&data__datetime__gte=2013-11-14%2012:00:00
to get the User's number, timezone, device id and Data list filtered with the given datetime.
I don't want to have to query the Data resources separately to filter them, I want the whole thing bundled within the User resource.
Is there a way to implement a filter applied to the nested resource using the framework?
Thanks for your time, I'll appreciate any suggestion!
You can extend your attribute argument you've passed to the data field with a full-scale function and reuse the DataResource:
def filter_data_items(bundle):
res = DataResource()
new_bundle = Bundle(request=bundle.request)
objs = res.obj_get_list(new_bundle)
return objs.filter(parent_id=bundle.obj.pk)
res.obj_get_list handles building and applying filters as defined per your DataResource. You just need to filter it futher on parent_id.
Reference.
I have been struggling to add many-to-many relation in the django serializer than be accessed through the view.
I created 2 classes in the model file "MODISLevel1" & "FileProperties". Where MODISLevel1 has a field called "filesProperties" pointing to the "FileProperties" class.
The model looks like:
class FileProperties(models.Model):
FileName = models.CharField(blank=True, max_length=1000)
FileID = models.CharField(blank=True, max_length=1000)
FileSize = models.CharField(blank=True, max_length=1000)
Updated = models.CharField(blank=True, max_length=1000)
GeoBox = models.CharField(blank=True, max_length=1000)
def __str__(self):
return self.FileName
class Meta:
ordering = ('FileName',)
class MODISLevel1(models.Model):
ProductLevel = models.CharField(max_length=7,
choices=ProductLevelsChoices,
default='MOD03')
SpacecraftType = models.CharField(max_length=5,
choices=SpacecraftTypeChoices,
default='Terra')
StartTimespan = models.TextField()
EndTimespan = models.TextField()
AOI = models.TextField()
DegreeNumbers = models.TextField()
filesProperties = models.ManyToManyField(FileProperties)
def __str__(self):
return self.ProductLevel
class Meta:
ordering = ('ProductLevel',)
As for the serializer I checked http://django-rest-framework.org/api-guide/relations.html#nested-relationships which tells that nested relationships should be created in the following way:
class FilePropertiesSerializer(serializers.ModelSerializer):
class Meta:
model = FileProperties
fields = ('id', 'FileName', 'FileID', 'FileSize', 'Updated', 'GeoBox')
class MODISLevel1Serializer(serializers.ModelSerializer):
filesProperties = FilePropertiesSerializer(many=True)
class Meta:
model = MODISLevel1
fields = ('id', 'ProductLevel', 'SpacecraftType', 'StartTimespan', 'EndTimespan', 'AOI', 'DegreeNumbers', 'filesProperties')
for the the view file I tried different ways to make it work but with no success.
1-Creating an instance from the Model "MODISLevel1", filling its fields, saving it, then filling the m2m field through iteration:
modisLevel1 = MODISLevel1()
modisLevel1.ProductLevel = productLevel
modisLevel1.SpacecraftType = spacecraftType
modisLevel1.StartTimespan = startTimespan
modisLevel1.EndTimespan = endTimespan
modisLevel1.AOI = AOI
modisLevel1.DegreeNumbers = degreeNumbers
modisLevel1.save()
#Inside a loop
f = FileProperties()
f.FileID = fileID
f.FileName = fileName
f.Updated = updated
f.GeoBox = geoBox
f.FileSize = fileSize
f.save()
modisLevel1.filesProperties.add(f)
serializer = MODISLevel1Serializer(data=modisLevel1)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
the error that i got is:
{
"non_field_errors": [
"Invalid data"
]
}
but for sure it's added in the database as I executed the save command "modisLevel1.save()", but that's not what I need.
2-Creating a json file and appending the 'filesProperties' to it through the iteration:
jsonFile = {u"ProductLevel": productLevel, u"SpacecraftType": spacecraftType, u"StartTimespan": startTimespan,
u"EndTimespan":endTimespan, u"AOI": AOI, u"DegreeNumbers": u"POINT(22.5,28.34) POINT(28.64,28.34) POINT(29.4,19.16) POINT(19.19,19.3067)",
u"filesProperties": [] }
#inside the loop
jsonFile["filesProperties"].append({u"FileName":fileName, u"FileID":fileID, u"FileSize":fileSize, u"Updated":updated, u"GeoBox":geoBox})
serializer = MODISLevel1Serializer(data=jsonFile)
and this one throws an error that the base model has to have a value before setting the m2m field:
Cannot add "": instance is on database "default", value is on database "None"
3-Create a dict and parse the Model to this dictionary, and then convert the FileProperties to the dictionary and append it to the previously created one:
modisLevel1 = MODISLevel1()
modisLevel1.ProductLevel = productLevel
modisLevel1.SpacecraftType = spacecraftType
modisLevel1.StartTimespan = startTimespan
modisLevel1.EndTimespan = endTimespan
modisLevel1.AOI = AOI
modisLevel1.DegreeNumbers = degreeNumbers
modisLevel1.save()
dict = model_to_dict(modisLevel1)
#FIXME!
dict['filesProperties'] = [{"FileName": "fileName", "FileID": "fileID", "FileSize": "fileSize", "Updated": "updated", "GeoBox": "geoBox"},
{"FileName": "fileName", "FileID": "fileID", "FileSize": "fileSize", "Updated": "updated", "GeoBox": "geoBox"},
{"FileName": "fileName", "FileID": "fileID", "FileSize": "fileSize", "Updated": "updated", "GeoBox": "geoBox"}]
serializer = MODISLevel1Serializer(data=dict)
and this one throws the same error as the previous one.
I did check some other posts that might be close to this issue, but none of them did work with me:
django rest nested relation in post/put
My json post request looks like the following:
[
{
"ProductLevel": "MOD03",
"SpacecraftType": "Terra",
"StartTimespan": "2013.11.02",
"EndTimespan": "2013.11.02",
"AOI": "swath",
"DegreeNumbers": "POINT(22.5,28.34) POINT(28.64,28.34) POINT(29.4,19.16) POINT(19.19,19.3067)"
}
]
Please notice that in the 'view.py' file I am able to store values in the database but if I bypass the serializer, like the examples that I've shown above.
So did somebody been through the same problem and know how to get this fixed? will be appreciated!