Django DRF nested serializers are not linked - python

I'm trying to build an API for a picture gallery using Django and Django Rest Framework for the API.
I have set up the following two models:
class Album(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(null=True, blank=True)
start_date = models.DateField()
end_date = models.DateField(null=True, blank=True)
parent_album = models.ForeignKey('self',
null=True,
blank=True,
on_delete=models.CASCADE
)
class Picture(models.Model):
path = models.ImageField()
album = models.ForeignKey('Album', on_delete=models.CASCADE)
For the serializers, I have defined the following, using the official Django DRF doc:
class PictureSerializer(serializers.ModelSerializer):
class Meta:
model = Picture
fields = '__all__'
class AlbumSerializer(serializers.ModelSerializer):
pictures = PictureSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = '__all__'
Now, I have some objects already defined, so I wanted to try in a shell:
>>> album = Album.objects.get(pk=1)
>>> len(Picture.objects.filter(album__exact=Album.objects.get(pk=1)))
3
>>> AlbumSerializer(instance=album).data
{'id': 1, 'name': 'First album', 'description': '', 'start_date': '2019-08-15', 'end_date': None, 'parent_album': None}
>>> AlbumSerializer()
AlbumSerializer():
id = IntegerField(label='ID', read_only=True)
pictures = PictureSerializer(many=True, read_only=True):
id = IntegerField(label='ID', read_only=True)
path = ImageField(max_length=100)
album = PrimaryKeyRelatedField(queryset=Album.objects.all())
name = CharField(max_length=100)
description = CharField(allow_blank=True, allow_null=True, required=False, style={'base_template': 'textarea.html'})
start_date = DateField()
end_date = DateField(allow_null=True, required=False)
parent_album = PrimaryKeyRelatedField(allow_null=True, queryset=Album.objects.all(), required=False)
You will notice that there are no pictures in the serialized data, but the field is there when I print the serializer, and that's my problem... Have I missed something somewhere?
Thanks for the help!

Your problem is due to the related name given to ForeignKeys.
Right now with the code you provided the name backwards relation name should be picture_set. So for the serializer to work you should change it do the following:
class AlbumSerializer(serializers.ModelSerializer):
picture_set = PictureSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = '__all__'
But, if you want to display as pictures you can also do the following:
class AlbumSerializer(serializers.ModelSerializer):
pictures = PictureSerializer(many=True, read_only=True, source='picture_set')
class Meta:
model = Album
fields = '__all__'
Finally, if you want to get rid of the picture_set you should change your code to the following:
models.py
class Picture(models.Model):
path = models.ImageField()
album = models.ForeignKey('Album', on_delete=models.CASCADE, related_name='pictures')
serializers.py
class AlbumSerializer(serializers.ModelSerializer):
pictures = PictureSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = '__all__'

Related

My Django Admin input doesn't allow me to add more than one image

I'm trying to make a Django model, with Django Rest Framework. I want this to allow me to load one or more images in the same input.
MODELS:
from django.db import models
from datetime import datetime
from apps.category.models import Category
from django.conf import settings
class Product(models.Model):
code = models.CharField(max_length=255, null=True)
name = models.CharField(max_length=255)
image = models.ImageField(upload_to='photos/%Y/%m/', blank = True, null=True, default='')
description = models.TextField()
caracteristicas = models.JSONField(default=dict)
price = models.DecimalField(max_digits=6, decimal_places=2)
compare_price = models.DecimalField(max_digits=6, decimal_places=2)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
quantity = models.IntegerField(default=0)
sold = models.IntegerField(default=0)
date_created = models.DateTimeField(default=datetime.now)
def __str__(self):
return self.name
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name = 'images')
image = models.ImageField(upload_to='photos/%Y/%m/', default="", null=True, blank=True)
SERIALIZER:
from rest_framework import serializers
from .models import Product, ProductImage
class ProductImageSerializer(serializers.ModelSerializer):
class Meta:
model = ProductImage
fields = ["id", "product", "image"]
class ProductSerializer(serializers.ModelSerializer):
images = ProductImageSerializer(many=True, read_only=True)
uploaded_images = serializers.ListField(
child = serializers.ImageField(max_length = 1000000, allow_empty_file = False, use_url = False),
write_only=True
)
class Meta:
model = Product
fields = [
'id',
'code',
'name',
'description',
'caracteristicas',
'price',
'compare_price',
'category',
'quantity',
'sold',
'date_created',
'images',
'uploaded_images'
]
def create(self, validated_data):
uploaded_images = validated_data.pop("uploaded_images")
product = Product.objects.create(**validated_data)
for image in uploaded_images:
newproduct_image = ProductImage.objects.create(product=product, image=image)
return product
I would simply like how to make the following input field allow me to load more than one image:
Imagen de referencia input
thank you very much
You didn't post your admin.py but my guess is that you also need to register your ProductImage model as an inlines since you already use a One2Many relationship between Product and ProductImage:
In your admin.py:
class ProductImageAdmin(admin.StackedInline):
model = ProductImage
class ProductAdmin(admin.ModelAdmin):
inlines = [ProductImageAdmin]
class Meta:
model = Product
admin.site.register(ProductImage)
admin.site.register(Product, ProductAdmin)
You can also check this SO answer out for more details.
Hope that helps :)

