Django Tastypie: Adding multiple models to a single Tastypie Resource - django

I have the following models in my Django Application. The BasicInfo and AddressInfo tables are linked to the Customer table using the id column of the Customer Table, which is the customer_id column in these tables.
For each customer, there can be multiple entries in the AddressInfo or the BasicInfo table.
class Customer(models.Model):
id = models.IntegerField(primary_key=True)
joining_dtm = models.DateTimeField()
isactive = models.IntegerField()
class Meta:
db_table = u'Customer'
class Addressinfo(models.Model):
id = models.IntegerField(primary_key=True)
apt_num = models.CharField(max_length=135, blank=True)
street = models.CharField(max_length=135, blank=True)
street_2 = models.CharField(max_length=135, blank=True)
city = models.CharField(max_length=135, blank=True)
country = models.CharField(max_length=135, blank=True)
postalcode = models.CharField(max_length=135, blank=True)
customer_id = models.ForeignKey(Customer)
class Meta:
db_table = u'AddressInfo'
class Basicinfo(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=135, blank=True)
about = models.CharField(max_length=135, blank=True)
website = models.CharField(max_length=135, blank=True)
customer_id = models.ForeignKey(Customer)
class Meta:
db_table = u'BasicInfo'
How do I create a single Tastypie resource which will enable me to provide all customer tables as an API.
The json object that the resource returns can be something similar to the following for the customer with id 12,
{
"id": 12,
"joining_dtm": "2012-10-25T07:06:54.041528",
"resource_uri": "/public-api/api/v0.1a/customer/1/",
"AddressInfo": [
{
"id":22,
"apt_num": "54",
"street": "Avondale Place",
"street_2": "",
"city": "Chicago",
"country": "India",
"postalcode": "600059",
"customer_id": 12
},
{
"id":96,
"apt_num": "11",
"street": "Infinite Loop",
"street_2": "",
"city": "Cupertino",
"country": "USA",
"postalcode": "123456",
"customer_id": 12
}
],
"BasicInfo": [
{
"id": 33,
"name": "My Full Name",
"about": "Blah Blah Blah",
"website": "http://google.com",
"customer_id": 12
},
{
"id": 147,
"name": "My New Name",
"about": "More Blah Blah",
"website": "http://gmail.com",
"customer_id": 12
}
]
}

If you are simply asking for an API that lists all of your customers:
/public-api/api/v0.1a/customer/?format=json
Should give you that. Otherwise, below is what I assume you were asking - how to set up the resources to show basic info / address info when viewing a customer's endpoint
In your api.py:
class CustomerResource(ModelResource):
# The 2nd parameter is tricky:
# Try suffixing with nothing, 's', or '_set'
# e.g. 'addressinfo', 'addressinfos', 'addressinfo_set'
addressinfos = fields.ToManyField('your_app_name.api.AddressInfoResource', 'addressinfo_set', Full=True)
basicinfos = fields.ToManyField('your_app_name.api.BasicInfoResource', 'basicinfo_set', Full=True)
class Meta:
queryset = Customer.objects.all()
resource_name = 'customer'
class AddressInfoResource(ModelResource):
class Meta:
queryset = Addressinfo.objects.all()
resource_name = 'addressinfo'
class BasicInfoResource(ModelResource):
class Meta:
queryset = Basicinfo.objects.all()
resource_name = 'basicinfo'
And of course add authentication / authorization at each of these resources as needed.

Related

Model with foreign keys taking ~90 seconds per query (foreign key model / serializer problem I think)

