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.
Related
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 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.
So I am creating follower system but there is a problem. Everything is working properly. but in follower serializer I want the username's and other details of users.
models.py
class Follow(models.Model):
user = models.OneToOneField(User,related_name="user" ,on_delete=models.CASCADE)
''' to obtain user ' eeee = User.objects.first() , eeee.user' '''
following = models.ManyToManyField(User,related_name='following_user',blank=True)
''' to obtain followers ' eeee.following_user.all()' '''
''' to obtain following ' eeee.user.following.all()' '''
def __str__(self):
return self.user.username
In field following , user.following.all() is used to get the user in manytomany field of request.user and
following_user.all() is used get all the users who has added request.user in their following field.
serializers.py
class FollowerSerializer(ModelSerializer):
user = UserSerializer(many=False)
follower = SerializerMethodField()
class Meta:
model = Follow
fields = ('user','follower')
def get_follower(self, obj):
context = self.context
request = context.get("request")
return request.user.following_user.all().values()
Here I am serializing all the user who has added request.user in their following field
views.py
class FollowerView(RetrieveAPIView):
queryset = Follow.objects.all()
serializer_class = FollowerSerializer
permission_classes = [IsAuthenticated]
lookup_field = 'id'
api
{
"user": {
"name": "eeee"
},
"is_follower": [
{
"id": 2,
"user_id": 9
},
{
"id": 5,
"user_id": 16
},
{
"id": 3,
"user_id": 10
}
]
}
These is the api I am getting of all the user who has added request.user in their following list.
the problem here is I am getting the pk of the user from the key user_id. But I want there username and other information like email, full name of the user who has that primary key. So how can I achieve that?
Modify the get_follower method in serializers.py as follows:
def get_follower(self, obj):
context = self.context
request = context.get("request")
qs = request.user.following_user.all()
data = [{'id': obj.pk, 'user_id': obj.user_id, 'name': obj.req_field} for obj in qs]
return data
I'm currently trying to make a post request with only data field in models.py:
class Filter(models.Model):
class Meta:
verbose_name_plural = "Filter"
verbose_name = "Filter"
db_table= "listing_filter"
data = JSONField(default={})
user = models.ForeignKey('backend.user', on_delete=models.CASCADE)
I have the following Serializer , i use JSONField following the drf document docs:
class FilterSerializer(serializers.ModelSerializer):
data = serializers.JSONField(required=True)
user = serializers.CharField(required=False)
class Meta:
model = Filter
fields = ('__all__')
and use it in the APIView:
class FilterView(APIView):
def post(self, request):
login_user = request.user
received_json_data=json.loads(request.body)
valid_ser = FilterSerializer(data=received_json_data)
if valid_ser.is_valid():
post_data = received_json_data["data"]
filter = Filter.objects.create(data=post_data, user=login_user)
filter.save()
return JsonResponse({'code':'200','data': filter.id}, status=200)
else:
return JsonResponse({'code':'400','errors':valid_ser.errors}, status=400)
When i send the following data in body it worked and saved the object:
{
"data": {
"http://example.org/about": {
"http://purl.org/dc/terms/title": [
{
"type": "literal",
"value": "Anna's Homepage"
}
]
}
}
}
But when i send data as a string(which is not a json object) it still save, how do i prevent this ?
{
"data" : "abc"
}
Using django-rest framework 3.3.3
I am trying to update existing data in my database using the django-rest framework, following a previously posted solution :django-rest-framework 3.0 create or update in nested serializer
I'm fairly new to django in general, and the rest framework.
Given the following models:
class Clinic(models.Model):
name = models.TextField()
streetNumber = models.IntegerField()
unitNumber = models.CharField(max_length=10, blank=True, null=True)
streetName = models.CharField(max_length=50)
city = models.CharField(max_length=50)
province = models.CharField(max_length=3)
country = models.CharField(max_length=30)
postalCode = models.CharField(max_length=6)
phone = models.CharField(max_length=10)
def __str__(self):
return self.name
class Time (models.Model):
clinic = models.ForeignKey(Clinic, related_name='times')
appointmentTime = models.CharField(max_length=100)
def __str__(self):
return self.appointmentTime
And the following serializers:
class TimeSerializer(serializers.ModelSerializer):
class Meta:
model = Time
fields = ('appointmentTime',)
class ClinicSerializer(serializers.ModelSerializer):
times = TimeSerializer(many=True)
class Meta:
model = Clinic
fields = '__all__'
def update(self, instance, validated_data):
instance.name = validated_data['name']
instance.streetName = validated_data['streetName']
instance.unitNumber = validated_data['unitNumber']
instance.city = validated_data['city']
instance.province = validated_data['province']
instance.country = validated_data['country']
instance.postalCode = validated_data['postalCode']
instance.phone = validated_data['phone']
#print(instance)
instance.save()
times_data = [item['id'] for item in validated_data['times']]
print(times_data)
for time in instance.clinics:
if time.id not in times_data:
time.delete()
for item in validated_data['times']:
time = Time(id=item['id'], clinic=instance,
appointmentTime=item['appointmentTime'])
time.save()
return instance
And the following view:
class ClinicList(APIView):
def get(self,request):
clinics = Clinic.objects.all()
serializer = ClinicSerializer(clinics, many=True)
return Response({'results': serializer.data})
def post(self, request):
serializer = ClinicSerializer(data=request.data)
if serializer.is_valid():
serializer.update(Clinic, serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
And the following existing data:
{
"id": 2,
"times": [
{
"appointmentTime": "time3"
},
{
"appointmentTime": "time3"
}
],
"name": "Example Clinic",
"streetNumber": 123,
"unitNumber": "",
"streetName": "Example Street",
"city": "Exampletown",
"province": "Ont",
"country": "Canada",
"postalCode": "A1A1A1",
"phone": "9059059059"
}
With my JSON POST data being:
{
"id": 2,
"times": [
{
"appointmentTime": "AAAA"
},
{
"appointmentTime": "time3"
}
],
"name": "Example Clinic",
"streetNumber": 123,
"unitNumber": "",
"streetName": "Example Street",
"city": "Exampletown",
"province": "Ont",
"country": "Canada",
"postalCode": "A1A1A1",
"phone": "9059059059"
}
My problem is that I am receiving an error stating:
File"C:\Users\colin\Documents\GitHub\Walk_inExpressAPI\clinics\serializer.py",
line 31, in update
instance.save()
TypeError: save() missing 1 required positional argument: 'self'
I'm not sure what Im doing wrong here, or what value to supply to instance.save() to correct this error. I've checked that instance was an object of type Clinic.
What i'm trying to accomplish is that each Clinic can have multiple appointment times. They wont share appointmentTime objects, because I would like to be able to delete an appointentTime each time a new appointment is booked, and each clinic will require its own set of available appointmentTimes.
Hopefully you guys can help lead me in the right direction! Thanks!
You should user the method PUT in your APIView:
def put(self, request, pk):
clinics = Clinic.objects.get(pk=pk)
serializer = ClinicSerializer(clinics, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
so in you url.py should have a pk, to identify the object you want to modify:
urlpatterns = [
url(r'^snippets/$', views.ClinicList.as_view()),
url(r'^snippets/(?P<pk>[0-9]+)/$', views.ClinicList.as_view()),
]
In update method change the instance attributes like:
instance.name = validated_data.get('name',instance.name)
Try to see the example in DRF page as well.