DRF post to model with a many-to-many field

I have the following models:
class Tag(TimeStampModel):
name = models.CharField(unique=True, max_length=100)
slug = models.SlugField(max_length=100, unique=True, blank=True)
featured = models.BooleanField(default=False, blank=True)
class Deal(VoteModel, models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='deals',
on_delete=models.CASCADE)
title = models.CharField(max_length=1024, blank=False, null=False)
slug = models.SlugField(max_length=1024, unique=True, blank=True)
description = models.TextField(blank=False, null=False)
poster = models.ImageField(blank=True)
tags = models.ManyToManyField(
Tag, blank=True)
And the following serializers:
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ['id', 'name', 'slug', 'featured', 'created_at']
class DealSerializer(serializers.ModelSerializer):
user = UserSerializer(many=False, read_only=True)
tags = TagSerializer(many=True, read_only=True)
tags_ids = serializers.PrimaryKeyRelatedField(many=True, write_only=True, queryset=Tag.objects.all())
class Meta:
model = Deal
fields = '__all__'
Views
class DealList(viewsets.ModelViewSet, VoteMixin):
serializer_class = DealSerializer
permission_classes = [IsOwnerOrAdminOrReadOnly]
def get_queryset(self):
return Deal.objects.all()
def perform_create(self, serializer):
serializer.save(user=self.request.user)
I am able to get the data and also post it, but because of the many-to-many field (tags), I seem to have some issues as a Deal may have tags that only exist (created beforehand, and cannot be created through a post request to the Deal).
I send data as the following:
{
title: 'some title',
all_other_fields: 'some data',
tags_ids: [2, 4]
}
The tags are sent as an array of tag ids, but I get an error as the following:
"Incorrect type. Expected pk value, received str."
I only added the tags_ids so I could perform write operations on it as I couldn't figure out a way to use the field tags as both a read and write field that would return an object on read, and accept an id on write.
I have read through many posts here on Stackoverflow, but nothing that would work for me yet. Any help would be very appreciated. Thank you.
Try changing your serializer like this
class DealSerializer(serializers.ModelSerializer):
user = UserSerializer(many=False, read_only=True)
tags = serializers.PrimaryKeyRelatedField(many=True, queryset=Tag.objects.all())
class Meta:
model = Deal
fields = '__all__'
Try doing it this way.
class DealSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
tags = TagSerializer(read_only=True)
class Meta:
model = Deal
fields = ('user', 'title', 'slug', 'description', 'poster', 'tags')

Django admin export issue

admin.site.register(RegistrationOTP)
class RegistrationOTPResource(resources.ModelResource):
class Meta:
model = RegistrationOTP
export_order = ('country','phone_number','otp','created_date','is_verified')
class RegistrationOTPAdmin(ImportExportModelAdmin):
resource_class = RegistrationOTPResource
here is models.py
class RegistrationOTP(models.Model):
country = models.ForeignKey(Country,on_delete=models.CASCADE, related_name='otp_country', default=1)
phone_number = models.CharField(max_length=13)#, unique=True, validators=[phone_regex],
otp = models.PositiveIntegerField()
created_date = models.DateTimeField(auto_now=True)
is_verified = models.CharField(choices=YES_OR_NO, default='no', max_length=10)
def __unicode__(self):
return '%s : %s'% (self.phone_number, self.otp)
Here i am doing as per documentation but it is not showing export csv option in admin
class RegistrationOTPResource(resources.ModelResource):
class Meta:
model = RegistrationOTP
fields = '__all__'
export_order = ('id','country','phone_number','otp','created_date','is_verified')
try this, i guess due to missing fields section, it is not working

Django REST Framework many to many create and update