I'm having trouble with both my serializer and models for a table using foreign keys. I have a view for my Cost table ( see below ) that when I query, I get the following output in about 300-400 ms :
[
{
"id": 12,
"hours1": 10,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12",
"employee": 14,
"job1": 417,
"job2": 671,
"job3": 671,
"job4": 671
},
{
"id": 13,
"hours1": 8,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12",
"employee": 10,
"job1": 411,
"job2": 671,
"job3": 671,
"job4": 671
}
]
The employee, job1, job2, job3, job4 fields are foreign key IDs that I wish to see their main/primary value for (in this case, names!). I've played around with a serializer to achieve this, however, the problem is that it takes about 90 seconds per query and keeps getting longer!
[
{
"id": 12,
"employee": {
"employee_name": "Person 1"
},
"job1": {
"No": "30201"
},
"job2": {
"No": "N/A"
},
"job3": {
"No": "N/A"
},
"job4": {
"No": "N/A"
},
"hours1": 10,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12"
},
{
"id": 13,
"employee": {
"employee_name": "Person 2"
},
"job1": {
"No": "30101"
},
"job2": {
"No": "N/A"
},
"job3": {
"No": "N/A"
},
"job4": {
"No": "N/A"
},
"hours1": 8,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12"
}
]
My schema and serializers:
class Employee(models.Model):
employee_name = models.CharField(max_length=200, unique=True)
employee_email = models.CharField(max_length=200)
employee_navID = models.CharField(max_length=200)
employee_department = models.CharField(max_length=200)
joining_date = models.DateField()
leaving_date = models.DateField()
weekly_salary = models.IntegerField(default=0)
car_allowance = models.IntegerField()
national_insurance = models.IntegerField()
pension = models.IntegerField()
created_at = models.DateField(auto_now_add=True)
#property
def status(self):
if(self.leaving_date >= date.today()):
return "active"
else:
return "inactive"
#property
def employee_cost(self):
return self.weekly_salary + self.car_allowance + self.national_insurance + self.pension
class Meta:
verbose_name_plural = "Employees"
def __str__(self):
return self.employee_name
class ExcelJobsList(models.Model):
No = models.CharField(max_length=200)
Description = models.CharField(max_length=200)
Custom_No = models.CharField(max_length=200)
Sell_to_Name = models.CharField(max_length=200)
Status = models.CharField(max_length=200)
Person_Responsible = models.CharField(max_length=200)
Region_Code = models.CharField(max_length=200)
Market_Code = models.CharField(max_length=200)
Agreement_Form_Code = models.CharField(max_length=200)
Technology_Code = models.CharField(max_length=200)
secondappr_Code = models.CharField(max_length=200)
Search_Description = models.CharField(max_length=200)
Work_in_Progress_Status = models.CharField(max_length=200)
Job_Category = models.CharField(max_length=200)
Project_Manager = models.CharField(max_length=200)
Sales_Person = models.CharField(max_length=200)
Payment_Terms_Code = models.CharField(max_length=200)
First_Agreement_No = models.CharField(max_length=200)
No_of_Service_Agreements = models.CharField(max_length=200)
CRM_Reference = models.CharField(max_length=200)
class Meta:
verbose_name_plural = "Jobs"
def __str__(self):
return self.No
These are populated with a unique list of employees and jobs respectively. I then use to this to create a basic timesheet form where users can assign hours to a job (up to 4). This is posted to the below model, which has foreign keys to the employee and job table.
class Cost(models.Model):
employee = models.ForeignKey(
Employee, default=1, on_delete=SET_DEFAULT)
job1 = models.ForeignKey(ExcelJobsList, default=0, on_delete=SET_DEFAULT)
hours1 = models.IntegerField()
job2 = models.ForeignKey(
ExcelJobsList, default=0, on_delete=SET_DEFAULT, related_name="job2")
hours2 = models.IntegerField()
job3 = models.ForeignKey(
ExcelJobsList, default=0, on_delete=SET_DEFAULT, related_name="job3")
hours3 = models.IntegerField()
job4 = models.ForeignKey(
ExcelJobsList, default=0, on_delete=SET_DEFAULT, related_name="job4")
hours4 = models.IntegerField()
date = models.DateField()
This produces the follow output after I've selected the fields I wanted from a serializer:
class JobModelSerializer(serializers.ModelSerializer):
class Meta:
model = ExcelJobsList
fields = ['No']
class EmployeeModelSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = ['employee_name']
class CostModelSerializer(serializers.ModelSerializer):
employee = EmployeeModelSerializer()
job1 = JobModelSerializer()
job2 = JobModelSerializer()
job3 = JobModelSerializer()
job4 = JobModelSerializer()
class Meta:
model = Cost
fields = ('__all__')
class CostListFilter(filters.FilterSet):
class Meta:
model = Cost
fields = {
'employee': ['exact'],
}
class JoinedFilterCostList(generics.ListAPIView):
queryset = Cost.objects.filter()
serializer_class = CostModelSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = CostListFilter
I've tried using to_field= 'xyz', however, this ends up returning null so either I'm missing something in my model or the serializer isn't set up correctly for it... or both!
Any help would be much really appreciated.
The slowness is caused by having to hit the database 4 times per Cost instance to get all the related ExcelJobsList.
To avoid this and make it more efficient, you can use select_related to the related ExcelJobsList like this:
class JoinedFilterCostList(generics.ListAPIView):
...
queryset = Cost.objects.select_related('job1', 'job2', 'job3', 'job4')
...
These should in turn end up with just one query, with inner joins on the related ExcelJobsList.

