How can I get the related object link under OneToOne relation.
I have 2 models:
class Model1(models.Model):
title = models.CharField(max_length=255, null=False)
class Model2(models.Model):
mymodel1 = models.OneToOneField(Model1, on_delete=models.CASCADE)
In the Model1Serializer, how can i get the link to MyModel2 related object
ahd get a result as follows:
[
{
"title": "My obj title"
"link2mymodel2": "http://myhost/model2/pk"
}
]
where pk is the relared object
You can add it as a property to Model1.
models.py
class Model1(models.Model):
title = models.CharField(max_length=255, null=False)
#property
def link2mymodel2(self):
return self.model2.link
class Model2(models.Model):
mymodel1 = models.OneToOneField(Model1, related_name=model2, on_delete=models.CASCADE)
link = models.URLField()
serializers.py
class Model1Serializer(serializers.ModelSerializer):
link2mymodel2 = serializers.URLField()
class Meta:
model = Model1
fields = ["title", "link2model2"]
Related
Supposing we have the following models:
class Person(models.Model):
name = models.CharField(max_length=10, blank=True, null=True)
class Food(models.Model):
person = models.ForeignKey(Person, default=None, on_delete=models.CASCADE)
name = models.CharField(max_length=10, blank=True, null=True)
I have these models in ModelViewSets and registered them as URLs with Django Rest Framework. And they are accessible as ressource URI successfully, but I want to add field for Food that can return the Person ressource URI. I can hardcode it as follows but I need something better:
class Food(models.Model):
person = models.ForeignKey(Person, default=None, on_delete=models.CASCADE)
name = models.CharField(max_length=10, blank=True, null=True)
def get_person_uri(self):
return f"http://127.0.0.1:8000/persons/{self.person.id}/"
Serializer is as follows:
class FoodSerializer(serializers.Model):
class Meta:
model = Food
fields = ["person", "name", "get_person_uri"]
Where get_person_uri should take me directly to the parent person URI
Moreover, here is my View:
class FoodViewSet(ModelViewSet):
serializer_class = FoodSerializer
permission_classes = [IsAuthenticated]
model = serializer_class.Meta.model
queryset = model.objects.all()
Here is how it is registered in urls.py:
router.register(r"foods", FoodViewSet, basename="foods")
Expected JSON response:
{
"name" : "Orange",
"person": 3,
"person_uri": "http://example.com/api/person/3"
}
What solutions can you suggest?
Good day,
I would like to ask, if there's a possibility to gain additional data inside my serializers?
These are my models...
models.py
class Chair(models.Model):
name = models.CharField(max_length=100, null=False, blank=False, unique=True)
bookable = models.BooleanField(default=False)
user_created = models.CharField(max_length=100)
date_created = models.DateField(auto_now_add=True)
class Booking(models.Model):
chair = models.ForeignKey(Chair, on_delete=models.CASCADE)
day = models.DateField()
user_name = models.CharField(max_length=100)
user_created = models.CharField(max_length=100)
date_created = models.DateField(auto_now_add=True)
and these my serializers...
serializers.py
class BookingSerializer(serializers.ModelSerializer):
class Meta:
model = Booking
fields = '__all__'
class ChairSerializer(serializers.ModelSerializer):
class Meta:
model = Chair
fields = '__all__'
When making a request inside js like this...
views.py
#api_view(['GET'])
def bookings_by_date(request, pk):
bookings = Booking.objects.filter(day=pk)
serializer = BookingSerializer(bookings, many=True)
return Response(serializer.data)
script.js
let url = '...here's my url for Booking...';
fetch(url)
.then((resp) => resp.json())
.then(function(data) {
// do something here
});
...I would like to get not only the id of the Chair (models.Foreignkey), but also it's name. My first thought was doing something like this...
class ChairSerializer(serializers.ModelSerializer):
class Meta:
model = Chair
fields = [
...
'chair',
'chair__name',
...
]
...but this doesn't seem to work! Does anyone know a solution for my problem? Thanks for all your help and have a great weekend!
You can use one of this two ways:
1-) Using SerializerMethodField. You can add readonly fields with this way. You should add get_<field_name> method or give a method name that you want to run for this field with name keyword. You can look the document for more details.
class BookingSerializer(serializers.ModelSerializer):
chair__name = serializers.SerializerMethodField()
class Meta:
model = Booking
fields = '__all__'
def get_chair_name(self, obj):
return obj.chair.name
2-) Using CharField with source attribute:
You can define basically this field fill from where.
class BookingSerializer(serializers.ModelSerializer):
chair__name = serializers.CharField(source='chair__name')
class Meta:
model = Booking
fields = '__all__'
I am creating an API to save class teachers. Now all the fields in the ClassTeacher model are foreign fields so I am using a SlugRelatedField in the serializer. It looks like SlugRelatedField does not support attribute lookup like this "user__username" and raises attribute error HOWEVER the object is still being saved.
models.py
class ClassTeacher(models.Model):
teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)
class_name = models.ForeignKey(Classes, on_delete=models.CASCADE)
school_id = models.ForeignKey(School, on_delete=models.CASCADE)
serializers.py
class ClassTeacherSerializer(ModelSerializer):
teacher = SlugRelatedField(slug_field='user__username', queryset=Teacher.objects.all()) <---- this is causing the error
class_name = SlugRelatedField(slug_field='class_name', queryset=Classes.objects.all())
school_id = SlugRelatedField(slug_field='school_id__username', queryset=School.objects.all()) <---- and I am assuming that this will too
class Meta:
model = ClassTeacher
fields = '__all__'
I tried adding a #property in the Teacher model to retrieve the username and use the property in the slug_field but that did not work too.
How can I save the object without getting the error?
EDIT 1:
teachers/models.py
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
photo = models.URLField()
teacher/serializers.py
class TeacherSerializer(ModelSerializer):
class Meta:
model = Teacher
fields = '__all__'
school/models.py
class School(models.Model):
school_id = models.OneToOneField(User, on_delete=models.CASCADE)
principal = models.CharField(max_length=50)
name = models.CharField(max_length=50)
photo = models.URLField()
school/serializers.py
class SchoolSerializer(ModelSerializer):
class Meta:
model = School
fields = '__all__'
EDIT 2:
Here's how I used the #property by referring from here:
teacher/models.py
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
photo = models.URLField()
#Here's the extra property part
#property
def username(self):
return self.user.username
classteacher/serializers.py
class ClassTeacherSerializer(ModelSerializer):
#Here I changed user__username to just username as mentioned in the above link
teacher = SlugRelatedField(slug_field='username', queryset=Teacher.objects.all())
class_name = SlugRelatedField(slug_field='class_name', queryset=Classes.objects.all())
school_id = SlugRelatedField(slug_field='school_id__username', queryset=School.objects.all())
class Meta:
model = ClassTeacher
fields = '__all__'
try renaming serializer field from teacher to user and using slug_field='username'
You can use #property for example
class User(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
photo = models.URLField()
#property
def get_username(self):
return Teacher.objects.filter(user_id=self.id)
and than inside your ClassTeacherSerializer use slug_field='username'
let me know if it works.
i want to count comments for every single Post
in models.py:
class Post(models.Model):
body = models.TextField(max_length=10000)
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
liked_by = models.ManyToManyField(User, blank=True, related_name='liked_by')
class Meta:
ordering = ['-date']
class Comment(models.Model):
body = models.TextField(max_length=1000)
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
class Meta:
ordering = ['-date']
in serializers.py:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
#comments = CommentSerializer()
user = UserSerializers()
total_likes = serializers.SerializerMethodField()
liked_by = SimpleUserSerializer(many=True, read_only=True)
total_comments = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('body','date','user', 'total_likes', 'liked_by','total_comments')
def get_total_likes(self, instance):
return instance.liked_by.count()
def get_total_comments(self, instance):
return instance.comments.count()
when i run this code, it shows, AttributeError: 'Post' object has no attribute 'comments'.
how do i count comments of a post?
Since you haven't configured the related_name, Django uses the default related_name and hence you should access the reveres FK using comment_set instead of comments
Thus, the get_total_comments(...) method should look like
def get_total_comments(self, instance):
return instance.comment_set.count()
Reference
What is related_name used for in Django?
I have designed a table for attribute and product attributes. An attribute can have many values.
For example, an attribute called color can have values like Black, white, Grey, Maroon etc. For
this I designed a table such way
However when registering to the admin, I get AttributeError: 'AttributeValueAdmin' object has no attribute 'urls' error.
class Attribute(models.Model):
name = models.CharField(max_length=30, unique=True)
slug = models.SlugField(max_length=250, unique=True)
class Meta:
verbose_name = "Attribute"
verbose_name_plural = "Attributes"
def __str__(self):
return self.name
class ProductAttribute(SortableModel):
product = models.ForeignKey(Product,
related_name="productattribute",
null=True,
on_delete=models.CASCADE)
attribute = models.ManyToManyField(
Attribute,
through="AttributeValue"
)
class Meta:
ordering = ("sort_order",)
verbose_name = "Product Attribute"
verbose_name_plural = "Product Attributes"
class AttributeValue(SortableModel):
name = models.CharField(max_length=250)
value = models.CharField(max_length=100, blank=True, default="")
slug = models.SlugField(max_length=255)
productattribute = models.ForeignKey(ProductAttribute,
null=True,
related_name='productattribute',
on_delete=models.CASCADE)
attribute = models.ForeignKey(
Attribute, related_name="values", on_delete=models.CASCADE
)
class Meta:
ordering = ("sort_order", "id")
unique_together = ("slug", "attribute")
def __str__(self) -> str:
return self.name
admin.py
class ProductAdmin(admin.ModelAdmin):
model = models.Product
prepopulated_fields = {'slug': ('name',), }
class AttributeValueAdmin(admin.TabularInline):
model = models.AttributeValue
extra = 2
class AttributeAdmin(admin.ModelAdmin):
model = models.Attribute
prepopulated_fields = {'slug': ('name',), }
class ProductAttributeAdmin(admin.ModelAdmin):
# model = models.ProductAttribute
inlines = (AttributeValueAdmin, )
admin.site.register(models.Attribute, AttributeAdmin)
admin.site.register(models.AttributeValue, AttributeValueAdmin)
admin.site.register(models.ProductAttribute, ProductAttributeAdmin)
As per django docs on admin, the first step is to display the intermediate model by subclassing inline class for AttributeValue table like you have done
class AttributeValueInline(admin.TabularInline):
model = models.AttributeValue
extra = 2
Second step is to create admin views for both Attribute and ProductAttribute models.
class AttributeAdmin(admin.ModelAdmin):
inlines = (AttributeValueInline, )
prepopulated_fields = {'slug': ('name',), }
class ProductAttributeAdmin(admin.ModelAdmin):
inlines = (AttributeValueInline, )
Third step is to register your Attribute and ProductAttribute models
admin.site.register(models.Attribute, AttributeAdmin)
admin.site.register(models.ProductAttribute, ProductAttributeAdmin)
You don't need to register AttributeValue model as you can create/edit AttributeValue inline from either Attribute or ProductAttribute table.
For reference you can read the django docs
https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#working-with-many-to-many-intermediary-models