models.py:
class Book(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=500)
image = models.ImageField(height_field="height_field", width_field="width_field")
height_field = models.IntegerField(default=255)
width_field = models.IntegerField(default=255)
price = models.FloatField()
edition = models.CharField(max_length=100)
no_of_page = models.IntegerField()
country = models.CharField(max_length=50)
publication = models.ForeignKey(Publication, on_delete=models.CASCADE)
authors = models.ManyToManyField(Author, through='AuthorBook')
ratings = GenericRelation(Rating, related_query_name='books')
class AuthorBook(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
class Author(models.Model):
name = models.CharField(max_length=100)
biography = models.TextField(max_length=500)
image = models.ImageField(height_field="height_field", width_field="width_field")
height_field = models.IntegerField(default=255)
width_field = models.IntegerField(default=255)
serializers.py
class AuthorListSerializer(ModelSerializer):
url = author_detail_url
class Meta:
model = Author
fields = [
'url',
'id',
'name',
]
class BookCreateUpdateSerializer(ModelSerializer):
authors = AuthorListSerializer(many=True, read_only=True)
def create(self, validated_data):
#code
def update(self, instance, validated_data):
#code
class Meta:
model = Book
fields = [
'name',
'description',
'price',
'edition',
'no_of_page',
'country',
'publication',
'authors',
]
views.py
class BookCreateAPIView(CreateAPIView):
queryset = Book.objects.all()
serializer_class = BookCreateUpdateSerializer
I am working for implementing Django REST Framework. I have two models Book and Author. But django create api view don't show authors field drop down. I checked many solution DRF for many to many field. Please help me to show authors field and write create() and update function. If you see DRF api page, it will be clear.
This is because of read_only=True. Try to remove this argument for bookserializer's authors field:
class BookCreateUpdateSerializer(ModelSerializer):
authors = AuthorListSerializer(many=True)

How to serialize a one to many relation in django-rest using Model serializer?

These are my models and serializers. I want a representation of Question Model along with a list of people the question was asked to.
I am trying this:
#api_view(['GET', 'PATCH'])
def questions_by_id(request,user,pk):
question = Question.objects.get(pk=pk)
if request.method == 'GET':
serializer = QuestionSerializer(question)
return Response(serializer.data)
But I get an empty dictionary ({}). However when I remove the asked field from QuestionSerializer I get a complete representation of Question along with Places serialized nicely. What am I missing ?
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'answered')
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
places = PlaceSerializer(many=True, required=False)
asked = AskedToSerializer(source='askedto_set', many=True)
fields = ('id', 'created_on', 'title', 'places', 'answered','asked')
extra_kwargs = {'created_by': {'read_only': True}}
class Question(BaseModel):
title = models.CharField(max_length=200, null=False)
places = models.ManyToManyField(Place, blank=True)
answered = models.BooleanField(default=False)
class AskedTo(BaseModel):
ques = models.ForeignKey(Question, on_delete=models.CASCADE)
to_user = models.ForeignKey(settings.AUTH_USER_MODEL)
replied = models.BooleanField(default=False)
class Place(models.Model):
g_place_id = models.CharField(max_length=20,primary_key=True)
json = models.TextField(null=True)
name = models.CharField(max_length=40)
I figured it out. There were two errors.
Changed this:
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'answered')
to this (notice the change in fields, fields on model and serializer didn't match)
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'replied')
Secondly, I needed to define any extra fields outside class Meta
class QuestionSerializer(serializers.ModelSerializer):
places = PlaceSerializer(many=True, required=False)
asked = AskedToSerializer(source='askedto_set', many=True)
class Meta:
model = Question
fields = ('id', 'created_on', 'title', 'places', 'answered','asked')
extra_kwargs = {'created_by': {'read_only': True}}
Notice the change in definition of places and asked.
In my case, I have this models.py:
class Section(models.Model):
title = models.CharField(max_length=500)
description = models.TextField(blank=True)
user = models.ForeignKey(Profile, related_name="sections", on_delete=models.CASCADE)
class Feed(models.Model):
title = models.CharField(max_length=500)
link_rss = models.URLField(max_length=500)
link_web = models.URLField(max_length=500)
description = models.TextField(blank=True)
language = models.CharField(max_length=50, blank=True)
logo = models.URLField(blank=True)
sections = models.ManyToManyField(Section, related_name="feeds")
And I completed the serializers.py this:
class FeedSerializer(serializers.ModelSerializer):
sections = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Feed
fields = '__all__'
class SectionSerializer(serializers.ModelSerializer):
feeds = FeedSerializer(many=True, read_only=True)
class Meta:
model = Section
exclude = ('user',)

Categories