I am trying to fetch related data from a parent table using an API. I am trying to get the details from the operator table which has a one-to-one field with the user table.
After going through various answers I understood how to join tables but due to some reason I am unable to fetch the user data
serializer.py
class OpDetailsSerializer(DynamicFieldsModelSerializer):
user = UserSerializer(source="operator_details",many=False, read_only=True)
print(user.data)
class Meta:
model = OperatorDetails
fields = ('gst_no','pan','user')
models.py
class OperatorDetails(models.Model):
operator=models.OneToOneField(settings.AUTH_USER_MODEL,related_name="operator_details",on_delete=models.CASCADE,primary_key=True)
pan = models.CharField(max_length=10, unique=True, null=True,blank=True)
gst_no = models.CharField(max_length=15, unique=True,null=True,blank=True)
def __str__(self):
return str(self.operator)
views.py
def view_operator_info(request):
fields = ('operator','pan','gst_no','user')
operator = OperatorDetails.objects.get(operator__id=request.user.id)
serializer = OpDetailsSerializer(operator,fields=fields)
content = {
"status": True,
"response": status.HTTP_200_OK,
"message": "Operator details",
"data":serializer.data
}
return Response(content)
Actual Output
{
"status": true,
"response": 200,
"message": "Operator details",
"data": {
"gst_no": "test",
"pan": "test"
}
}
expected Output
{
"status": true,
"response": 200,
"message": "Operator details",
"data": {
"gst_no": "test",
"pan": "test",
"user":{
"email":.....
//data from user table
}
}
}
can anyone help. Thanks in advance
Your field name has to match the field on our model the other option is to use the source parameter.
class OpDetailsSerializer(DynamicFieldsModelSerializer):
user = UserSerializer(source="operator", many=False, read_only=True)
print(user.data)
class Meta:
model = OperatorDetails
fields = ('gst_no','pan','operator')
class OperatorDetails(models.Model):
operator = models.OneToOneField(settings.AUTH_USER_MODEL,related_name="operator_details",on_delete=models.CASCADE,primary_key=True)
pan = models.CharField(max_length=10, unique=True, null=True,blank=True)
gst_no = models.CharField(max_length=15, unique=True,null=True,blank=True)
def __str__(self):
return str(self.operator)
def user(Self):
return {
'email':self.operator.email,
'firstname':self.first_name,
'lastname':self.last_name
}
Add the method user in your model OperatorDetails and try it.
These thing helps me lot for making custom objects in my projects and share your review after you try these so i can can also take note.
Related
I am trying to sort my results by either date or primary key, newer entries in the database first.
models.py
class Notifications(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateTimeField('date transacted')
read = models.BooleanField(default=False)
message = models.CharField(max_length=300)
views.py
class getnotifications(viewsets.ModelViewSet):
# Database model
queryset = User.objects.all()
# Serializer - this performs the actions on the queried database entry
serializer_class = NotificationsSerializer
# What field in database model will be used to search
lookup_field = 'username'
serializers.py
class NotificationsSerializer(DynamicModelSerializer):
notifications_set = DynamicRelationField('ListNotificationsSerializer', many=True, embed=True)
class Meta:
model = User
fields = ['notifications_set']
class ListNotificationsSerializer(DynamicModelSerializer):
class Meta:
model=Notifications
name='notifications_set'
fields=['pk','date','read','message']
URL accessing request
http://localhost:8000/pm/getnotifications/<omitted>/?sort[]=-notifications_set.pk
JSON results
{
"notifications_set": [
{
"pk": 1,
"date": "2022-10-10T17:11:33.821757Z",
"read": false,
"message": "A user with the phone <omitted> has submitted a restock request for 5 of airpods"
},
{
"pk": 2,
"date": "2022-10-10T00:00:00Z",
"read": false,
"message": "A user with the phone <omitted> has submitted a restock request for 5 of airpods"
},
{
"pk": 3,
"date": "2022-10-10T17:25:11.824385Z",
"read": false,
"message": "A user with the phone <omitted> has submitted a restock request for 5 of airpods"
}
],
"links": {
"notifications_set": "notifications_set/"
}
}
Whether I use -notification_set.pk or notification_set.pk it displays the results in Postman the same way, with no changed order
Using this plugin: https://github.com/AltSchool/dynamic-rest
for sorting
This plugin was linked in the rest framework docs
Still very new to Django. I'm trying to send a post request to my backend from insomnia right now and I'm getting the error Getting null value when I'm passing in the ID value.
model:
class Run(models.Model):
gameId = models.ForeignKey(Game,on_delete=models.CASCADE, related_name='runs')
userId = models.ForeignKey(User,on_delete=models.CASCADE, related_name='game')
name = models.CharField(max_length=30)
isComplete = models.BooleanField(default=False)
deaths = models.IntegerField()
badges = models.IntegerField()
def __str__(self):
return self.name
Here is my seralizer:
class RunSerialzer(serializers.HyperlinkedModelSerializer):
gameId = GameSerialzer(
read_only = True
)
userId = UserSerialzer(
read_only = True
)
class Meta:
model = Run
fields=('id','name','isComplete','deaths','badges','userId','gameId')
My view:
class RunList(generics.ListCreateAPIView):
queryset = Run.objects.all()
serializer_class = RunSerialzer
And my request I'm making:
{
"gameId":1,
"userId": 1,
"name":"testrun2",
"deaths":0,
"badges": 0,
"isComplete": false
}
and the second one I tried
{
"gameId": {
"id": 1,
"name": "Vintage White",
"photo": "https://archives.bulbagarden.net/media/upload/thumb/0/08/White_EN_boxart.png/250px-White_EN_boxart.png"
},
"userId": {
"id": 1,
"name": "TestUser"
},
"name":"testrun2",
"isComplete": false,
"deaths": 0,
"badges": 0
}
I feel like I need to create either a new Serailizer to get the instance I want in my Game model with the pk im passing in the request
since you are using read_only=True on your serializer for two of your fields you need to pass gameId and userId manually to save method of serializer.
in your view(RunList) you should overwrite perform_create method like following:
def perform_create(self, serializer):
gameId = self.request.data.get('gameId')
userId = self.request.data.get('userId')
return serializer.save(gameId=gameId, userId=userId)
I am trying to create a simple model with foreign keys using Django rest framework.
This are the models:
class Route(models.Model):
place_origin = models.ForeignKey(
Place, null=False, on_delete=models.DO_NOTHING)
class Place(models.Model):
name = models.CharField(max_length=50, null=False)
This are the serializers for each model:
class PlaceSerializer(serializers.ModelSerializer):
class Meta:
model = Place
fields = ["id", "name"]
class RouteSerializer(serializers.ModelSerializer):
place_origin = PlaceSerializer(many=False, read_only=True)
class Meta:
model = Route
fields = ["id", "place_origin"]
This RouteSerializer has the place_origin property in order to show the place details(all the fields from it) when I am looking at the route detail. What I mean is for routes I want to display:
[
{
"id": 1,
"place_origin": {
"id": 1,
"name": "New york"
}
},
{
"id": 2,
"place_origin": {
"id": 2,
"name": "Boston"
}
}
]
And not just:
[
{
"id": 1,
"place_origin": 1
},
{
"id": 2,
"place_origin": 2
}
]
This is the view:
#api_view(['POST'])
def routes_new_route_view(request):
"""
Create a new route
"""
if request.method == "POST":
data = JSONParser().parse(request)
place_origin = Place.objects.get(id=data["place_origin"])
data["place_origin"] = PlaceSerializer(place_origin)
data["place_origin"] = data["place_origin"].data
serializer = RouteSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
else:
return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I want to send the request from postman this way:
{
"place_origin": 3
}
But I am getting the error from the title.
Thanks for all the help!
The error is that you're trying to send data via a PlaceSerializer but this field is read_only. On the other hand, your DB expects place_origin since you precised null=False in your model. Both combined gives the error "Not NULL constraint failed".
The easiest way is to slightly modify your serializer in order to have one field for write and another for read.
class RouteSerializer(serializers.ModelSerializer):
place_origin = PlaceSerializer(many=False, read_only=True)
place = serializers.PrimaryKeyRelatedField(source="place_origin",queryset=Place.objects.all(),write_only=True)
class Meta:
model = Route
fields = ["id", "place_origin", "place"]
Here, you will use place field as a way to create the relationship with your Route instance.
I have 2 model classes:
class CustomUser(AbstractUser):
username = models.CharField(max_length=30, unique=True)
tags = models.ManyToManyField('events.Tag', related_name='user_tag', blank=True)
class Tag(models.Model):
name = models.CharField(unique=True, max_length=50)
And serializers:
class UserSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True)
class Meta:
...
class TagSerializer(serializers.ModelSerializer):
class Meta:
lookup_field = 'name'
model = Tag
fields = ('id', 'name')
When I do a get query I get something like this:
"data": {
"type": "CustomUser",
"id": "6",
"attributes": {
"username": "mercer",
},
"relationships": {
"tags": {
"data": [
{
"type": "Tag",
"id": "1"
}
]
},
}
}
What I want is to get Tag 'name' field in user representation:
"type": "Tag",
"id": "1",
"name":"name"
And I want to make patch query for adding tag to user.
I can use SerializerMethodField(), but this way I will not able to add tags
The problem was with settings of rest framework. I wrote there custom json render classes and recived this form of output. Now i removed it and everything is fine.
Hello Everyone!
I have latest django-rest-framework and I'm trying to make serializer show possible choices for each field on OPTIONS request.
Here's part of my model
# models.py
class Task(models.Model):
parent = models.ForeignKey('self',
blank=True, null=True, related_name='child_tasks')
title = models.CharField(max_length=128)
status = models.CharField(max_length=16, choices=STATUS_CHOISES, default='new')
priority = models.CharField(max_length=16, choices=PRIORITY_CHOISES, default='1')
chief = models.ForeignKey('users.SystemUser', related_name='tasks',
blank=True, null=True)
And here's serializer
# serializers.py
class ParentRelatedField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
obj = self.context['view'].get_object()
return Task.objects.exclude(pk=obj.pk)
def get_user_choices():
return tuple([(i.id, i.system_name) for i in SystemUser.objects.all()])
class TaskDetailSerializer(serializers.Serializer):
title = serializers.CharField()
parent = ParentRelatedField(
required=False, allow_null=True
)
status = serializers.ChoiceField(choices=STATUS_CHOISES)
priority = serializers.ChoiceField(choices=PRIORITY_CHOISES)
chief = serializers.ChoiceField(choices=get_user_choices(), required=False)
I achieved that for chief field using get_user_choices function, so i get:
"chief": {
"type": "choice",
"required": false,
"read_only": false,
"label": "Chief",
"choices": [
{
"value": 1,
"display_name": "First User Name"
}
]
}
ParentRelatedField works great for validation, but not for metadata:
"parent": {
"type": "field",
"required": false,
"read_only": false,
"label": "Parent"
}
I can't use ChoiceField with function (like in chief) for that because parent choice must exclude current Task object.
Solved the problem.
Solution was found at drf 3.4 announcement and drf issue comments.
I changed my field to make it more universal
class SelfExcludingRelatedField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
obj = self.context['view'].get_object()
return self.queryset.exclude(pk=obj.pk)
Then wrote custom metadata class (copied from github).
class CustomMetadata(SimpleMetadata):
def get_field_info(self, field):
field_info = super().get_field_info(field)
if (not field_info.get('read_only') and
isinstance(field, SelfExcludingRelatedField) and
hasattr(field, 'choices')):
field_info['choices'] = [
{
'value': choice_value,
'display_name': choice_name
}
for choice_value, choice_name in field.choices.items()
]
return field_info
And added it to settings:
REST_FRAMEWORK = {
'DEFAULT_METADATA_CLASS': 'api_v0.metadata.CustomMetadata',
}