Update a Foreign Key using PUT in Django Rest Framework - django

I am basically a new entrant into Rest framework and relatively new to Django. I am working on an Employee Rest API and I created Employee - Department tables using below Django models. I assigned a foreign key relationship between Employee's-department ID and Department-department ID. Now I want to update the Employee table using PUT operation. But when i update Employee.dept_id, it is not updated with the new value. I understand that since its a read only field, am not able to update it. How to change it to write field? so that i can update the department id in the employee table.
models.py
class Department(models.Model):
dept_id = models.IntegerField(primary_key=True)
dept_name = models.CharField(max_length=30)
def __str__(self):
return self.dept_id
class Meta:
ordering = ('dept_id',)
class Employee(models.Model):
first_name = models.CharField(max_length=30,blank=True)
last_name = models.CharField(max_length=30,null=True,blank=True)
emp_id = models.AutoField(primary_key=True)
hire_date = models.DateField(default=datetime.date.today)
email_id = models.EmailField(blank=True)
dept = models.ForeignKey(Department, null=True,blank=True,related_name="dept")
def __str__(self):
return '%s %s' % (self.first_name, self.last_name)
class Meta:
ordering = ('emp_id',)
My serializers for the above models are
serializers.py
class DepartmentSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Department
fields = ('dept_id','dept_name')
class EmployeeSerializer(serializers.ModelSerializer):
dept_id = DepartmentSerializer().get_fields()
class Meta:
model = Employee
fields = ('emp_id','last_name','first_name','hire_date','email_id', 'dept_id')
views.py
#api_view(['GET','POST'])
def employee_list(request, format=None):
"""
List all employees, or create a new employee
"""
if request.method == 'GET':
employees = Employee.objects.all()
serializer = EmployeeSerializer(employees,many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = EmployeeSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#api_view(['GET','PUT','DELETE'])
def employee_detail(request, pk, format=None):
"""
Retrieve, update or delete a employee.
"""
try:
employee = Employee.objects.get(pk=pk)
except Employee.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method=='GET':
serializer = EmployeeSerializer(employee)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = EmployeeSerializer(employee,data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method=='DELETE':
employee.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

You need to overwrite the create() and update() views in your Employee Serializer. Please refer this link for complete doc.

Related

user foreign key in django rest framework

I have one user model and Accesskey model. I want to save user who is accessing the API(access_key) in the accesskey table.
models.py
class AccessKeys(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='user',null=True)
access_keys =models.CharField(max_length=400)
def __str__(self):
return self.access_keys
serializers.py
class AccessKeySerializer(serializers.ModelSerializer):
user_id = serializers.RelatedField(source='CustomUser', read_only=True)
access_keys = serializers.CharField(max_length=200,required=True)
class Meta:
model =AccessKeys
fields = '__all__'
views.py
class AccessKeyView(generics.ListCreateAPIView):
permission_classes = [IsAuthenticated, ]
queryset = AccessKeys.objects.all()
serializer_class = AccessKeySerializer
def post(self,request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
when I'm saving accesskey in the model there is null value in user_id field.

how to add data to child model along with parent mode?

I am working with Django REST framework. I want to add data to child model along with the parent model. Basically creating two records at a time.
models.py
class Visit(models.Model):
name = models.CharField(max_length=200)
gender = models.CharField(choices=GENDER_CHOICE, max_length=1)
mobile = models.CharField(max_length=18,default="")
email = models.CharField(max_length=256, null=True, blank=True)
address = models.TextField(null=True, blank=True)
visit_type = models.IntegerField(choices=VISIT_TYPE)
visit_purpose = models.CharField(max_length=250)
visitor_photo = models.FileField(upload_to="visitor/",null=True, blank=True)
id_photo = models.FileField(upload_to="id_card/",null=True, blank=True)
date_created = models.DateTimeField(default=timezone.now, editable=False)
class Status(models.Model):
visit = models.ForeignKey(Visit,on_delete=models.CASCADE)
description = models.CharField(max_length=200)
from_time = models.DateField()
to_time = models.DateTimeField(null=True, blank=True)
aproved = models.BooleanField(default=False)
visit_complete = models.BooleanField(default=False)
exit_time = models.DateTimeField(null=True, blank=True)
date_created = models.DateTimeField(default=timezone.now, editable=False)
serializers.py
class StatusSerializers(serializers.ModelSerializer):
class Meta:
model = Status
fields = "__all__"
class VisitSerializers(serializers.ModelSerializer):
visit = StatusSerializers(many=True)
class Meta:
model = Visit
fields = "__all__"
def create(self, validated_data):
print(validated_data)
model_b = Visit.objects.create(**validated_data)
# print(self.context.request.data.get('description'))
child_model_data = {
'description': self.context.request.data.get('description'),
}
child_model_serializer = StatusSerializers(data=child_model_data)
child_model_serializer.is_valid(raise_exception=True)
child_model_serializer.save(status=model_b)
return model_b
views.py
#api_view(['GET', 'POST'])
def create_visitor(request):
if request.method == 'GET':
visitor = Visit.objects.all()
serializer = VisitSerializers(visitor,context={'request': request},many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = VisitSerializers(data=request.data,context={'request': request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
In the above code I am able to create child record i.e. Status model record but I don't know how to add other fields to it during its creation.
In your serializer child model data can be found in self.context.request.data and you can simply use child serializer to validate and save in db.
class VisitSerializers(serializers.ModelSerializer):
class Meta:
model = Visit
fields = "__all__"
def create(self, validated_data):
print(validated_data)
model_b = Visit.objects.create(**validated_data)
child_model_data = {
'field': self.context['request'].data.get('your_field');
.....
}
child_model_serializer = ChildSerializer(data=child_model_data)
child_model_serializer.is_valid(raise_exception=True)
child_model_serializer.save()
return model_b
Update you need to change in multiple places
First in your view, in post method serializer initialization you need to pass context object.
#api_view(['GET', 'POST'])
def create_visitor(request):
if request.method == 'GET':
visitor = Visit.objects.all()
serializer = VisitSerializers(visitor,context={'request': request},many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = VisitSerializers(data=request.data, context={'request': request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
And then in your VisitSerializer
class VisitSerializers(serializers.ModelSerializer):
visit = StatusSerializers(many=True) # not sure why you did this, this should be removed i guess
class Meta:
model = Visit
fields = "__all__"
def create(self, validated_data):
print(validated_data)
model_b = Visit.objects.create(**validated_data)
# print(self.context.request.data.get('description'))
child_model_data = {
'description': self.context.request.data.get('description'),
'status': model_b
# and make sure all other necessary fields.
}
child_model_serializer = StatusSerializers(data=child_model_data)
child_model_serializer.is_valid(raise_exception=True)
child_model_serializer.save()
return model_b
You can have extra fields in your serializer along with default fields. For reverse relations, you can add a field like this
class VisitSerializers(serializers.ModelSerializer):
status = StatusSerializer(many=True)
class Meta:
model = Visit
You can pass data to VisitSerializer like this. {, status: [, ]}

Django Rest Framework: How to associate the object with the user when posting the object

I'm new to creating REST API so I might misunderstand something.
I'm creating REST API using Django Rest Framework. And I'm trying to create an object and send it from my mobile app.
However, API returns 400. I think it still cannot associate the object with the request user and I'm wondering how to do it.
models.py
class Item(models.Model):
item_name = models.CharField()
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
serializers.py
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name', 'created_by')
and views.py
class ListItems(generics.ListCreateAPIView):
queryset = Item.objects.all()
serializer_class = ItemSerializer
What I want to know is how to associate the object with the request user when posting the object like as we do like
if form.is_valid():
item = form.save(commit=False)
item.created_by = request.user
item.save()
I think the easiest approach is like this:
class ItemSerializer(serializers.ModelSerializer):
created_by = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
Reference can be found here
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name',)
class ListItems(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
you can do this way
One of the possible way to overwrite serializer_create method. As user is not associated with request.data first we need to make sure, this is write_only field and also need to assign current user from modelSerializer's self.context.request.user. Following addition should solve the problem.
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('item_name', 'created_by')
extra_kwargs = {'created_by': {'write_only': True}}
def create(self, validated_data):
item = Item(
item_name=validated_data['item_name'],
created_by=self.context.request.user
)
item.save()
return item
Reference link
It works for me
models.py
class Category(models.Model):
name = models.CharField('Category', max_length=200, unique=True, help_text='Name of the category')
slug = models.SlugField('Slug', max_length=100, db_index=True, unique=True, help_text='Name of the category in format URL')
def __str__(self):
return (self.name)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
serializers.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = [
'id', 'name', 'slug'
]
read_only_fields = [
'slug',
]
Finally, I get the user in the view, before to save the post.
views.py
class CategoryList(APIView):te a new category instance.
permission_classes = (IsAuthenticatedOrReadOnly,)
def get(self, request, format=None):
categories = Category.objects.all()
serializer = CategorySerializer(categories, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request, format=None):
serializer = CategorySerializer(data=request.data)
if serializer.is_valid():
serializer.save(created_by=self.request.user)
Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Django-rest saving model against a specific user

I have a model which has user as a foreign key:
class Task(models.Model):
user = models.ForeignKey(User)
what_task = models.CharField(max_length=100, )
#This helps to print in admin interface
def __str__(self):
return u"%s" % (self.what_task)
It's serializer:
class TaskSerializer(serializers.ModelSerializer):
steps = StepSerializer(many=True)
class Meta:
model = Task
fields = '__all__'
def create(self, validated_data):
steps_data = validated_data.pop('steps')
task = Task.objects.create(**validated_data)
for step_data in steps_data:
Step.objects.create(task=task, **step_data)
return task
And in my view I have a function that handles GET and POST request. GET is correct it returns me all the tasks of a specific user-I use request.user.id for it.
I am not sure about my POST, how can I save task against a specific user in this case:
#api_view(['GET', 'POST'])
def task_list(request):
"""
List all tasks, or create a new task.
"""
if request.method == 'GET':
tasks = Task.objects.filter(user=request.user.id)
serializer = TaskSerializer(tasks, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = TaskSerializer(data=request.data)
print(request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(
serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Where I must make change, in serializer or in view?
Change in your models,
user = models.ForeignKey(User, blank=True)
Then, migrate your changes.
Then, In your views,
if serializer.is_valid():
serializer.save(user=request.user)

Unable to send a PUT request on PrimaryKeyRelatedField

I am learning Django and trying to create a Game REST API using Django REST framework.I am not sure how to update the publisher field.Do I need to create another Serializer for my Publisher class and implement a save method?
I am using POSTMAN to send a PUT request and I am recieving this:
"publisher": [
"Incorrect type. Expected pk value, received unicode."
]
when I send a JSON request
{
"name": "Test",
"release_date": "1979-01-01",
"rating": 5,
"comment": "Terrible Game!",
"publisher": "Me"
}
models.py
class Publisher(models.Model):
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class Game(models.Model):
name = models.CharField(max_length=100)
release_date = models.DateField(null=True, blank=True)
rating = models.IntegerField(
null=True, blank=True,
validators=[MinValueValidator(0), MaxValueValidator(5)])
comment = models.TextField(null=True, blank=True)
publisher = models.ForeignKey(Publisher, null=True, blank=True)
def __unicode__(self):
return self.name
class Meta:
ordering = ('name',)
serializers.py
from rest_framework import serializers
from game.models import Game, Publisher
class GameSerializer(serializers.ModelSerializer):
publisher = serializers.PrimaryKeyRelatedField(allow_null=True, queryset=Publisher.objects.all(), required=False)
class Meta:
model = Game
fields = ('pk','name', 'release_date', 'rating', 'comment', 'publisher')
def create(self, validated_data):
"""
Create and return a new 'Game' instance, given the validated data.
"""
return Game.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
Update and return an existing 'Game' instance, given the validated data.
"""
instance.name = validated_data.get('name', instance.name)
instance.release_date = validated_data.get('release_date',instance.release_date)
instance.rating = validated_data.get('rating', instance.rating)
instance.comment = validated_data.get('comment', instance.comment)
instance.publisher = validated_data.get('publisher', instance.publisher)
instance.save()
return instance
views.py
class GameDetail(APIView):
"""
Retrieve, update or delete a game instance.
"""
def get_object(self,pk):
try:
return Game.objects.get(pk=pk)
except Game.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
game = self.get_object(pk)
serializer = GameSerializer(game)
return Response(serializer.data)
def put(self, request, pk, format=None):
game = self.get_object(pk)
serializer = GameSerializer(game, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
game = self.get_object(pk)
game.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
You're sending a string ("Me") as the publisher attribute. That's why you get your "Incorrect type. Expected pk value, received unicode." error.
Since publisher is a ForeignKey in your Game model, you'll have to get the instance of "Me" Publisher before saving it in your database.
One possible approach is to modify your update() method in your serializer and search for the right instance :
instance.publisher = Publisher.objects.get_or_create(name=validated_data.get('publisher')) and then call instance.save().