How to add model instances to a Foreign Key field in Django Rest Framework

I need to get the ItemModel instances from the item model and store them in the foreign key field in the OrderModel but I am not sure how. I've tried to iterate through the item order list and add one but it doesn't display correctly. Any help would be much appreciated.
My goal is to display the data like this:
{
"payment_method": "test",
"confirmation": "14087147WA285750M",
"total_price": "15.00",
"is_paid": "True",
"order_created": "2021-07-09T19:51:18Z",
"item_order": [
{
"id": 2,
"name": "Carrots",
"image": "image link",
"slug": "carrots",
"price": "5.00",
"itemID": "ct1",
"quantity": 1
},
{
"id": 8,
"name": "Dog Food",
"image": "image link",
"slug": "dog-food",
"price": "10.00",
"itemID": "df4",
"quantity": 1
}
]
}
View:
#api_view(['POST'])
def create_order(request):
user = request.user
order = request.data
order_item = OrderModel.objects.create(
user=user,
payment_method=order['payment_method'],
confirmation=order['confirmation'],
total_price=order['total_price'],
is_paid=order['is_paid'],
order_created=order['order_created'],
item_order= """ The item model instance """
)
ordered_items = OrderSerializer(order_item, many=True).data
return Response(ordered_items)
Order Model:
class OrderModel(models.Model):
user = models.ForeignKey(CustomerModel, on_delete=models.CASCADE)
item_order = models.ForeignKey(
ItemModel, on_delete=models.CASCADE)
payment_method = models.CharField(max_length=50)
confirmation = models.CharField(max_length=255)
total_price = models.DecimalField(
max_digits=5, decimal_places=2)
is_paid = models.BooleanField(default=False)
has_been_sent = models.BooleanField(default=False)
order_created = models.DateTimeField()
def __str__(self):
return str(self.id)
Order Serializer:
class OrderSerializer(serializers.ModelSerializer):
item_order = ItemSerializer(many=True)
class Meta:
model = OrderModel
fields = ['payment_method', 'confirmation',
'total_price', 'is_paid', 'has_been_sent', 'order_created',
'item_order']
read_only_fields = ['id']

How to count the distinct of model_set in Django?

