How to add additional fields, like count of objects to ModelSerializer with many=True - django

I don't know how to add additional fields to my ModelSerializer, which accepts many=True. Here is all my code:
ViewSet:
def list(self, request, *args, **kwargs):
contracts = self.request.user.all_contracts
serializer = ContractGetSerializer(contracts, many=True, context={'request': request}, )
return Response({"results": serializer.data}, status=status.HTTP_200_OK)
ContractGetSerializer serializer. It also has inner Many=True fields
class ContractGetSerializer(serializers.ModelSerializer):
files = FileModelSerializer(many=True)
is_author = serializers.SerializerMethodField('is_author_method')
contract_signing_status = serializers.SerializerMethodField('get_recipient_signing_status')
def is_author_method(self, foo):
return foo.owner.id == self.context['request'].user.id
class Meta:
model = Contract
fields = ['id', 'is_author', 'title','contract_signing_status','files', ]
Now my ContractGetSerializer returns a ReturnList with all contracts. How can I add additional fields to that ReturnList and make it a dictionary?
I need for instance to return only 5 recent contracts (filtered by timestamp).
Also, I need to add total SIGNED contracts, and total PENDING contracts, counting all contracts data so that my frontend would not need to do this.
My current JSON:
{
"results": [
{
"id": 178,
"is_author": true,
"title": "ahhzhzh",
"message_to_all_recipients": null,
"contract_signing_status": "WAITING_FOR_ME",
"contract_signing_type": "SIMPLE",
"contract_signing_date": {
"start_date": "2010-09-04T14:15:22Z",
"end_date": "2010-09-04T14:15:22Z"
},
"recipients": [
{
"message": null,
"recipient_signing_status": "NOT_SIGNED",
"recipient_review_status": "NOT_REQUIRED",
"recipient_action": "SIGN",
"role": "ADMIN"
},
{
"message": null,
"recipient_signing_status": "NOT_SIGNED",
"recipient_review_status": "NOT_REQUIRED",
"recipient_action": "SIGN",
"role": "BASE"
}
]
},
{
"id": 179,
"is_author": true,
"title": "dhhdhd",
"message_to_all_recipients": null,
"contract_signing_status": "WAITING_FOR_ME",
"contract_signing_type": "SIMPLE",
"contract_signing_date": {
"start_date": "2010-09-04T14:15:22Z",
"end_date": "2010-09-04T14:15:22Z"
},
"recipients": [
{
"message": null,
"recipient_signing_status": "NOT_SIGNED",
"recipient_review_status": "NOT_REQUIRED",
"recipient_action": "SIGN",
"role": "ADMIN"
},
{
"message": null,
"recipient_signing_status": "NOT_SIGNED",
"recipient_review_status": "NOT_REQUIRED",
"recipient_action": "SIGN",
"role": "BASE"
}
]
},
]
}
My desired JSON:
{
"recents_count": 5,
"signed_count": 10,
"results": [
{
"id": 178,
"is_author": true,
"title": "ahhzhzh",
"message_to_all_recipients": null,
"contract_signing_status": "WAITING_FOR_ME",
"contract_signing_type": "SIMPLE",
"contract_signing_date": {
"start_date": "2010-09-04T14:15:22Z",
"end_date": "2010-09-04T14:15:22Z"
},
"recipients": [
{
"message": null,
"recipient_signing_status": "NOT_SIGNED",
"recipient_review_status": "NOT_REQUIRED",
"recipient_action": "SIGN",
"role": "ADMIN"
},
{
"message": null,
"recipient_signing_status": "NOT_SIGNED",
"recipient_review_status": "NOT_REQUIRED",
"recipient_action": "SIGN",
"role": "BASE"
}
]
},
{
"id": 179,
"is_author": true,
"title": "dhhdhd",
"message_to_all_recipients": null,
"contract_signing_status": "WAITING_FOR_ME",
"contract_signing_type": "SIMPLE",
"contract_signing_date": {
"start_date": "2010-09-04T14:15:22Z",
"end_date": "2010-09-04T14:15:22Z"
},
"recipients": [
{
"message": null,
"recipient_signing_status": "NOT_SIGNED",
"recipient_review_status": "NOT_REQUIRED",
"recipient_action": "SIGN",
"role": "ADMIN"
},
{
"message": null,
"recipient_signing_status": "NOT_SIGNED",
"recipient_review_status": "NOT_REQUIRED",
"recipient_action": "SIGN",
"role": "BASE"
}
]
},
]
}

