I'm working with a Django project that implement the Rest framework.
I have this model
class Portfolio(models.Model):
ticker = models.CharField(max_length=10, default='???')
name = models.CharField(max_length=25)
amount = models.FloatField()
owner = models.ForeignKey('auth.User', related_name='portfolio', on_delete=models.CASCADE)
def __str__(self):
return self.name
Note on the 'owner' ForeignKey.
And in my serializers.py I have this
class MyRelatedField(serializers.RelatedField):
def to_representation(self, obj):
return 'Test'
class UserSerializer(serializers.ModelSerializer):
portfolio = serializers.MyRelatedField(many=True)
class Meta:
model = User
fields = ('url', 'id', 'username', 'portfolio')
AS I read on the docs, I should override the RelatedField (which I did) if I want a custom representation. However when I try to run I get this error
AttributeError: module 'rest_framework.serializers' has no attribute 'MyRelatedField'
No matter what I return in MyRelatedField, the same error occurs.
My question is how to debug and ideally, fix this error.
Thank you.
Since MyRelatedField and UserSerializer located in same module you need to replace portfolio = serializers.MyRelatedField(many=True) with portfolio = MyRelatedField(many=True).
Related
My models have users that can have multiple devices. When I do a GET request on users it returns only the fields specified in the user model, as it should. But I want the option to include in the JSON returned by the GET request the list of devices the user has. How can I do that? Secondly, is there a way I can sometimes get a user with the list of devices in the same JSON, and other times without it? Also, I am really new to Django, and I would appreciate a lot code examples to understand better, if possible.
These are my models:
class User(models.Model):
name = models.CharField(max_length=100)
birth_date = models.DateField()
address = models.CharField(max_length=200)
class Device(models.Model):
description = models.CharField(max_length=200)
location = models.CharField(max_length=200)
max_energy_consumption = models.FloatField()
avg_energy_consuumption = models.FloatField()
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
My serializers:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = '__all__'
And the following default ModelViewSets for CRUD api calls:
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class DeviceViewSet(ModelViewSet):
queryset = Device.objects.all()
serializer_class = DeviceSerializer
There are some different ways easiest one would be add a property in your User model and add that to your serializer
class User(models.Model):
name = models.CharField(max_length=100)
birth_date = models.DateField()
address = models.CharField(max_length=200)
#property
def devices(self):
return Device.objects.filter(user_id=self.id).values("location", "description").distinct()
class UserSerializer(serializers.ModelSerializer):
devices = serializers.ReadOnlyField()
class Meta:
model = User
fields = '__all__'
EDIT - for second part of your question:
I have experienced that writing '__all__' in our serializers not the best thing to do when we do not need all the information all the time(performance issues). To address this obsticle making a seperate serializer would be again an easy solution. Whenever I am facing this kind of thing i query same endpoint but send a different qs that i dont use in other endpoint in your case lets say your user viewsets route is something like /api/user/ you can add a qs when you send your get request to your backend and add ?with_devices=true.
Then you can use your second user serializer like this:
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
def get_serializer_class(self):
if self.request.GET.get("with_devices", False):
return UserWithDeviceSerializer
return UserSerializer
where your serializers would be something like:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["name", "birth_date", ..so on]
class UserWithDeviceSerializer(serializers.ModelSerializer):
devices = serializers.ReadOnlyField()
class Meta:
model = User
fields = '__all__'
This would give you what you asked in the comment.
I am trying to update my Teachers view in DRF by instead of including the link to the department field, I would display the name of the department. When I added the PrimaryKeyRelated field, I was able to see the department.name but couldnt use update or create within DRF. Is there a way I could change the display without causing the need for the methods or is that not the case?
Error
The `.update()` method does not support writable dotted-source fields by default.
Write an explicit `.update()` method for serializer `school.serializers.TeacherSerializer`, or set `read_only=True` on dotted-source serializer fields.
The `.create()` method does not support writable dotted-source fields by default.
Write an explicit `.create()` method for serializer `school.serializers.TeacherSerializer`, or set `read_only=True` on dotted-source serializer fields.
models.py
class Department(models.Model):
name = models.CharField(max_length=300)
def __str__(self):
return self.name
class Teacher(models.Model):
name = models.CharField(max_length=300)
department = models.ForeignKey(Department, on_delete=models.CASCADE)
tenure = models.BooleanField()
def __str__(self):
return f'{self.name} teaches {self.department}'
# dont need success url if get_absolute_url on create and update view
def get_absolute_url(self):
return reverse('teacher', kwargs={'pk': self.pk})
serializers.py
class TeacherSerializer(serializers.HyperlinkedModelSerializer):
department = serializers.PrimaryKeyRelatedField(
source='department.name', queryset=Department.objects.all())
class Meta:
model = Teacher
fields = ['url', 'name', 'department', 'tenure']
class DepartmentSerializer(serializers.HyperlinkedModelSerializer):
teacher_set = TeacherSerializer(many=True, required=False)
class Meta:
model = Department
fields = ['url', 'name', 'teacher_set']
views.py
class TeacherViewSet(viewsets.ModelViewSet):
queryset = Teacher.objects.all()
serializer_class = TeacherSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
class DepartmentViewSet(viewsets.ModelViewSet):
queryset = Department.objects.all()
serializer_class = DepartmentSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
have you tried add related_name for model Teacher in field foreign key and call in serializers? link to docs
everyone. I want to make a test in DRF. But DRF gave me this error:
models.py:
class TestTaker(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
test = models.ForeignKey(Test, on_delete=models.CASCADE)
class UsersAnswers(models.Model):
test_taker = models.ForeignKey(TestTaker, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
answer = models.ForeignKey(Answer, on_delete=models.CASCADE)
serializers.py
class UsersAnswersSerializer(serializers.ModelSerializer):
class Meta:
model = UsersAnswers
fields = "__all__"
class TestTakerSerializer(serializers.ModelSerializer):
users_answers_set = UsersAnswersSerializer(many=True)
class Meta:
model = TestTaker
fields = "__all__"
And the error is:
Got AttributeError when attempting to get a value for field `users_answers_set` on serializer `TestTakerSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `TestTaker` instance.
Original exception text was: 'TestTaker' object has no attribute 'users_answers_set'.
I tried to add "source" parameter to users_answers_set, but nothing changed.
Thank you.
try usersanswers_set instead of users_answers_set.
or
you can define custom related_name in your model:
class UsersAnswers(models.Model):
test_taker = models.ForeignKey(TestTaker, related_name="users_answers_set" , on_delete=models.CASCADE)
During a GET request, Django give me this error:
Got AttributeError when attempting to get a value for field `name` on serializer `projectSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `ProjectTask` instance.
Original exception text was: 'ProjectTask' object has no attribute 'name'.
I'm pretty new to django and rest_framework, here's the code:
models.py
class Project(models.Model):
name = models.CharField(max_length=50)
class ProjectTask(models.Model):
project = models.ForeignKey(Project,on_delete=models.CASCADE)
description = models.CharField(max_length=200)
status = models.IntegerField()
serializers.py
class projectTaskSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectTask
fields = ['description','status']
class projectSerializer(serializers.ModelSerializer):
tasks = projectTaskSerializer(many=True)
class Meta:
model = Project
fields = ['name','tasks']
views.py (I'm only mentioning the get function that is being called to avoid cluttering the question
def get(self, request):
projects = ProjectTask.objects.all()
serialized_projects = projectSerializer(projects,many = True)
return Response({'projects' : serialized_projects.data})
I am trying to create form using this model. I want to add data in this database model using form to perform CRUD operation. I am using MySQL database.
models.py
from django.db import models
from .managers import CategoryManager, SubCategoryManager
# this is my parent model
class Node(models.Model):
name = models.CharField(max_length=150)
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
related_name='children',
null=True,
blank=True
)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
verbose_name_plural = 'Nodes'
class Category(Node):
object = CategoryManager()
class Meta:
proxy = True
verbose_name_plural = 'Categories'
class SubCategory(Node):
object = SubCategoryManager()
class Meta:
proxy = True
verbose_name_plural = 'SubCategories'
class Product(models.Model):
sub_category = models.ForeignKey(
SubCategory, on_delete=models.CASCADE
)
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
def __str__(self):
return self.name
Try the Imagine smart compiler which allows automatic generating of code + tests for your CRUD APIs and Django models from a very simple config. Amongst other things, it generates code in the correct way to handle foreign key relationships in Django Views. You can also try a demo here imagine.ai/demo
PS: Something like this simple config will generate all the code for the CRUD API along with the tests!
Model Node {
id integer [primary-key]
name string [max-length 150]
}
Model Product {
id integer [primary-key]
name string [max-length 100]
description string [nullable]
}