I've looked into this question, but I think it's different.
Let me explain a bit further. I have a serializer called DetailTrackSerializer to serialize my Track model, and I've nested a TaggedSerializer in DetailTrackSerializer.
class DetailTrackSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=120)
link = serializers.URLField(max_length=120)
tagged_set = TaggedSerializer(many=True)
artist = ArtistSerializer()
class Meta:
model = Track
fields = ('id', 'artist', 'title', 'link', 'tagged_set',)
class TaggedSerializer(serializers.ModelSerializer):
tag = TagSerializer()
class Meta:
model = Tagged
fields = ('tag',)
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('name',)
Currently, this DetailTrackSerializer is returning a json like this
{
"tracks": [
{
"id": 168,
"artist": {
"id": 163,
"name": "Gob"
},
"title": "Face the Ashes",
"link": "",
"tagged_set": [
{
"tag": {
"id": 1356,
"name": "punk rock"
}
},
{
"tag": {
"id": 1356,
"name": "punk rock"
}
},
{
"tag": {
"id": 1356,
"name": "punk rock"
}
},
...
The list goes on, if there are 100 "punk rock" tag in this track, it will shows up 100 times and there may be another tag also not only "punk rock". What I need is something like this
{
"tracks": [
{
"id": 168,
"artist": {
"id": 163,
"name": "Gob"
},
"title": "Face the Ashes",
"link": "",
"tagged_set": [
{
"tag": {
"id": 1356,
"name": "punk rock"
},
"frequency": 100,
},
{
"tag": {
"id": 546,
"name": "pop"
},
"frequency": 236,
},
...
Each tag only appears once, and has its frequency.
Note: I'm using Django Rest Framework as well
Edit: models.py
class Tagged(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
track = models.ForeignKey(Track, on_delete=models.CASCADE)
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
class Track(models.Model):
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
link = models.URLField(max_length=255, blank=True)
tags = models.ManyToManyField(Tag, through='Tagged', blank=True)
From your Tagged I understood that there are big chances of Data Redundancy, that's why your tagged_set is showing multiple times.
What I'm trying to say is, this is not a Representation Problem with your serializer, rather than it's an Implementation Problem with your Models.
So, unique_together attribute will solve the problem, as
class Tagged(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
track = models.ForeignKey(Track, on_delete=models.CASCADE)
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
class Meta:
unique_together = ('track', 'tag')
After changing the models, please do makemigrations and migration.
Note: While doing migration you may come acrross django.db.utils.IntegrityError: UNIQUE constraint failed exception. So, delete all entries in the Tagged model
After reading through Django's docs about querysets, this is the solution I came up with
class DetailTrackSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=120)
link = serializers.URLField(max_length=120)
tags_frequency = serializers.SerializerMethodField()
artist = ArtistSerializer()
def get_tags_frequency(self, track):
tags = track.tags.all()
return tags.values('id', 'name').annotate(Count('id'))
class Meta:
model = Track
fields = ('id', 'artist', 'title', 'link', 'tags_frequency',)
which will give me json representation like this
{
"tracks": [
{
"id": 168,
"artist": {
"id": 163,
"name": "Gob"
},
"title": "Face the Ashes",
"link": "",
"tags_frequency": [
{
"name": "punk rock",
"id": 1356,
"id__count": 100
},
{
"name": "punk",
"id": 1357,
"id__count": 60
}
]
},
{
"id": 169,
"artist": {
"id": 164,
"name": "Jeff And Sheri Easter"
},
"title": "The Moon And I (Ordinary Day Album Version)",
"link": "",
"tags_frequency": []
},
Edwin Harly, you have some data overlap between Track, Tag and Tagged model. If you accept, I suggest you remove Tagged model. and if you want to save which user create tag, add user field in Tag model.
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
class Track(models.Model):
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
link = models.URLField(max_length=255, blank=True)
tags = models.ManyToManyField(Tag, through='Tagged', blank=True)
Then, you can your serializers like this:
class DetailTrackSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=120)
link = serializers.URLField(max_length=120)
tags = TagSerializer(many=True)
artist = ArtistSerializer()
class Meta:
model = Track
fields = ('id', 'artist', 'title', 'link', 'tags',)
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('name',)

django rest-framework serializer reverse relation

I am programming django based web site using django rest-framework.
I want to use rest-framework to get model's data.
this is my model.py
class TimeTable(models.Model):
subject_name = models.CharField(max_length=50)
subject_code = models.CharField(max_length=10, unique=True)
classification = models.CharField(max_length=50)
professor = models.CharField(max_length=50)
department = models.CharField(max_length=50)
credit = models.CharField(max_length=1)
year = models.CharField(max_length=4, default='2018')
semester = models.CharField(max_length=1, default='1')
def __str__(self):
return self.subject_code + '-' + self.subject_name
class Class(models.Model):
owner = models.ForeignKey(Profile, null=True)
timetable = models.ForeignKey(TimeTable, null=True)
grade = models.FloatField()
this is serializer.py
class TimeTableSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TimeTable
fields = ('url', 'subject_name', 'subject_code', 'classification', 'professor', 'department', 'credit', 'year', 'semester')
class ClassSerializer(serializers.HyperlinkedModelSerializer):
timetables = TimeTableSerializer(read_only=True)
class Meta:
model = Class
fields = ('url','owner', 'timetable', 'grade', 'timetables')
I want to get JSON response Like this
(http://localhost:8000/api/classes/)
[
{
"url": "http://localhost:8000/api/classes/8/",
"owner": "http://localhost:8000/api/profiles/19/",
"timetable": "http://localhost:8000/api/timetables/3/",
"grade": 4.5
"timetables": {
"url": "http://localhost:8000/api/timetables/3/",
"subject_name": "Artificial Inteligence",
"subject_code": "3413513413",
"classification": "major",
"professor": "John Lee",
"department": "software",
"credit": "3",
"year": "2018",
"semester": "1"
}
}
]
but i got this
[
{
"url": "http://localhost:8000/api/classes/8/",
"owner": "http://localhost:8000/api/profiles/19/",
"timetable": "http://localhost:8000/api/timetables/3/",
"grade": 4.5
}
]
How Can I get TimeTable's JSON data in Class JSON??
class ClassSerializer(serializers.HyperlinkedModelSerializer):
timetable = TimeTableSerializer(read_only=True)
class Meta:
model = Class
fields = ('url','owner', 'timetable', 'grade')

How can i serialize manytomany django models using DRF

How can i get products name and id instead of pro_id and ord_id in output ? but not in string type. for example : "name : somebook" is not a valid option for me.
I just working on this for 2 days without break and i think im missing a little detail but i cant find what is it.
Output i want
[
{
"order_id": 1,
"totalquantity": 12,
"totalprice": 56,
"userid": 1,
"customerAddress": "evka1",
"customerPhone": "539",
"trackNo": 12034,
"products": [
{
"name": "somebook",
"id": 1,
"quantity": 6
},
{
"name": "someotherbook",
"id": 2,
"quantity": 6
}
]
}
]
Output i get
[
{
"order_id": 1,
"totalquantity": 12,
"totalprice": 56,
"userid": 1,
"customerAddress": "evka1",
"customerPhone": "539",
"trackNo": 12034,
"products": [
{
"pro_id": 2,
"ord_id": 1,
"quantity": 6
},
{
"pro_id": 3,
"ord_id": 1,
"quantity": 6
}
]
}
]
Order model
class Order(models.Model):
order_id = models.AutoField(primary_key=True)
totalquantity = models.IntegerField(default=0, null=True)
totalprice = models.IntegerField(default=0, null=True)
userid = models.IntegerField(default=0, null=True)
trackNo = models.IntegerField(default=0, null=True)
billNo = models.IntegerField(default=0, null=True)
customerAddress = models.CharField(max_length=30,default="nil", null=True)
customerPhone = models.CharField(max_length=30,default="nil", null=True)
Order Serializer
class OrderSerializer(serializers.ModelSerializer):
products = ProductOrderSerializer(many=True, read_only=True)
class Meta:
model = Order
fields = ('order_id', 'totalquantity', 'totalprice', 'userid', 'customerAddress', 'customerPhone', 'trackNo', 'products')
Product Model
class Product(models.Model):
name = models.CharField(max_length=30,default="nil", null=True)
author = models.CharField(max_length=30,default="nil", null=True)
date = models.DateField(null=True)
price = models.FloatField(default=0, null=True)
quantity = models.IntegerField(default=0, null=True)
soldcount = models.IntegerField(default=0, null=True)
category = models.CharField(max_length=30,default="nil", null=True)
Product Serializer
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id', 'name', 'author', 'date', 'price', 'quantity', 'soldcount', 'category')
ProductOrder Model
class ProductOrder(models.Model):
pro_id = models.IntegerField(default=0,null=True)
ord_id = models.ForeignKey(Order, related_name='products')
quantity = models.IntegerField(default=0, null=True)
ProductOrder Serializer
class ProductOrderSerializer(serializers.ModelSerializer):
class Meta:
model = ProductOrder
fields = ('pro_id', 'ord_id', 'quantity')
The output you desired can be achieved with nested serializers in django-rest-framework,
But, it needs some refactoring in your models,
You need to add a ForeignKey to the model ProductOrder towards the Product model, inorder to attain a ManyToMany relation,
class ProductOrder(models.Model):
pro_id = models.ForeinKey(Product, related_name='orders', default=0,null=True)
ord_id = models.ForeignKey(Order, related_name='products')
quantity = models.IntegerField(default=0, null=True)
Also, in your serializers,
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('name', 'id', 'quantity')
class ProductOrderSerializer(serializers.ModelSerializer):
details = ProductSerializer(source='pro_id', read_only=True)
class Meta:
model = ProductOrder
fields = ('details', )
class OrderSerializer(serializers.ModelSerializer):
products = ProductOrderSerializer(many=True, read_only=True)
class Meta:
model = Order
fields = ('totalquantity', 'totalprice', 'userid',
'trackNo', 'billNo', 'customerAddress', 'customerPhone',
'products')
You could call OrderSerializer and get the output as you desired,
def get(self, request, *args, **kwargs):
orders = Order.objects.all()
serializer = OrderSerializer(orders, many=True)
return Response(serializer.data)
The output will be like this,
[
{
"totalquantity": 0,
"totalprice": 0,
"userid": 0,
"trackNo": 0,
"billNo": 0,
"customerAddress": "nil",
"customerPhone": "nil",
"products": [
{
"details": {
"name": "nil",
"id": 1,
"quantity": 0
}
},
{
"details": {
"name": "nil",
"id": 1,
"quantity": 0
}
}
]
}
]
I solved the problem when i changed ProductOrderSerializer to :
class ProductListingSerializer(serializers.RelatedField):
def to_representation(self, value):
dict={}
dict['name'] = value.pro_id.name
dict['id'] = value.pro_id.pk
dict['quantity'] = value.quantity
return dict
but im in "my code works i don't know why" situation now :D