I need to get the value of the foreignkey field not as an id but as a value of one of the fields of the linked model.
models.py
class RfiParticipation(models.Model):
...
vendor = models.ForeignKey('Vendors', models.DO_NOTHING, related_name='to_vendor')
m = models.ForeignKey('Modules', models.DO_NOTHING, related_name='to_modules')
...
class Modules(models.Model):
MODULES_NAME = (....
)
mid = models.AutoField(primary_key=True)
module_name = models.CharField(max_length=50, choices=MODULES_NAME, unique=True)
serializer.py
class VendorsManagementListSerializer(serializers.ModelSerializer):
company_information = serializers.SerializerMethodField()
vendor_modules = serializers.SerializerMethodField()
class Meta:
model = Vendors
fields = ('vendor_name',
...
'company_information',
'vendor_modules',)
def get_vendor_modules(self, obj):
r = RfiParticipation.objects.filter(vendor=obj).order_by('rfi').values('m', 'rfi')
return r
Now this request r = RfiParticipation.objects.filter(vendor=obj).order_by('rfi').values('m', 'rfi') returns to me:
"vendor_modules": [
{
"m": 2,
"rfi": "20R1"
},
{
"m": 3,
"rfi": "20R1"
},
{
"m": 4,
"rfi": "20R1"
}
]
How I can make m: module_name instead of m: 2? Where module_name is the field from Modules model.
I try .values('m.module_name', 'rfi') but got
Cannot resolve keyword 'm.module_name' into field. Choices are: active, id, m, m_id, rfi, rfi_id, timestamp, user_id, vendor, vendor_id
You can use SlugRelatedField here. For example:
class RFISerializer(serializers.ModelSerializer):
to_modules = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='module_name'
) # using to_modules as it was used as related field
class Meta:
model = RfiParticipation
fields = ['rfi', 'to_modules']
class VendorsManagementListSerializer(serializers.ModelSerializer):
vendor_modules = RFISerializer(source='m')
class Meta:
model = Vendors
fields = ('vendor_name',
...
'vendor_modules',)
Nice decision )
r = RfiParticipation.objects.filter(vendor=obj).order_by('rfi').values(module=F('m__module_name'), round=F('rfi'))
Related
I want to return Object details and Aggregated data under the same Django rest api. Can it be possible from ModelViewSet? If not what is the correct way of doing this?
Here is my Code.
#Tin
class TinTable(CoreModel):
tin=models.CharField(unique=True, max_length=30)
assessee_name=models.CharField(max_length=500)
def __str__(self):
return self.tin
# Financial Year
class FinancialYear(CoreModel):
financial_year = models.CharField(max_length = 50)
class Meta:
ordering = ["start_year"]
def __str__(self):
return self.financial_year
Ledger Class
# Ledger
class Ledger(CoreModel):
UNDISPUTED = 0
APPEAL = 1
TRIB = 2
HC = 3
ONE_SEVENTY_THREE = 4
ONE_TWEENTY_ONE = 5
ADR = 6
Ledger_SECTION_CHOICES = (
(UNDISPUTED, 'Undisputed'),
(APPEAL, 'Appeal'),
(TRIB, 'Tribunal'),
(HC, 'High Court'),
(ONE_SEVENTY_THREE, '173'),
(ONE_TWEENTY_ONE, '121/A'),
(ADR, 'ADR')
)
tin=models.ForeignKey(TinTable, on_delete = models.CASCADE)
financial_year = models.ForeignKey(FinancialYear, on_delete = models.CASCADE)
demand = models.FloatField(default = 0.0 ) #demand
disputed_demand = models.FloatField(default=0.0)
payment = models.FloatField(default = 0.0 ) #payment
balance = models.FloatField(default = 0.0 )
status = models.PositiveSmallIntegerField(
choices=Ledger_SECTION_CHOICES, default=UNDISPUTED)
comment = models.CharField(max_length=500, blank=True)
def __str__(self):
return self.tin.tin
Serializer of Ledger
class LedgerSerializer(serializers.ModelSerializer):
financial_year = serializers.PrimaryKeyRelatedField(
queryset=FinancialYear.objects.all(), source='financial_year', write_only=True
)
tin = serializers.PrimaryKeyRelatedField(
queryset=TinTable.objects.all(), source='tin', write_only=True
)
assessee_name = serializers.ReadOnlyField(
source='tin.assessee_name'
)
class Meta:
model = Ledger
fields = ('tin','assessee_name','financial_year','demand','disputed_demand', 'payment','status')
View of Ledger
class LedgerView(viewsets.ModelViewSet):
serializer_class = LedgerSerializer
queryset = Ledger.objects.active()
The output I want
{
"results":[
{
"tin":"222222222222",
"name":"Karim",
"total_disputed": 5000,
"total_undisputed": 900,
"total_payment": 500,
"financial_details":[
{
"year": "2017-2018",
"undisputed":900,
"disputed":0,
"payment":500,
"status":"undisputed"
},
{
"year": "2018-2019",
"undisputed":0,
"disputed":5000,
"payment":0,
"status":"disputed"
}
]
},
{
"tin":"3333333333333",
"name":"Rahim",
"total_disputed": 8000,
"total_undisputed": 1900,
"total_payment": 1500,
"financial_details":[
{
"year": "2017-2018",
"undisputed":900,
"disputed":3000,
"payment":500,
"status":"undisputed"
},
{
"year": "2018-2019",
"undisputed":1000,
"disputed":5000,
"payment":1000,
"status":"disputed"
},
........ # more data
]
},
........ # more data
]
}
It would be so helpful to get a detailed answer for it. Thanks in advance.
You need to write a serializer to serialize financial_details, for ex:
class FinancialYearSerializer(serializers.ModelSerializer):
class Meta:
model = FinancialYear
fields = # your desired fields...
class LedgerSerializer(serializers.ModelSerializer):
financial_details = FinancialYearSerializer(source='financial_year', read_only=True, many=True)
# the rest of your fields
class Meta:
model = Ledger
fields = ('tin','assessee_name','financial_year','demand','disputed_demand', 'payment','status')
For other fields which will hold aggregated data, you can use SerializerMethodField() as the docs describe.
TIP:
You should add related_name to your ForeignKey() fields for reverse relations, Django is already creating it with default naming but it's better to set yours.
tin = models.ForeignKey(TinTable, on_delete = models.CASCADE, related_name='ledgers')
financial_year = models.ForeignKey(FinancialYear, on_delete = models.CASCADE, related_name='ledgers')
I want to design solution for ordering items. I have endpoint that create orders BUT I need to to have items object in the order. let me show you the code
class ItemModel(models.Model):
name = models.CharField(max_length=50)
price = models.FloatField()
discretion = models.CharField(max_length=500)
available = models.BooleanField(default=True)
class OrderModel(models.Model):
phone = models.CharField(max_length=20)
delevary_time = models.DateTimeField()
class CartModel(models.Model):
order = models.ForeignKey(OrderModel, on_delete=models.CASCADE, related_name='order_m')
item = models.ForeignKey(ItemModel, on_delete=models.CASCADE, related_name='item_m')
I need endpoint that create order to me. her what I did
class CartSerializer(serializers.ModelSerializer):
class Meta:
model = CartModel
exclude = ['order',]
depth = 2
class OrderSerializer(serializers.ModelSerializer):
cart = serializers.SerializerMethodField()
class Meta:
model = OrderModel
fields = ['phone', 'state', 'delevary_time', 'cart']
def get_cart(self, obj):
cart = CartModel.objects.filter(order__id=obj.id)
serializer = CartSerializer(cart, many=True)
return serializer.data
this is the endpoint
router.register('order', OrderViewSet, 'api-order')
{
"phone": 124997988698,
"delevary_time": "2020-07-17T19:34:00",
"cart": [
{
"item": 1
},
{
"item": 2
}
]
}
when I post the json it don't save the cart it only save the oder phone and delevary_time. How I can save the cart at the same time
class CartSerializer(serializers.ModelSerializer):
class Meta:
model = CartModel
exclude = ['order',]
depth = 2
class OrderSerializer(serializers.ModelSerializer):
order_m = CartSerializer(many=True) # adding this
class Meta:
model = OrderModel
fields = ['phone', 'state', 'delevary_time', 'order_m']
def create(self, validated_data):
cart_data = validated_data.pop('order_m')
order = OrderModel.objects.create(**validated_data)
for c in cart_data:
CartModel.objects.create(order=order, **c)
return order
i have applied join on two tables with following query,
VIEWS.PY
class performance(viewsets.ModelViewSet):
queryset = Leads.objects.select_related('channelId'
).values("channelId__channelName").annotate(tcount=Count('channelId'))
serializer_class = teamwise_lead_performance_serializer
but i am unable to catch response using this serializers,
SERIALIZER.PY
class channel_serializer(serializers.ModelSerializer):
class Meta:
model = Channels
fields = ['channelName']
class performance_serializer(serializers.ModelSerializer):
tcount = serializers.IntegerField()
channel = channel_serializer(many=True, read_only=True)
class Meta:
model = Leads
fields = ['tcount', 'channel']
actual results:
[
{
"tcount": 88
},
{
"tcount": 25
},
{
"tcount": 31
},
...
]
expected results:
[
{
"channelName": "abc",
"tcount": 88
},
{
"channelName": "def",
"tcount": 25
},
{
"channelName": "ghi",
"tcount": 31
},
...
]
i have tried the following:
How to join two models in django-rest-framework
Models.py
class Channels(models.Model):
id = models.IntegerField(primary_key=True)
channelName = models.CharField(max_length=20, default=None)
class Meta:
db_table = "table1"
class Leads(models.Model):
id = models.IntegerField(primary_key=True)
channelId = models.ForeignKey(Channels, on_delete=models.CASCADE, db_column='channelId')
class Meta:
db_table = "table2"
why is it not getting the channelName in response?
what am i doing wrong here?
Thank you for your suggestions
Edit
When I try Mehren's answer, I get the following error:
KeyError when attempting to get a value for field channelName on serializer performance_serializer. The serializer field might be named incorrectly and not match any attribute or key on the dict instance.Original exception text was: 'channelId'.
I managed to get it working with the following:
class performance_serializer(serializers.ModelSerializer):
tcount = serializers.IntegerField()
channelName = serializers.CharField(source='channelId__channelName')
class Meta:
model = Leads
fields = ['tcount', 'channelName']
class performance(viewsets.ModelViewSet):
queryset = Leads.objects.select_related('channelId'
).values("channelId__channelName").annotate(tcount=Count('channelId'))
serializer_class = performance_serializer
That being said, I would strongly encourage you to follow both PEP and Django naming conventions.
Here is what your code would look like following said conventions:
class Channel(models.Model):
id = models.IntegerField(primary_key=True)
channel_name = models.CharField(max_length=20, default=None)
class Lead(models.Model):
id = models.IntegerField(primary_key=True)
channel = models.ForeignKey(Channel, on_delete=models.CASCADE)
class PerformanceSerializer(serializers.ModelSerializer):
channel_count = serializers.IntegerField()
channel_name = serializers.CharField(source='channel__channel_name')
class Meta:
model = Lead
fields = ['channel_count', 'channel_name']
class PerformanceViewSet(viewsets.ModelViewSet):
queryset = Lead.objects.select_related('channel'
).values("channel__channel_name").annotate(channel_count=Count('channel'))
serializer_class = PerformanceSerializer
The main takeaway from this is to not change the default name of your ForeignKey columns! It makes working with related models much more confusing, and is possibly the reason for your problem in the first place (although I couldn't prove it).
If you want to only get the channelName, then it's better to use
channelName = serializers.CharField(source='channelId.channelName')
Also, please fix your syntax. You are not following the pep8 standards.
EDIT
class PerformanceSerializer(serializers.ModelSerializer):
tcount = serializers.IntegerField()
channelName = serializers.CharField(source='channelId.channelName')
class Meta:
model = Leads
fields = ['tcount', 'channelName']
EDIT
queryset = Leads.objects.select_related('channelId').values("channelId__channelName").annotate(tcount=Count('channelId'))
Remove the .values("channelId__channelName") part from your view
I have the following models:
class Asset(models.Model):
isin = models.CharField(max_length=100)
asset_type = models.CharField(max_length=50)
last_price = models.FloatField
security_weight = models.FloatField
update_date = models.DateTimeField
def __str__(self):
return self.isin
class Meta:
ordering = ('isin',)
class PortfolioElement(models.Model):
nominal = models.FloatField
weight = models.FloatField
asset = models.OneToOneField(
Asset,
on_delete=models.CASCADE,
primary_key=True,
)
def __str__(self):
return self.asset.isin
class Meta:
ordering = ('asset',)
class Portfolio(models.Model):
number = models.CharField(max_length=100)
update_date = models.DateTimeField
elements = models.ManyToManyField(PortfolioElement)
def __str__(self):
return self.number
class Meta:
ordering = ('number',)
class Client(models.Model):
number = models.CharField(max_length=100)
update_date = models.DateTimeField
portfolios = models.ManyToManyField(Portfolio)
def __str__(self):
return self.number
class Meta:
ordering = ('number',)
and the following serializer:
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = ('number', 'portfolios')
depth = 1
However, I would like to see the actual data in the portfolios (and portfolio elements). But when I try to make a GET request on an arbitrary Client (by the (Client).number field) I can only see the following:
{
"number": "28101317",
"portfolios": [
{
"id": 14,
"number": "5471-339425",
"elements": [
{
"asset": 326
},
{
"asset": 327
}, ... (and so on)
How can a tweak my code, so that I also can get the actual "asset" information?
/Niclas
You can try this:
class AssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = '__all__'
class PortfolioElementSerializer(serializers.ModelSerializer):
asset = AssetSerializer()
class Meta:
model = PortfolioElement
fields = ('nominal', 'weight', 'asset')
class PortfolioSerializer(serializers.ModelSerializer):
elements = PortfolioElementSerializer(many=True)
class Meta:
model = Portfolio
fields = ('number', 'update_date', 'elements')
class ClientSerializer(serializers.ModelSerializer):
portfolios = PortfolioSerializer(many=True)
class Meta:
model = Client
fields = ('number', 'portfolios')
Model
class makitVerifications (models.Model):
Matter_fk = models.ForeignKey(Matter,on_delete=models.CASCADE , related_name = "makit_validate")
user_fk = models.ForeignKey('auth.User',on_delete=models.CASCADE , related_name = 'verification_by' , )
Serializer
class UserVerifyCountSerializer (serializers.ModelSerializer):
# Tried makit_validate = makitSerializer (read_only = True)
class Meta :
model = makitVerifications
fields = ( 'user_fk', 'makit_validate', 'Matter_fk')
UserVerifyCountSerializer._declared_fields['makit_validate']= makitSerializer (read_only = True)
View
class UserVerfiyCountViewSet(generics.ListCreateAPIView):
serializer_class = UserVerifyCountSerializer
def get_queryset(self):
queryset = makitVerifications.objects.all()
return queryset
JSON
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"user_fk": 1,
"Matter_fk": 1
},
{
"user_fk": 1,
"Matter_fk": 1
}
]
The field "makit_validate" which is expected as nested object is missing in the JSON .How can I get nested object of the related model in json .
I also tried an example from the docs but the same field is missing from JSON
Also how to omit/disable fields from the objected being nested in the above serializer class
Makie Serilazer
class makitSerializer(serializers.ModelSerializer):
kamaz = kamazSerializer(many=True,read_only=True)
facilities = FacilitiesSerializer (many =True,read_only=True)
class Meta :
model = Makit
fields = '__all__'
## Makit Model ##
class Makit (models.Model):
pincode = models.IntegerField()
area = models.CharField(max_length=200)
street = models.CharField(max_length=200)
landmark = models.CharField(max_length=100,blank=True , null = True)
contact_no= models.BigIntegerField()
From doc here: Serializer fields
Try:
makit_validate = makitSerializer(source="the_field_you_find_makit" read_only = True)