'Workout' object has no attribute 'exercises' - python

I just started to play with Django trying to make an API REST, for some reason that i don't understand i'm getting this error 'Workout' object has no attribute 'exercises' when i do GET - /workouts.
I'm using Django REST framework to make this thing.
This is part of my code that I think you need to help me:
serializers.py
class WorkoutSerializer(serializers.ModelSerializer):
exercises = serializers.StringRelatedField(many=True)
class Meta:
model = Workout
fields = ['id', 'name', 'creation_date', 'exercises']
class ExerciseSerializer(serializers.ModelSerializer):
class Meta:
model = Exercise
fields = ['url', 'video_url', 'workout']
models.py
class Workout(models.Model):
name = models.CharField(max_length=30)
creation_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.name
class Exercise(models.Model):
video_url = models.URLField(max_length=200)
workout = models.ForeignKey(Workout, related_name='workout', on_delete=models.CASCADE)
def __str__(self):
return str(self.video_url)
views.py
class WorkoutViewSet(viewsets.ModelViewSet):
queryset = Workout.objects.all()
serializer_class = WorkoutSerializer
class ExerciseViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows exercises to be viewed or edited.
"""
queryset = Exercise.objects.all()
serializer_class = ExerciseSerializer

Related

Django Rest Framework: 'RelatedManager' object has no attribute 'body' error when using nested serializer

With Django DRF, I am trying to display the comments for a particular post in a blog using a nested serializer.
I have run into the following error:
'RelatedManager' object has no attribute 'body'
Here is my code:
comment model:
class Comment(models.Model):
#adapted from https://blog.logrocket.com/use-django-rest-framework-to-build-a-blog/
created = models.DateTimeField(auto_now_add=True)
body = models.TextField(blank=False)
user_id = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='comments', on_delete=models.CASCADE)
post = models.ForeignKey('Posts', related_name='comments', on_delete=models.CASCADE)
#property
def time_left(self):
return self.post.time_left
Post model:
class Posts(models.Model):
title = models.CharField(max_length=100)
topic = MultiSelectField(choices=TOPIC_CHOICES)
creation_timestamp = models.DateTimeField(auto_now_add=True)
expiration_timestamp = models.DateTimeField(default=expiration)
body = models.CharField(max_length=255)
user_id = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) #related_name='posts'
likes = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="likes",blank=True)
dislikes = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="dislikes",blank=True)
#property
def is_expired(self):
#taken from https://stackoverflow.com/questions/41505243/how-to-automatically-change-model-fields-in-django
if now() > self.expiration_timestamp:
return True
return False
#property
def time_left(self):
return self.expiration_timestamp - now()
serializers.py:
class CommentSerializer(serializers.ModelSerializer):
time_left = serializers.ReadOnlyField()
class Meta:
model = Comment
fields = ('created', 'body', 'user_id', 'post','time_left')
class PostsSerializer(serializers.ModelSerializer):
is_expired = serializers.ReadOnlyField()
time_left = serializers.ReadOnlyField()
comments = CommentSerializer(source='comments.body',) ########## THIS IS THE PROBLEMATIC LINE #######
class Meta:
#make sure that the relevant fields are read only
model = Posts
fields = ('comments','title','topic','creation_timestamp','expiration_timestamp','body','user_id','likes','dislikes','is_expired','time_left')
I believe the problematic line is the following one from serializers.py:
comments = CommentSerializer(source='comments.body',)
I think you are confusing the different keywords that a serializer can receive. Here I leave you an answer so that you understand it perfectly.
Your comments variable should look like this.
comments = CommentSerializer(many=True)

Retrieve details from multiple tables using django query

I need to write a single django query such that I'm able to display the "status, date, time, job_name, sol_name and dept_name"
so that a rest api is created.
Model classes
class Job_Report(models.Model):
id:models.BigAutoField(primary_key=True)
job_id: models.ForeignKey(Job, models.DO_NOTHING)
status:models.CharField(max_length=7)
date: models.DateField()
time: models.TimeField()
duration = models.BigIntegerField(blank=True, null=True)
class Job(models.Model):
id:models.BigAutoField(primary_key=True)
name:models.CharField(max_length=500)
folder_id:models.ForeignKey(Job_folder, models.DO_NOTHING)
class Job_folder(models.Model):
id:models.BigAutoField(primary_key=True)
repo_id:models.ForeignKey(Sol_folder, models.DO_NOTHING)
class Sol_folder(models.Model):
id:models.BigAutoField(primary_key=True)
sol_id:models.ForeignKey(Solution, models.DO_NOTHING)
class Solution(models.Model):
id:models.BigAutoField(primary_key=True)
name:models.CharField(max_length=500)
dep_id:models.ForeignKey(Department, models.DO_NOTHING)
class Department(models.Model):
id:models.BigAutoField(primary_key=True)
dept_name:models.CharField(max_length=500)
I tried using
query_set=Job_Folder.objects.raw('select Job_Folder.status,Job_Folder.date,Job_Folder.time,Job.name,Department.name,Solution.name from Job_Folder,Job,Solution')`
but resulted in error.
Any help would be appreciated.
If you are using Django Rest Framework, then it is easy to define the computed values in serializer:
No need to make a query in SQL
Just follow the steps:
Create Serializer of Job_Report model.
class Job_ReportSerializer(serializers.ModelSerializer):
job = serializers.SerializerMethodField()
sol = serializers.SerializerMethodField()
dept = serializers.SerializerMethodField()
class Meta:
model = Job_Report
fields = '__all__'
def get_job(self, obj):
# 'get_' + 'attribute-name' to give definition
return obj.job.job_name
def get_sol(self, obj):
return obj.job.folder.sol.sol_name
.
.
.
# Define for all SerializerMethodField
Follow this for more info about DRF (Django REST Framework) https://www.django-rest-framework.org/tutorial/quickstart/
You'll take hardly 2 hours to complete the tutorial, but trust me it'll save your days of work.
Let me know if you want a solution without DRF.
models.py
All your models as you defined,
Just make one change:
- Do not use ids. Just use foreign keys.
class Job_Report(models.Model):
job: models.ForeignKey(Job, models.DO_NOTHING)
status:models.CharField(max_length=7)
date: models.DateField()
time: models.TimeField()
duration = models.BigIntegerField(blank=True, null=True)
class Job(models.Model):
name:models.CharField(max_length=500)
folder:models.ForeignKey(Job_folder, models.DO_NOTHING)
class Job_folder(models.Model):
name:models.CharField()
repo:models.ForeignKey(Sol_folder, models.DO_NOTHING)
class Sol_folder(models.Model):
name:models.CharField(max_length=500)
sol:models.ForeignKey(Solution, models.DO_NOTHING)
class Solution(models.Model):
name:models.CharField(max_length=500)
dept:models.ForeignKey(Department, models.DO_NOTHING)
class Department(models.Model):
name:models.CharField(max_length=500)
Serializers.py
class Job_ReportSerializer(serializers.ModelSerializer):
job = serializers.SerializerMethodField()
sol = serializers.SerializerMethodField()
dept = serializers.SerializerMethodField()
class Meta:
model = Job_Report
fields = '__all__'
def get_job(self, obj):
# 'get_' + 'attribute-name' to give definition
return obj.job.name
def get_sol(self, obj):
return obj.job.folder.sol.name
.
.
.
# Define for all SerializerMethodField
views.py
#api_view(['GET'])
def list_reports(request):
job_reports = Job_Report.objects.all()
data = JobReportSerializer(job_reports, many=True, context={'request':request}).data
return Response(data)
#api_view(['GET'])
def detail_report(request, pk):
job_report = Job_Report.objects.get(id=pk)
data = JobReportSerializer(job_report,context={'request':request}).data
return Response(data)
urls.py
Add Urls to the functional views
Boom!!! API is ready, don't forget to add 'rest_framework' in INSTALLED_APPS in settings.py

How to make a ManyToMany serialization to be able to create a object in the POST request?

I'm trying to create a serialize to handle a ManyToMany relation, but it's not working. I have read the documentation and I probably doing something wrong. Also I have read the answers here.
Here are my models.
class Author(models.Model):
name = models.CharField(verbose_name="Name", max_length=255)
class Book(models.Model):
author = models.ForeignKey(
Author,
related_name="id_author",
blank=True,
null=True,
on_delete=models.PROTECT)
price = models.FloatField(verbose_name="Price")
class FiscalDocument(models.Model):
seller = models.ForeignKey(
User,
related_name="user_id",
on_delete=models.PROTECT)
book = models.ManyToManyField(Book)
My serializer:
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ('id', 'name')
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('id', 'author', 'price')
def to_representation(self, instance):
response = super().to_representation(instance)
response['author'] = AuthorSerializer(instance.author).data
return response
class FiscalDocumentSerializer(serializers.ModelSerializer):
book = BookSerializer()
class Meta:
model = FiscalDocument
fields = ('id', 'seller', 'book')
def create(self, validated_data):
book_data = validated_data.pop('book')
fiscal_document = FiscalDocument.objects.create(**validated_data)
Book.objects.create(FiscalDocument=FiscalDocument,**medicine_data)
return fiscal_document
When I try to access the endpoint of the FiscalDocument, django-rest-framework is throwing an error:
Got AttributeError when attempting to get a value for field price on serializer BookSerializer. The serializer field might be named incorrectly and not match any attribute or key on the ManyRelatedManager instance. Original exception text was: ManyRelatedManager object has no attribute price.
If anyone can help XD.

Object of type 'ListSerializer' is not JSON serializable

I want to get all customer data and responses and also remarks.
This is model.py
class Customer(models.Model):
name = models.CharField(max_length=200)
email_address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
age = models.SmallIntegerField(default=14)
remarks = models.ManyToManyField(Remark,null=True,blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
class Response(models.Model):
question = models.ForeignKey(Question)
customer = models.ForeignKey(Customer)
response_text = models.CharField(max_length=100, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
def __str__(self):
return str(self.id)
This is serializers.py
class ResponseSerializer(ModelSerializer):
class Meta:
model = Response
fields = '__all__'
class RemarksSerializer(ModelSerializer):
class Meta:
model = Remark
fields = '__all__'
class CustomerInformationSerializer(ModelSerializer):
remarks = RemarksSerializer(many=True)
responses = serializers.SerializerMethodField()
def get_responses(self, obj):
responses = Response.objects.filter(customer=obj)
return ResponseSerializer(responses, many=True)
class Meta:
model = Customer
fields = ('name', 'email_address', 'phone_number', 'age', 'remarks', 'responses')
This is services.py
def customer_information(company_id=1):
cus = Customer.objects.filter(remarks__company_id=company_id)
return CustomerInformationSerializer(cus, many=True).data
This is views.py
class CustomerInformationView(APIView):
def get(self, request):
company_id = request.GET.get('company_id', 1)
resp = {'data': customer_information(company_id)}
return Response(data=resp, status=status.HTTP_200_OK)
This is url.py
url(r'^customer/$', CustomerInformationView.as_view()),
I'm having this problem. How can I solve this. Kindly guide me.
get function in your view should return responses.data, insted of responsed.
SIDE NOTE
First, let me point you to a resource that I think is GREAT for anything dealing with Django REST Framework:
Classy Django REST Framework. It is a fantastic resource because you can easily dig right into the source code to see how you may or may not need to override default operations.
MY ANSWER
What I suggest is that instead of using the APIView, you use ListAPIView.
It would look something like this:
from rest_framework.generics import ListAPIView
class Customer(models.Model):
name = models.CharField(max_length=200)
email_address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
age = models.SmallIntegerField(default=14)
remarks = models.ManyToManyField(Remark,null=True,blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
class Response(models.Model):
question = models.ForeignKey(Question)
customer = models.ForeignKey(Customer, related_name='responses')
response_text = models.CharField(max_length=100, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
def __str__(self):
return str(self.id)
class ResponseSerializer(ModelSerializer):
class Meta:
model = Response
fields = '__all__'
class RemarksSerializer(ModelSerializer):
class Meta:
model = Remark
fields = '__all__'
class CustomerInformationSerializer(ModelSerializer):
remarks = RemarksSerializer(many=True)
responses = ResponseSerializer(many=True)
class Meta:
model = Customer
fields = ('name', 'email_address', 'phone_number', 'age', 'remarks', 'responses')
class CustomerInformationView(ListAPIView):
queryset = Customer.objects.all()
serializer_class = CustomerInformationSerializer
lookup_field = 'remarks__company'
Note the change that I made by adding related_name to the customer field on your Response model. See Django documentation for more information on related_name. In short, it adds responses as a field name on your Customer model so that you can travel backwards through that relationship.
This is not tested, but this should be a better strategy to do what you want without having to have a get_responses method, or a services.py.
Some there might be error because of missing "/" at the end of path like "event-api"=incorrect and "event-api/" correct. That worked for me. Hope you also have same problem.
Incorrect: path('event-api',views.event_view,name="event-view")
Correct: path('event-api/',views.event_view,name="event-view")

django rest framework: object of type 'NoneType' has no len() when calling get method of related table

I am building an django rest api for saving/managing customer data for my project. I have two models. Customer for storing basic customer details and CustomerDetails for storing a bunch of customer details. I want to write a single api to create/update data for both the models.
Now my code is saving the user data. But I now I can't get the customer data. When I call the get method, the following error occurs.
TypeError at /api/v1/customer
object of type 'NoneType' has no len()
Request Method: GET
Request URL: http://127.0.0.1:8000/api/v1/customer
Also, Do I need to do anything extra to use this code for updation (PUT)
urls.py
router.register(r'customer', views.CustomerViewSet, 'customers')
models.py
class Customer(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=20)
address = models.CharField(max_length=50)
city = models.CharField(max_length=256)
"""some more fields to go"""
# Create your models here.
class CustomerDetails(models.Model):
customer = models.OneToOneField(Customer, on_delete=models.CASCADE, primary_key=True, )
spouse_name = models.CharField(max_length=256)
interests = models.CharField(max_length=256)
"""many more fields to go"""
serializers.py
class CustomerDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = CustomerDetails
fields = ('spouse_name',)
class CustomerSerializer(serializers.ModelSerializer):
customer_details = CustomerDetailsSerializer( required=True)
class Meta:
model = Customer
fields = ('name', 'city', 'customer_details', )
def create(self, validated_data):
request = self.context.get('request')
user = request.user
# create user
customer = Customer.objects.create(
user = user,
name = validated_data['name'],
city = validated_data['city'],
# etc ...
)
print(json.dumps(validated_data, indent=4))
customer_details_data = validated_data.pop('customer_details')
# create profile
customer_details = CustomerDetails.objects.create(
customer = customer,
spouse_name = customer_details_data['spouse_name'],
)
customer.customer_details = customer_details_data;
return customer
class CustomerListSerializer(serializers.ModelSerializer):
model = Customer
customer_details = CustomerDetailsSerializer()
class Meta:
model = Customer
fields = '__all__'
views.py
class CustomerViewSet(viewsets.ModelViewSet):
customer_photo_thumb = Customer.get_thumbnail_url
customer_details = ''
permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
queryset = Customer.objects.all()
serializer_class = CustomerSerializer
def get_queryset(self):
queryset = Customer.objects.all()
def get_serializer_class(self):
if self.action == 'list' or self.action == 'retrieve':
return CustomerListSerializer
return CustomerListSerializer
Your method .get_queryset return None replace like this:
def get_queryset(self):
return Customer.objects.all()
In fact your get_queryset method is not necessary since the queryset attribute does not change.

Categories