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?
Related
view.py
#csrf_exempt
def blog_list(request):
if request.method == 'GET':
post = Post.objects.all()
serializer = BlogSerializer(post, many=True)
return JsonResponse(serializer.data,safe=False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = BlogSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
class BlogSerializer(serializers.Serializer):
class Meta:
model = Post
fields = ['author', 'title', 'text', 'published_date']
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
First you need to understand a few things about this code
def blog_list(request):
if request.method == 'GET':
post = Post.objects.all()
serializer = BlogSerializer(post, many=True)
return JsonResponse(serializer.data,safe=False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = BlogSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
Here request referes to a request object, which contains all the data inside the request (method, body, data, headers, etc), and not just the request data. So, you want to parse request.body, instead of request.
data = JSONParser().parse(request.body)
You should pass request.data to serializer. Passing whole request won't return anything, because request does not have fields you declared in serializer. They are in post data.
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)
I have this comment serializer which is not working. It is showing this error
{
"post": [
"This field is required."
]
}
I am quite new to this, so can't really figure out how to properly link both the post and comment model.
this is my serializer
class CommentCreateSerializer(serializers.ModelSerializer):
content = serializers.CharField()
post = PostSerializer(many=True)
class Meta:
model = Comment
fields = ['id', 'post', 'content', 'reply']
def create(self, validated_data):
user = self.context['request'].user
content = validated_data['content']
reply = validated_data['reply']
if reply:
comment = Comment(user=user, content=content, reply=reply, post=post)
comment.save()
else:
comment = Comment(user=user, content=content, post=post)
comment.save()
return validated_data
view
#api_view(['POST'])
def comment_post_api(request, slug):
try:
post = get_object_or_404(Post, slug=slug)
except Post.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = CommentCreateSerializer(post,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)
urls
path('<slug>/comment/', views.comment_post_api, name='comment_post'),
Thanks
You can try these way:
class CommentCreateSerializer(serializers.ModelSerializer):
content = serializers.CharField()
user_id = serializers.PrimaryKeyRelatedField(source='user',queryset=User.objects.all(),write_only=True)
post_id = serializers.PrimaryKeyRelatedField(source='post',queryset=Post.objects.all(),write_only=True)
class Meta:
model = Comment
fields = ['id', 'post_id','user_id','content', 'reply']
and remove you create method not needed because you forgot to add post object creation before assign
or you can just write
class CommentCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
no need add custom create method here also
views.py
#api_view(['GET','POST'])
def comment_post_api(request, slug):
try:
post = get_object_or_404(Post, slug=slug)
except Post.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
comments = Comment.objects.filter(post=post)
serializers = CommentCreateSerializer(comments,many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
if request.method == 'POST':
serializer = CommentCreateSerializer(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)
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)
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)