I did not find a solution although I looked at the questions asked...
When I use this serializer:
class MessageSerializer(ModelSerializer):
sender = UserMobileSerializer(read_only=True)
class Meta:
model = Messages
fields = '__all__'
I get something like this:
{
"id": 62,
"sender": {
"pk": 12,
"email": "john#gmail.com",
"full_name": "John",
"profile_photo": null
},
"created_at": "2018-04-29T00:54:50.437662",
"message": "sdkjnasljdhkajsjdlasdasda",
"read_at": false,
"target": 18
}
I would like the target field to be like sender, that is: display the full user information instead of just the ID.
I tried to add this line: target = UserMobileSerializer(), but I still get only the ID in the output. I also tried target = UserMobileSerializer(read_only=True) but nothing changed.
You are not adding the field you defined to the fields in the serializer's Meta. Try this:
class MessageSerializer(ModelSerializer):
sender = UserMobileSerializer(read_only=True)
class Meta:
model = Messages
fields = ('your', 'fields', 'sender')
EDIT: You need to serialize target, too. Like this:
class TargetSerializer(ModelSerializer):
class Meta:
model = Target
fields = ('id', 'title') # Add fields you want to get in the response.
class MessageSerializer(ModelSerializer):
target = TargetSerializer(read_only=True) # You should have TargetSerializer defined
sender = UserMobileSerializer(read_only=True)
class Meta:
model = Messages
fields = ('your', 'fields', 'target', 'sender')
Related
I want to add Dynamic Field Array serializer in my drf project:
My get response looks something like this:
{
"title": "some",
"created_at": "2022-03-06T15:59:52.684469Z",
"fields": [
{
"id": 1,
"title": "Some title?",
"parent_field": 1
},
{
"id": 2,
"title": "Yet another fields",
"parent_field": 1
}
]
}
This is the item detail serializer, and fields is another model serializer. I achieved this by using this code:
class AnotherFieldSerializer(serializers.ModelSerializer):
class Meta:
model = AnotherModel
fields = "__all__"
class FirstSerializer(serializers.ModelSerializer):
fields = serializers.SerializerMethodField()
class Meta:
model = FirstModel
fields = "__all__"
def get_fields(self, obj):
serializer_context = {'request': self.context.get('request')}
children = obj.fields
if not children.exists():
return None
serializered_children = FirstSerializer(
children,
many=True,
context=serializer_context
)
return serializered_children.data
This works only for GET requests I want this to also work with POST and PUT requests. So, imagine I want to add/edit an item into my model FIRST model and add fields associated with it by just sending this JSON:
{
"title": "some",
"created_at": "2022-03-06T15:59:52.684469Z",
"fields": [
{
"id": 1,
"title": "Some title?",
},
{
"id": 2,
"title": "Yet another fields",
}
]
}
I know I can get fields from the response and loop through each item to create an instance of Another Model but fields validation will be much harder I think. But if there's more of a drf of doing this thing then it would be great. Also, I have no problem with making another serializer just for POST and PUT requests.
I hope my English was understandable.
You can create a new serializer for validation. To validate multiple items you can do the following,
class PostSerializer(serializers.Serializer):
title = serializers.CharField(max_length=200)
created_at = serializers.DateTimeField()
fields = FirstSerializer(many=True) # your model serializer for the FirstModel
Ref : https://www.django-rest-framework.org/api-guide/serializers/#listserializer
I'm searching for a solution to create a serializer / API Endpoint to represent data in a custom order. When adding serializers and viewsets to DRF, I only get the fields associated with that Model. But what I like to have is a custom structure of all my models together. As an example:
I have a model called season, a model called evenings and a model called events. Now I'd like to have an API Endpoint to have that all together, like so:
{
"requestTime": "2021-11-09 08:20",
"requestURL": "/all",
"requestMethod": "GET",
"responseCode": 200,
"season": "2021/2022",
"evenings": [
{
"evevning_id": 0,
"day": "",
"date": "2021-11-11",
"event_count": 2,
"events": [
{},
{}
]
}
]
}
For data structure in the models I have some ForeignKeys like:
season
|
evening
|
event
Any suggestions how to achieve this?
Use nested serializer (Season > Evening > Event) like this.
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = models.Event
fields = ['id',...]
class EveningSerializer(serializers.ModelSerializer):
events = EventSerializer(many=True)
class Meta:
model = models.Evening
fields = ['id', 'day', 'date','events',...]
class SeasonSerializer(serializers.ModelSerializer):
evenings = EveningSerializer(many=True)
class Meta:
model = models.Season
fields = ['id', 'season', 'evenings',...]
make sure when fetching season from database, use prefetch related in queryset.
My conceptual model is that there are DemanderFeature objects which have LoadCurve objects linked to them in a many-to-many relationship, along with a single attribute indicating "how many times" the two are associated, using an attribute in the many-to-many relationship called number.
I have been struggling for quite a while now, reading many answers on stackoverflow but I just cannot get it to work in exactly the way that I want. This is my desired output, when looking at the detail view of a DemanderFeature:
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"name": "testdemander",
"loadcurves": [
{"name": "lc", "number": 5},
{"name": "lc2", "number": 10}
],
// Other DemanderFeature fields removed for brevity...
}
]
The closest I have been able to get to this is with this setup:
Models
class LoadCurve(models.Model):
created = models.DateTimeField(auto_now_add=True)
finalized = models.BooleanField(default=False)
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
name = models.CharField(max_length=100)
length = models.IntegerField(default=0)
deleted = models.BooleanField(default=False)
demanderfeatures = models.ManyToManyField("DemanderFeature", through="DemanderFeatureToLoadCurveAssociation")
class Meta:
ordering = ['name']
constraints = [
models.UniqueConstraint(fields=["owner", "name"], condition=models.Q(deleted=False), name="loadcurve_unique_owner_name")
]
class DemanderFeature(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100)
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
demanderfeaturecollection = models.ForeignKey(DemanderFeatureCollection, on_delete=models.CASCADE)
loadcurves = models.ManyToManyField("LoadCurve", through="DemanderFeatureToLoadCurveAssociation")
deleted = models.BooleanField(default=False)
geom = gis_models.PointField(default=None)
class Meta:
ordering = ['name']
constraints = [
models.UniqueConstraint(fields=["owner", "demanderfeaturecollection", "name"], condition=models.Q(deleted=False),
name="demanderfeature_unique_owner_demanderfeaturecollection_name")
]
class DemanderFeatureToLoadCurveAssociation(models.Model):
loadcurve = models.ForeignKey(LoadCurve, on_delete=models.CASCADE)
demanderfeature = models.ForeignKey(DemanderFeature, on_delete=models.CASCADE)
number = models.IntegerField()
Serializers
(I am using __all__ for the sake of debugging, so that I can see everything that is being serialized and available)
class LoadCurveSerializer(serializers.ModelSerializer):
class Meta:
model = LoadCurve
fields = "__all__"
class DemanderFeatureToLoadCurveAssociationSerializer(serializers.ModelSerializer):
class Meta:
model = DemanderFeatureToLoadCurveAssociation
fields = "__all__"
class DemanderFeatureSerializer(serializers.ModelSerializer):
demanderfeaturecollection = serializers.SlugRelatedField(slug_field="name", queryset=DemanderFeatureCollection.objects.all())
loadcurves = LoadCurveSerializer(read_only=True, many=True)
# loadcurves = DemanderFeatureToLoadCurveAssociationSerializer(read_only=True, many=True)
class Meta:
model = DemanderFeature
fields = "__all__"
lookup_field = "name"
There is a commented line in the previous code block which I was trying to use to get the DemanderFeatureToLoadCurveAssociationSerializer because I thought this would be the proper way to get the number field which its related model defines, but when I uncomment this line (and comment the line just below it) I only get this error:
AttributeError at /demanderfeatures/
Got AttributeError when attempting to get a value for field `number` on serializer `DemanderFeatureToLoadCurveAssociationSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `LoadCurve` instance.
Original exception text was: 'LoadCurve' object has no attribute 'number'.
If I do not swap those lines, however, I get this as a result:
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"name": "testdemander",
"loadcurves": [
{
"id": 1,
"created": "2020-12-29T11:29:11.585034Z",
"finalized": true,
"name": "lc",
"length": 0,
"deleted": false,
"owner": 1,
"demanderfeatures": [
1
]
},
{
"id": 2,
"created": "2020-12-29T12:46:31.044624Z",
"finalized": true,
"name": "lc2",
"length": 0,
"deleted": false,
"owner": 1,
"demanderfeatures": [
1
]
}
],
// Other DemanderFeature fields removed for brevity...
}
]
Which does not contain that critical number field which is defined in the DemanderFeatureToLoadCurveAssociation model.
I feel like I am just missing something quite obvious but I have not been able to find it.
I'm putting my specific implementation here in case it is useful to anyone. It is adapted from #JPG's own Q&A which was made as a response to this question (probably because it would be a pain to try to re-create my entire ORM and environment). I adapted their code to my own implementation.
It is worth noting however that this implementation only works one way. I originally tried implementing it with such a conceptual mapping:
DemanderFeature -> Person
LoadCurve -> Group
Membership -> DemanderFeatureToLoadCurveAssociation
But since I wanted to see all the associated LoadCurve objects when retrieving a DemanderFeature object, I actually needed to flip this mapping around so that DemanderFeature objects are equivalent to Group objects and LoadCurve objects are equivalent to Person objects. It is obvious to me now why this is (I am conceptualizing the DemanderFeature as a GROUP of LoadCurves), but at the time I didn't see the technical difference.
Anyway here is the updated serializers.py. Thanks again #JPG for taking the time!
class LoadCurveSerializer(serializers.ModelSerializer):
class Meta:
model = LoadCurve
fields = ["name"]
def serialize_demanderfeaturetoloadcurveassociation(self, loadcurve_instance):
# simple method to serialize the through model fields
demanderfeaturetoloadcurveassociation_instance = loadcurve_instance \
.demanderfeaturetoloadcurveassociation_set \
.filter(demanderfeature=self.context["demanderfeature_instance"]) \
.first()
if demanderfeaturetoloadcurveassociation_instance:
return DemanderFeatureToLoadCurveAssociationSerializer(demanderfeaturetoloadcurveassociation_instance).data
return {}
def to_representation(self, instance):
rep = super().to_representation(instance)
return {**rep, **self.serialize_demanderfeaturetoloadcurveassociation(instance)}
class DemanderFeatureToLoadCurveAssociationSerializer(serializers.ModelSerializer): # create new serializer to serialize the through model fields
class Meta:
model = DemanderFeatureToLoadCurveAssociation
fields = ["number"]
class DemanderFeatureSerializer(serializers.ModelSerializer):
demanderfeaturecollection = serializers.SlugRelatedField(slug_field="name", queryset=DemanderFeatureCollection.objects.all())
loadcurves = serializers.SerializerMethodField()
class Meta:
model = DemanderFeature
fields = ["name", "demanderfeaturecollection", "loadcurves", "geom"]
# fields = "__all__"
lookup_field = "name"
def get_loadcurves(self, demanderfeature):
return LoadCurveSerializer(
demanderfeature.loadcurve_set.all(),
many=True,
context={"demanderfeature_instance": demanderfeature} # should pass this `group` instance as context variable for filtering
).data
I'm a beginner in django rest framework.
I am wondring if there is a way to create a field from a GET. For example if "count" == 0, create a field named "available" : "out_of_stock" else "available
models.py
class Count(models.Model):
name = models.CharField(max_length=100)
count = models.IntergerField()
serializers.py
class CountSerializer(serializers.ModelSerializer):
class Meta:
model = Count
fields = '__all__'
views.py
class CountViewSet(viewsets.ModelViewSet):
queryset = Count.objects.all()
serializer_class = CountSerializer
output
[
{
"id": 1,
"count": 10,
},
{
"id": 2,
"count": 0,
}
]
First, for a good practise, avoid using fields = '__all__' and instead define your fields specifically.
For ex: fields = ['name', 'count'], you will see doing so will pay off shortly as I continue.
DRF has the feature to mark a serializer field as read_only or write_only so it works on specific requests, as it looks from the naming read_only intended for GET and write_only for POST.
You can do what you are looking for in so many ways, but I think the easiest way to do it would be using SerializerMethodField like this:
class CountSerializer(serializers.ModelSerializer):
available = serializers.SerializerMethodField()
def get_available((self, obj):
value = 'out_of_stock' if obj.count == 0 else 'available'
return value
class Meta:
model = Count
fields = ['name', 'count', 'available']
For more advanced needs, you can read about dynamic serializer fields on drf docs, see.
I am trying to make a nested serializer but when I run the following code it gives me an empty list. I tried to replicate the solution of this question and my problem is exactly similar
The only difference is in that answer serializer.Serializer is used but I am using Model Serializer
class hhhSerializer(serializers.Modelserializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(read_only=True)
class Meta:
model = ItemBatch
fields = ('id','name')
class dispatchhistorySerializer(serializers.ModelSerializer):
truck_name = ReadOnlyField(source='truck_name.name')
truck_type = ReadOnlyField(source='truck_type.name')
items = hhhSerializer(many=True)
class Meta:
model = DispatchPlan
fields = "__all__"
Output:
"id": 35,
"truck_name": "24 ft mxl 14 Ton",
"truck_type": "Container",
"items": [
{},
{},
{},
{},
{},
{},
{},
{},
{},
{}
],
You have to declare the field explicitly at DispatchHistorySerializer.Meta.fields; now, as personal recommendation avoid always "all" in the field list
This code should work (I had renamed your classes to comform python convention)
class HhhSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(read_only=True)
class Meta:
model = ItemBatch
fields = ('id','name')
class DispatchHistorySerializer(serializers.ModelSerializer):
truck_name = ReadOnlyField(source='truck_name.name')
truck_type = ReadOnlyField(source='truck_type.name')
items = HhhSerializer(many=True) # 2) here we say how to serialize 'items'
class Meta:
model = DispatchPlan
fields = ('id', 'truck_name', 'truck_type', 'items',) # 1) here we say: include 'items' please
EDIT: if using ModelSerializer, define which model in the Meta class; if it isn't a ModelSerializer, use a simple Serializer instead