There are multiple ways to do so, but I think doing this in serializers is better.
I will do something like this:
at view
def list(self, request, *args, **kwargs):
serializer = ContractResultSerializer(self.request.user, context={'request': request}, )
return Response(serializer.data, status=status.HTTP_200_OK)
at serializers
class ContractResultSerializer(serializers.Serializers):
recents_count = serializers.SerializerMethodField()
signed_count = serializers.SerializerMethodField()
results = serializers.SerializerMethodField()
def get_recents_count(self, obj):
# I don't know how is your model looks like to write the query to get the recent count
# but I think it will be something like this
return obj.contracts.filter(your recent condition).count()
def get_signed_count(self, obj):
# is the same as get_recents_count()
def get_results(self, obj):
return ContractGetSerializer(obj.all_contracts, many=True, context={'request': self.context['request']}).data
class ContractGetSerializer(serializers.ModelSerializer):
files = FileModelSerializer(many=True)
is_author = serializers.SerializerMethodField('is_author_method')
contract_signing_status = serializers.SerializerMethodField('get_recipient_signing_status')
def is_author_method(self, foo):
return foo.owner.id == self.context['request'].user.id
class Meta:
model = Contract
fields = ['id', 'is_author', 'title','contract_signing_status','files', ]
You can also do the same if you like at your view instead of writing a new serializer
for ex:
def list(self, request, *args, **kwargs):
contracts = self.request.user.all_contracts
serializer = ContractGetSerializer(contracts, many=True, context={'request': request}, )
response ={
"recents_count": self.request.user.contracts.filter(your recent condition).count(),
"signed_count": self.request.user.contracts.filter(your recent condition).count(),
"results": serializer.data
}
return Response(response, status=status.HTTP_200_OK)

Related

How can i group by all data according to model in DRF?

