Django-rest saving model against a specific user - django

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)

Related

How can I pass serializer.data into create function without using save function in views django?

views.py
def productslist(request):
products = Products.objects.all()
context = {'products':products}
return render(request,'productslist.html',context)
def productsform(request):
return render(request,'productscreate.html')
#api_view(['GET','POST'])
def products_list(request):
if request.method == 'GET':
product = Products.objects.all()
serializer = Productserialize(product,many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = Productserialize(data=request.data)
if serializer.is_valid():
def create(serializer):
return Products.objects.create(serializer)
return Response(serializer.data)
return Response(serializer.errors)
#api_view(['GET','PUT'])
def products_detail(request, pk):
products = Products.objects.get(pk=pk)
if request.method == 'GET':
serializer = Productserialize(products)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = Productserialize(products, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
In views.py I have done create function but it is not working not saving data
I want to save serializer data using create function
I want to pass serializer.data into create function in post method api
serializer.py
class Productserialize(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
description = serializers.CharField(required=False, allow_blank=True, max_length=100)
image = serializers.FileField()
def create(self, validated_data):
"""
Create and return a new `Snippet` instance, given the validated data.
"""
return Products.objects.create(**validated_data)
Just use a ListCreateAPIView and use a ModelSerializer instead of a Serializer.
# Serializer
class ProductSerializer(serializers.ModelSerializer):
class Meta:
fields = ('id', 'title', 'description', 'image')
model = Product
# View
from rest_framework.generics import ListCreateAPIView
class ProductListCreate(ListCreateAPIView):
serializer_class = ProductSerializer
queryset = Product.objects.all()
ListCreateAPIView handle GET to list and POST to create new objects, you don't need to override anything else.
you can try to call it
if serializer.is_valid():
def create(serializer):
return Products.objects.create(serializer.data)
crerate(serializer)
return Response(serializer.data)

Django Rest FrameWork Add Model With User Foreign Key

I'm using Django version 3 and the Rest Framework, i have a model with a user foreignkey.
so every time a model is saved the user also need to be saved.
To be able to add or to edit a model via rest you need to be authenticated using token authentication.
I'm using ClassBasedViews, the problem is that i can't find a way to add a model because in my serializer the field user is excluded because i don't want it to be editable.
models.py:
class Chambre(models.Model):
local_id=models.PositiveIntegerField(unique=True)
nom=models.CharField(max_length=255)
user=models.ForeignKey(User,on_delete=models.CASCADE,blank='true')
class Meta:
unique_together = ('local_id', 'user',)
serializers.py:
class ChambreSerializer(serializers.ModelSerializer):
class Meta:
model = Chambre
exclude =['user',]
views.py:
class ChambreListApi(APIView):
"""
List all chambres, or create a new chambre.
"""
authentication_classes=(TokenAuthentication,)
permission_classes=(IsAuthenticated,)
def get(self, request, format=None):
chambres = Chambre.objects.filter(user=request.user)
serializer = ChambreSerializer(chambres, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = ChambreSerializer(data=request.data)
if serializer.is_valid():
serializer.save(commit=False)
serializer.user=request.user
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Try moving the logic that sets the user to the serializer. By default GenericAPIView will pass the request to the serializer via the context.
class ChambreSerializer(serializers.ModelSerializer):
class Meta:
model = Chambre
exclude =['user',]
def create(self, validated_data):
validated_data["user"] = self.context["request"].user
return super().create(validated_data)
When I followed the accepted solution posted by schillingt, I had to add context={'request': request} when I instantiated the serializer. The Django REST Framework docs mention adding context to a serializer here.
serializers.py
class NoteCreateSerializer(serializer.ModelSerializer):
class Meta:
model = Note
exclude = ['owner',]
def create(self, validated_data):
validated_data['owner'] = self.context['request'].user
return super().create(validated_data)
views.py
#api_view['POST']
def create_note(request):
note_serializer = NoteCreateSerializer(
data = request.data,
context = {'request': request} # this will populate 'self.context'
# in the serializer instance
)
# ...
Thanks so much for this solution, though! It ended an hours-long struggle.
thanks for the reply that was very useful, i've solved it by adding method to serializer.py like this :
class ChambreSerializer(serializers.ModelSerializer):
class Meta:
model = Chambre
exclude =['user',]
def set_the_user(self,request):
self.user=request.user
def create(self, validated_data):
validated_data["user"] = self.user
return super().create(validated_data)
and in the views.py :
def post(self, request, format=None):
serializer = ChambreSerializer(data=request.data)
serializer.set_the_user(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)

django rest m2m serializers throw Bad request error

I have two models: Task and Scenario:
class Task(models.Model):
stakeholder = models.ForeignKey(User, related_name='tasks', blank=True, )
project = models.ForeignKey(Project, related_name='project_tasks' )
title = models.CharField(max_length=50, blank=True, null = True, )
...
class Scenario(models.Model):
stakeholder = models.ForeignKey(User, related_name='scenarios', blank=True,)
tasks = models.ManyToManyField(Task, blank=True)
Their serialzers:
class TaskSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
class Meta:
model = Task
fields = '__all__'
class ScenarioSerializer(serializers.ModelSerializer):
tasks = TaskSerializer(many=True, required=False)
class Meta:
model = Scenario
fields = '__all__'
def create(self, validated_data):
tasks = validated_data.pop('tasks')
instance = Scenario.objects.create(**validated_data)
for task_data in tasks:
task = Task.objects.get(pk=task_data.get('id'))
instance.tasks.add(task)
return instance
def update(self, instance, validated_data):
tasks = validated_data.pop('tasks', [])
instance = super().update(instance, alidated_data)
for task_data in tasks:
task = Task.objects.get(pk=task_data.get('id'))
instance.tasks.add(task)
return instance
And view to perform CRUD operations on both these models:
#api_view(['GET', 'POST'])
def task_list(request):
tasks = []
"""
List all tasks, or create a new task.
"""
if request.method == 'GET':
#get all the tasks in aspecific project
if request.query_params.get('projectId'):
# get a specific project
projectId = request.query_params.get('projectId')
project = Project.objects.get(id=projectId)
tasks = project.project_tasks.all()
serializer = TaskSerializer(tasks, many=True)
return Response(serializer.data)
else:
tasks = Task.objects.filter(stakeholder=request.user)
serializer = TaskSerializer(tasks, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = TaskSerializer(data=request.data)
if serializer.is_valid():
serializer.save(stakeholder=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(
serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#api_view(['GET', 'PUT', 'DELETE'])
def task_detail(request, pk):
"""
Get, udpate, or delete a specific task
"""
try:
task = Task.objects.get(pk=pk)
except Task.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = TaskSerializer(task)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = TaskSerializer(task, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(
serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
task.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
for scenario:
#api_view(['GET', 'POST'])
def scenarios_list(request):
scenarios = []
"""
List all scenarios, or create a new.
"""
if request.method == 'GET':
# get projects of a specific stakeholder
if request.query_params.get('stakeholderId'):
stakeholderId = request.query_params.get('stakeholderId')
scenarios = Scenario.objects.filter(stakeholder=stakeholderId)
serializer = ScenarioSerializer(scenarios, many=True)
return Response(serializer.data)
else:
scenarios = Scenario.objects.all()
serializer = ScenarioSerializer(scenarios, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = ScenarioSerializer(data=request.data)
if serializer.is_valid():
serializer.save(stakeholder=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(
serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#api_view(['GET', 'PUT', 'DELETE'])
def scenario_detail(request, pk):
"""
Get, udpate, or delete a specific scenarios
"""
try:
scenario = Scenario.objects.get(pk=pk)
except Project.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = ScenarioSerializer(scenario)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = ScenarioSerializer(scenario, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(
serilizer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
scenario.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
The idea is Tasks will be created first and then a scenario can be created by attaching a few tasks to it.
Scenario creation is successful but since I implemented logic of scenarios, task creation and updation stated failing throwing me Bad request error.
request looks like following:
{project: "6", title: "Fourteen", how_often: "", how_important_task: "", role: "", …}
Do I need to update the task serializer as well? What is going wrong?
UPDATE: If I comment id = serializers.IntegerField()in TaskSerializer Task creation is successful, but I need that line otherwise scenario creation fails.
please, remove id from TaskSerializer and try it:
class ScenarioSerializer(serializers.ModelSerializer):
tasks = TaskSerializer(many=True, required=False)
class Meta:
model = Scenario
fields = '__all__'
def get_or_create_task(self, data):
qs = Task.objects.filter(pk=data.get('id'))
if qs.exists():
return qs.first()
task = Task.objects.create(**data)
return task
def add_tasks(self, instance, tasks):
for task_data in tasks:
task = self.get_or_create_task(task_data)
instance.tasks.add(task)
def create(self, validated_data):
tasks = validated_data.pop('tasks')
instance = Scenario.objects.create(**validated_data)
self.add_tasks(instance, tasks)
return instance
def update(self, instance, validated_data):
tasks = validated_data.pop('tasks', [])
instance = super().update(instance, alidated_data)
self.add_tasks(instance, tasks)
return instance
and why you don't use class base views for your app?

Update a Foreign Key using PUT in Django Rest Framework

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.

PUT request using Django serializer

I have an architecture in a way that when user registers, his/her settings are initialized. So we need GET and PUT to get or update the settings respectively on settings API.
I am using model serializers of django rest framework for storing and getting the settings. It works well in giving response but it doesn't save that to models.
serializers.py
class UserSettingsSerializer(serializers.ModelSerializer):
class Meta:
model = UserSettings
fields = ('group_notifications', 'updates', 'goal_remind_me',
'goal_days', 'goal_time_interval', 'user')
models.py
class UserSettings(models.Model):
class Meta:
db_table = 'user_settings'
user = models.ForeignKey('User')
group_notifications = models.BooleanField(default=True)
updates = models.BooleanField(default=False)
goal_remind_me = models.BooleanField(default=False)
goal_days = ListField()
goal_time_interval = models.IntegerField(null=True)
views.py
def settings(request, pk):
if request.method == 'PUT':
request.data['user'] = user.id
serializer = UserSettingsSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
serializer_dict = serializer.data
serializer_dict["message"] = "Settings updated successfully."
return Response(serializer_dict, status=status.HTTP_200_OK)
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
I get response as expected but it's not storing data in user_settings table and I am using PostgreSql.
In order for a ModelSerializer to preform an update, you need to pass in the instance that you want updated to the constructor.
def settings(request, pk):
# query for the UserSettings object
instance = get_object_or_404(UserSettings.objects.all(), pk=pk)
if request.method == 'PUT':
request.data['user'] = user.id
# pass in the instance we want to update
serializer = UserSettingsSerializer(instance, data=request.data)
# validate and update
if serializer.is_valid():
serializer.save()
serializer_dict = serializer.data
serializer_dict["message"] = "Settings updated successfully."
return Response(serializer_dict, status=status.HTTP_200_OK)
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)