Currently, I am working on a DFR project where can successfully get all data but i need some modify with the json data.
Here Is the code
class PermissionSerializers(serializers.ModelSerializer):
class Meta:
model = Permission
fields = ['id', 'name', 'codename']
def to_representation(self, instance):
return {
'model': instance.content_type.name,
'data' :{
'id': instance.id,
'name': instance.name,
'codename': instance.codename,
}
}
And i get this JSON format,
{
"next": "http://127.0.0.1:8000/en/ga/api-version/common/admin/permissions/?page=4",
"previous": "http://127.0.0.1:8000/en/ga/api-version/common/admin/permissions/?page=2",
"total": 33,
"page": 3,
"page_size": 10,
"results": [
{
"model": "user",
"data": {
"id": 25,
"name": "Can add user",
"codename": "add_user"
}
},
{
"model": "user",
"data": {
"id": 29,
"name": "Admistrative Access",
"codename": "admin_access"
}
},
But I want to modify with something like this which has model name on top and then all available data inside a dictionary:
{
"model": "user",
"data": {
"id": 26,
"name": "Can change user",
"codename": "change_user"
},
{
"id": 25,
"name": "Can add user",
"codename": "add_user"
},
},
You get something like this because you have pagination in your API, if you don't want it just disable pagination.
I came up with this solution:
def list(self, request):
_models_list = [
'organization','user','group', 'logentry', 'organizationtype',
'keyword', 'productsupport','feedbacksupport','twittercredential']
models = ContentType.objects.filter(model__in = _models_list)
model_dict = {
'model': '',
'data':''
}
results = []
for model in models:
_permissions = []
access = ' Access'
if model.model == 'group':
model_dict['model'] = 'Role'+ access
elif model.model == 'organizationtype':
model_dict['model'] = 'Organization Type'+ access
elif model.model == 'productsupport':
model_dict['model'] = 'Product'+ access
elif model.model == 'feedbacksupport':
model_dict['model'] = 'Feedback'+ access
else:
model_dict['model'] = model.model.capitalize()+ access
permissions = Permission.objects.filter(content_type = model)
for premission in permissions:
_permissions.append(premission)
serializer = PermissionSerializers(_permissions, many=True)
data = serializer.data
model_dict['data'] = data
results.append(model_dict.copy())
return Response(results)

Django-Tastypie self children

Trying to make api for multiple subtasks.
I have the task model, that can have another task as a parent:
class Task(models.Model):
parent_task = models.ForeignKey("Task", null=True, blank=True)
name = models.CharField(max_length=64)
def __unicode__ (self):
return self.name
Now I'm trying to make tastypie resource:
class TaskResource(ModelResource):
parent_task = fields.ForeignKey(TaskResource, 'parent_task', full=False) <-- ERROR HERE
class Meta:
queryset = Task.objects.all()
resource_name = 'task'
list_allowed_methods = ['get', 'put', 'post', 'delete']
include_resource_uri = False
def dehydrate(self, bundle, for_list=False):
bundle.data["subtasks"] = "how?" <-- HOW??
return bundle
Thanks for your time.
P.S. I need something like this:
[
{
"id": 1,
"name": "Task 1",
"subtasks": [
{
"id": 1,
"name": "Task 1",
"subtasks": [...]
}
]
},
{
"id": 2,
"name": "Task 2",
"subtasks": "how?"
}
]
Almost a copy of Including child resources in a Django Tastypie API but not exactly.
So your first problem is that you specify relation to self wrong. It should be just self:
parent_task = fields.ForeignKey('self', 'parent_task', null=True, full=False)
Secondly, notice null=True - parent could be null.
Lastly, you just need to add another relation field and ask for the full details
subtasks = fields.ToManyField('self', 'task_set', full=True)
task_set is a related_name for the Task.parent_task field.
The resulting code is:
class TaskResource(ModelResource):
parent_task = fields.ForeignKey('self', 'parent_task', null=True, full=False)
subtasks = fields.ToManyField('self', 'subtasks', full=True)
class Meta:
queryset = Task.objects.all()
resource_name = 'task'
list_allowed_methods = ['get', 'put', 'post', 'delete']
include_resource_uri = False
And the result:
{
"meta": {
"previous": null,
"total_count": 3,
"offset": 0,
"limit": 20,
"next": null
},
"objects": [
{
"parent_task": null,
"subtasks": [
{
"parent_task": "/api/v1/task/1/",
"subtasks": [],
"id": 2,
"name": "Root's Child 1"
},
{
"parent_task": "/api/v1/task/1/",
"subtasks": [],
"id": 3,
"name": "Root's Child 2"
}
],
"id": 1,
"name": "Root Task"
},
{
"parent_task": "/api/v1/task/1/",
"subtasks": [],
"id": 2,
"name": "Root's Child 1"
},
{
"parent_task": "/api/v1/task/1/",
"subtasks": [],
"id": 3,
"name": "Root's Child 2"
}
]
}

Filtering rest framework serializer output

I have a rest serializer and i am getting this output(actual data in thousands). I am trying to filter this data such that it displays only the json where a condition is met. (in my case, student.student_id = 29722):
[
{
"student": {
"student_id": 29722,
"first_name": "Michaella",
"middle_name": "Helene",
"last_name": "Bonose",
"email": "",
"phone": null,
"cell_phone": null
},
"credits_completed": null,
"academic_program_gpa": null,
"primary_program": true,
"academic_program": {
"id": 595,
"acad_program_category": {
"id": 1,
"program_type": {
"id": 1,
"program_category_type": "Academic"
},
"title": "Associate in Arts"
},
"acad_program_type": {
"id": 2,
"program_type": "Associate Degree"
},
"acad_program_code": "AA.ARTS",
"program_title": "Associate in Arts Degree",
"required_credits": 60,
"min_gpa": 2.0,
"description": ""
}
},
{
"student": {
"student_id": 29722,
"first_name": "Michaella",
"middle_name": "Helene",
"last_name": "Bonose",
"email": "",
"phone": null,
"cell_phone": null
},
"credits_completed": null,
"academic_program_gpa": null,
"primary_program": true,
"academic_program": {
"id": 596,
"acad_program_category": {
"id": 2,
"program_type": {
"id": 1,
"program_category_type": "Academic"
},
"title": "Associate in Sciences"
},
"acad_program_type": {
"id": 2,
"program_type": "Associate Degree"
},
"acad_program_code": "AS.SCIENCE",
"program_title": "Associate in Sciences Degree",
"required_credits": 60,
"min_gpa": 2.0,
"description": ""
}
}, .......
Here is my APIView for this:
class StudentAcademicProgramList(APIView):
def get(self, request, format=None):
student_academic_program = Student_academic_program.objects.all()
serialized_Student_academic_program = StudentAcademicProgramSerializer(student_academic_program, many=True)
return Response(serialized_Student_academic_program.data)
class StudentAcademicProgramDetail(APIView):
def get_objects(self, pk):
try:
return Student_academic_program.object.get(pk=pk)
except Student_academic_program.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
student_academic_program = self.get_object(pk)
serialized_Student_academic_program = StudentAcademicProgramSerializer(student_academic_program)
return Response(serialized_Student_academic_program.data)
Hoe do i filter this such that it only displays the values where student.student_id = 29722 ?
Do you mean to filter StudentAcademicProgramList?
class StudentAcademicProgramList(APIView):
def get(self, request, format=None):
# This should be populated however you need it to be.
student_id = 29722
student_academic_program = Student_academic_program.objects.filter(student_id=student_id)
serialized_Student_academic_program = StudentAcademicProgramSerializer(student_academic_program, many=True)
return Response(serialized_Student_academic_program.data)

Django REST Meta data for M2M missing

On my json output I don't seem to get key value pairs on my m2m field attribute_answers. see the code below. How to I add in the attribute_answers fields?
json
{
"id": 20,
"name": "Jake",
"active": true,
"type": {
"id": 1,
"name": "Human",
"active": true,
"created": "2013-02-12T13:31:06Z",
"modified": null
},
"user": "jason",
"attribute_answers": [
1,
2
]
}
Serializer
class ProfileSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(slug_field='username')
attribute_answers = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Profile
depth = 2
fields = ('id', 'name', 'active', 'type', 'user', 'attribute_answers')
def restore_object(self, attrs, instance=None):
"""
Create or update a new snippet instance.
"""
if instance:
# Update existing instance
instance.name = attrs.get('name', instance.name)
instance.active = attrs.get('active', instance.active)
instance.type = attrs.get('type', instance.type)
instance.attribute_answers = attrs.get('attribute_answers', instance.attribute_answers)
return instance
# Create new instance
return Profile(**attrs)
If I understand your question correctly, you want a Nested Relationship. In your ProfileSerializer, you'll want:
attribute_answers = AttributeAnswerSerializer(many=True)
This will display all the attributes of each associated attribute_answer model and give you something like:
{
"id": 20,
"name": "Jake",
"active": true,
"type": {
"id": 1,
"name": "Human",
"active": true,
"created": "2013-02-12T13:31:06Z",
"modified": null
},
"user": "jason",
"attribute_answers": [
{"id": 1, "foo": "bar"},
{"id": 2, "foo": "bar2"}
]
}

Django Tastypie - How to get related Resources with a single request

Suppose following Resources are given:
class RecipeResource(ModelResource):
ingredients = fields.ToManyField(IngredientResource, 'ingredients')
class Meta:
queryset = Recipe.objects.all()
resource_name = "recipe"
fields = ['id', 'title', 'description',]
class IngredientResource(ModelResource):
recipe = fields.ToOneField(RecipeResource, 'recipe')
class Meta:
queryset = Ingredient.objects.all()
resource_name = "ingredient"
fields = ['id', 'ingredient',]
A HTTP Request to myhost.com/api/v1/recipe/?format=json gives following response:
{
"meta":
{
...
},
"objects":
[
{
"description": "Some Description",
"id": "1",
"ingredients":
[
"/api/v1/ingredient/1/"
],
"resource_uri": "/api/v1/recipe/11/",
"title": "MyRecipe",
}
]
}
So far so good.
But now, I would like to exchange the ingredients resource_uri ("/api/v1/ingredient/1/") with something like that:
{
"id": "1",
"ingredient": "Garlic",
"recipe": "/api/v1/recipe/1/",
"resource_uri": "/api/v1/ingredient/1/",
}
To get following response:
{
"meta":
{
...
},
"objects":
[
{
"description": "Some Description",
"id": "1",
"ingredients":
[
{
"id": "1",
"ingredient": "Garlic",
"recipe": "/api/v1/recipe/1/",
"resource_uri": "/api/v1/ingredient/1/",
}
],
"resource_uri": "/api/v1/recipe/11/",
"title": "MyRecipe",
}
]
}
The answer is the attribute full=True:
ingredients = fields.ToManyField('mezzanine_recipes.api.IngredientResource', 'ingredients', full=True)