How to update nested serializers with file field - python

I have 2 models: Profile and Image. Profile field "logo" related to Image with models.ForeignKey() construction. I want to update my Profile record with update request (Patch with JSON payload).
How can I do that?
I've tried to send this JSON
{
"name": "TestName",
"company": "myCompany",
"phone": "33222111",
"website": "site.com"
}
And it's OK, record have updated. But! In Image model I have models.ImageField(). How I should deal with this field through another serializer?
Then I've tried to send this JSON (122 id of existed Image record in DB)
REQUEST:
{
"logo": 122
}
ANSWER:
{
"logo": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
}
}
OK, so, think I should send object of exist record
REQUEST:
{
"logo": {
"id": 122,
"uuid": "bf9ba033-208f-47e0-86e5-93c44e05a616",
"created": "2018-12-20T12:54:57.178910Z",
"original_name": "hello.png",
"filetype": "png",
"file": "http://localhost/upload/img/0a9lg1apnebb.png",
"owner": 1
}
}
ANSWER:
{
"logo": {
"file": [
"The submitted data was not a file. Check the encoding type on the form."
]
}
}
Here my two models and serializers
class Image(models.Model):
id = models.AutoField(primary_key=True)
uuid = models.UUIDField(primary_key=False, default=uuid.uuid4, editable=False)
created = models.DateTimeField(auto_now_add = True)
original_name = models.CharField(max_length = 256, default=None)
filetype = models.CharField(max_length = 10, default=None)
file = models.ImageField(upload_to=update_img_filename,
default="static/noimg.jpg")
owner = models.ForeignKey(User, related_name="images",
on_delete=models.CASCADE, null=True)
class Profile(models.Model):
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
name = models.CharField(max_length=256, blank=True, null=True)
company = models.CharField(max_length=256, blank=True, null=True)
phone = models.CharField(max_length=256, blank=True, null=True)
website = models.CharField(max_length=256, blank=True, null=True)
logo = models.ForeignKey(Image, related_name="profile_logo",
on_delete=models.SET_NULL, null=True,
blank=True, default=None)
is_admin = models.BooleanField(default=False)
owner = models.ForeignKey(User, related_name="profiles",
on_delete=models.CASCADE, null=True)
class ProfileSerializer(serializers.ModelSerializer):
logo = ImageSerializer()
class Meta:
model = Profile
fields = '__all__'
extra_kwargs = {
'owner': {'read_only': True},
'user': {'read_only': True},
'is_admin': {'read_only': True},
}
def create(self, validated_data):
return Profile.objects.create(**validated_data)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = '__all__'
extra_kwargs = {
'owner': {'read_only': True}
}
def validate(self, data):
extension = str(data['file']).split(".")[-1].lower()
original_name = str(data['file'])
if 'original_name' not in data:
data['original_name'] = original_name
if 'filetype' not in data:
data['filetype'] = extension
return data
def create(self, validated_data):
return Image.objects.create(**validated_data)

in database, model Profile will have logo_id mapping with model Image.
Try json like this if you want to have relationship with existed logo:
{
"name": "TestName",
"company": "myCompany",
"phone": "33222111",
"website": "site.com",
"logo: 122
}
Serializer:
class ProfileSerializer(serializers.ModelSerializer):
logo = ImageSerializer()
class Meta:
model = Profile
fields = '__all__'
extra_kwargs = {
'owner': {'read_only': True},
'user': {'read_only': True},
'is_admin': {'read_only': True},
}
def create(self, validated_data):
logo_id = validated_data.pop('logo')
profile = Profile.objects.create(**validated_data)
logo_instance = Image.objects.get(pk=logo_id)
profile.logo = logo_instance
profile.save()
def update(self, instance, validated_data):
nested_serializer = self.fields['logo']
nested_instance = instance.profile
nested_data = validated_data.pop('logo')
nested_serializer.update(nested_instance, nested_data)
return super(ProfileSerializer, self).update(instance, validated_data)

if you need set the image only from the existing images, you can do that:
class ProfileSerializer(serializers.ModelSerializer):
logo = ImageSerializer()
class Meta:
model = Profile
fields = '__all__'
extra_kwargs = {
'owner': {'read_only': True},
'user': {'read_only': True},
'is_admin': {'read_only': True},
}
class ImageSerializer(serializers.ModelSerializer):
def to_internal_value(self, data):
return Image.objects.get(pk=data)
class Meta:
model = Image
fields = '__all__'
extra_kwargs = {
'owner': {'read_only': True}
}
...
Then you will can send json:
{
"name": "TestName",
"company": "myCompany",
"phone": "33222111",
"website": "site.com"
"logo": 122
}
And image will be set.

Related

Correct way to get/create nested objects with DRF?

I have the following models:
class Platform(models.Model): # Get
key = models.CharField(max_length=32, unique=True)
class PlatformVersion(models.Model): # Get or create
platform = models.ForeignKey(Platform, on_delete=models.CASCADE)
major = models.IntegerField(db_index=True)
minor = models.IntegerField(db_index=True)
build = models.IntegerField(db_index=True)
class DeviceManufacturer(models.Model): # Get or create
name = models.CharField(max_length=64, unique=True)
class DeviceModel(models.Model): # Get or create
manufacturer = models.ForeignKey(DeviceManufacturer, on_delete=models.CASCADE)
platform = models.ForeignKey(Platform, on_delete=models.CASCADE)
# ...
# And many others
POST JSON looks like this:
{
"device_model": {
"manufacturer": {
"name": "samsung"
},
"platform": {
"key": "android"
}
},
"version": {
"platform": {
"key": "android"
},
"major": 8,
"minor": 1,
"build": 0
}
}
I have the following serializers:
class PlatformSerializer(serializers.ModelSerializer):
class Meta:
model = Platform
fields = ('name', 'key',)
extra_kwargs = {
'name': {
'read_only': True,
},
}
def create(self, validated_data):
return Platform.objects.get(key=validated_data['key'])
class PlatformVersionSerializer(serializers.ModelSerializer):
platform = PlatformSerializer()
class Meta:
model = PlatformVersion
fields = ('platform', 'major', 'minor', 'build',)
def create(self, validated_data):
# ?
pass
class DeviceManufacturerSerializer(serializers.ModelSerializer):
class Meta:
model = DeviceManufacturer
fields = ('name',)
def create(self, validated_data):
manufacturer, _ = DeviceManufacturer.objects.get_or_create(
defaults={
'name': validated_data['name'],
},
name=validated_data['name']
)
return manufacturer
class DeviceModelSerializer(serializers.ModelSerializer):
manufacturer = DeviceManufacturerSerializer()
platform = PlatformSerializer()
class Meta:
model = DeviceModel
fields = ('manufacturer', 'platform')
def create(self, validated_data):
# ?
pass
I've marked in serializers places where I don't know how to correctly do the object creation, as some of nested objects are «read-only» (I mean that I in fact use get, not create), some of them are get or create.
How should I do it?

How to set nested related fields serializers in Djando Rest Framework?

I'm using Django Rest Framework to build an API where I have the following models of Users making, confirming and showing interest on Events:
models.py
class User(AbstractBaseUser, PermissionsMixin):
user_name = models.CharField(_("user name"), max_length=150, unique=True)
slug = AutoSlugField(populate_from='user_name', unique=True)
class Event(models.Model):
name = models.CharField(max_length=100, blank=False, null=False)
owner = models.ForeignKey(User, related_name="owned_events", on_delete=models.SET_NULL, blank=True, null=True)
confirmed = models.ManyToManyField(User, related_name="confirmed_events", blank=True)
interested = models.ManyToManyField(User, related_name="interested_events", blank=True)
to serialize it I used the following code as I found here and at the DRF docs:
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = [
"url",
"user_name",
"password",
]
extra_kwargs = { "password": {"write_only": True} }
class EventSerializer(serializers.HyperlinkedModelSerializer):
owner = UserSerializer(required=False)
confirmed = UserSerializer(required=False, many=True)
interested = UserSerializer(required=False, many=True)
class Meta:
model = Event
lookup_field = 'slug'
extra_kwargs = { 'url': {'lookup_field': 'slug'} }
fields = [
"url",
"owner",
"name",
"confirmed",
"interested",
]
It works just fine like that, but I wanted the UserSerializer to show confirmed and interested events of each user just like each event shows the users confirmed and interested. I changed serializers and got the url of each event, like that:
serializers.py with HyperlinkedRelatedField on UserSerializer
class UserSerializer(serializers.HyperlinkedModelSerializer):
confirmed_events = serializers.HyperlinkedRelatedField(
queryset=Event.objects.all(),
view_name='event-detail',
lookup_field='slug',
many=True,
required=False
)
interested_events = serializers.HyperlinkedRelatedField(
queryset=Event.objects.all(),
view_name='event-detail',
lookup_field='slug',
many=True,
required=False
)
class Meta:
model = User
fields = [
"url",
"user_name",
"password",
"confirmed_events",
"interested_events",
]
extra_kwargs = { "password": {"write_only": True} }
This got me the following JSON from the User model:
{
"user_name": "d",
"confirmed_events": [],
"interested_events": [
"http://localhost:8000/events/eqwer-2/",
"http://localhost:8000/events/test/",
"http://localhost:8000/events/test-2/",
"http://localhost:8000/events/test-3/",
]
}
And from the Event model:
{
"url": "http://localhost:8000/events/eqwer-2/",
"owner": null,
"name": "eqwer",
"slug": "eqwer-2",
"confirmed": [],
"interested": [
{
"user_name": "d",
"confirmed_events": [],
"interested_events": [
"http://localhost:8000/events/eqwer-2/",
"http://localhost:8000/events/test/",
"http://localhost:8000/events/test-2/",
"http://localhost:8000/events/test-3/",
]
}
]
},
The Event model JSON is fine because it shows each user's data, but I wanted the User JSON to show each event data instead of just the event URL, it'd be something like:
{
"user_name": "d",
"confirmed_events": [],
"interested_events": [
{
"url": "http://localhost:8000/events/eqwer-2/",
"owner": null,
"name": "eqwer",
"slug": "eqwer-2",
},
]
}
Create a separate serializer for interested_events like so:
class InterestedEventsSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
fields = ('url', 'owner', 'name', 'slug')
And in your UserSerializer declare the interested_events using the serializer above:
class UserSerializer(serializers.HyperlinkedModelSerializer):
confirmed_events = ... #
interested_events = InterestedEventsSerializer(many=True)
class Meta:
model = User
fields = [
"url",
"user_name",
"password",
"confirmed_events",
"interested_events",
]
extra_kwargs = { "password": {"write_only": True} }

Unable to populate images in Django Rest Serializer

I'm developing a REST API using Django Rest Framework but I'm unable to populate image in Feed Serializer
Django Version: 3.1.7
Python Version: 3.9.2
Models:
class User(AbstractUser):
age = models.PositiveIntegerField(null=True)
address = models.TextField(null=True)
email = models.EmailField(_('email address'), unique=True, null=False)
first_name = models.CharField(_('first name'), max_length=150, blank=False, null=False)
last_name = models.CharField(_('last name'), max_length=150, blank=False, null=False)
image = models.ImageField(upload_to='storage', null=True)
class Feed(models.Model):
description = models.TextField()
likes_count = models.PositiveIntegerField(default=0, null=True)
comments_count = models.PositiveIntegerField(default=0, null=True)
updated_at = models.DateTimeField(auto_now=True)
created_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user')
tags = models.ManyToManyField(User, related_name='tags', blank=True)
class FeedImage(models.Model):
path = models.ImageField(upload_to='storage', null=False)
post = models.ForeignKey(Feed, on_delete=models.CASCADE, null=False, default='')
Serializers:
class FeedUserSerializer(ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'image')
class FeedImageSerializer(serializers.ModelSerializer):
class Meta:
model = FeedImage
fields = ('id', 'path', 'post')
class FeedSerializer(ModelSerializer):
user = FeedUserSerializer()
images = FeedImageSerializer(many=True, read_only=True)
class Meta:
model = Feed
fields = ('id', 'description', 'comments_count', 'likes_count', 'updated_at', 'created_at',
'tags', 'images', 'user')
View:
class FeedsListView(generics.ListAPIView):
queryset = Feed.objects.all()
return FeedSerializer
Problem: I get this result without images
[{
"id": 1,
"description": "Hello world",
"comments_count": 0,
"likes_count": 0,
"updated_at": "2021-04-26T03:01:44.219235Z",
"created_at": "2021-04-26T03:01:44.219235Z",
"tags": [],
"user": {
"id": 1,
"first_name": "ZAIN",
"last_name": "REHMAN",
"image": "http://192.168.88.28:8000/storage/1_Facebook_1.jpg"
}
}]
Expected Output:
[{
"id": 1,
"description": "Hello world",
"comments_count": 0,
"likes_count": 0,
"updated_at": "2021-04-26T03:01:44.219235Z",
"created_at": "2021-04-26T03:01:44.219235Z",
"tags": [],
"user": {
"id": 1,
"first_name": "abc_first",
"last_name": "cdef_last",
"image": "http://192.168.88.28:8000/storage/1_Facebook_1.jpg"
},
"images": [
{
"id": 1,
"post": "1",
"path": "imagelocation"
},
{
"id": 2,
"post": "1",
"path": "imagelocation 2"
}
]
}]
Any solution or any workaround will be appreciated.
Ignore this: Bulk text to resolve StackOverflow problem
industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
You can use SerializerMethodField for this.
class FeedSerializer(ModelSerializer):
images = serializers.SerializerMethodField()
class Meta:
model = Feed
fields = ('id', 'description', 'comments_count', 'likes_count', 'updated_at', 'created_at',
'tags', 'images', 'user')
def get_images(self, obj):
return obj.feedimage_set.all().values('id', 'path', 'post')

Django Serializer's Source argument is not working

I am trying to get all the bills and their customer-related details (i.e. 'customer_code', 'email' etc.) with it.
However, source='user.customer_code does not seem to have any effect at all. What am I missing?
I have been following along this:
this stackoverflow post with no luck.
My two models:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(db_index=True, unique=True, max_length=200)
customer_code = models.CharField(max_length=300, blank=True, null=True, default=None)
class Bill(models.Model):
customer = models.ForeignKey(
User, on_delete=models.CASCADE, blank=True, null=True, related_name="customer_bill"
)
payable_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
View:
class BillView(APIView):
def get(self, request, format=None):
q = Bill.objects.all().select_related('customer')
s = BillSerializer(q, many=True)
return JsonResponse({
"bill": s.data
})
Serializer:
class BillSerializer(serializers.ModelSerializer):
customer_code = serializers.CharField(source='user.customer_code', read_only=True)
class Meta:
model = Bill
fields = ('id','payable_amount','customer_code') # binding customer_code here
Current Output:
"bill": [
{
"id": 1,
"payable_amount": "1000.00"
},
{
"id": 2,
"payable_amount": "2000.00"
}
]
Expected Result:
"bill": [
{
"id": 1,
"payable_amount": "1000.00",
"customer_code": "CUS10001" # want this to be attached
},
{
"id": 2,
"payable_amount": "2000.00",
"customer_code": "CUS10002" # want this to be attached
}
]

Django Rest Framework foreign key nesting

I am trying to nest my Users table inside my Relationships table. So instead of this:
[
{
"user": 1,
"related_user": 2,
"relationship": "followed_by"
}
]
I am trying to get this:
[
{
"user": {
"username": "user1",
"name": "User 1",
"email": "bla",
"phone": "bla",
"date_joined": "2017-11-01T21:34:13.101256Z"
},
"related_user": {
"username": "user2",
"name": "User 2",
"email": "bla",
"phone": "bla",
"date_joined": "2017-11-01T21:34:13.101256Z"
},
"relationship": "followed_by"
}
]
I looked up tutorials and I tried adding serializers.RelatedField , UserSerializer(many=true, read-only=true) etc. but nothing worked
Models.py
class User(models.Model):
username = models.CharField(max_length=255)
name = models.CharField(max_length=255)
email = models.CharField(max_length=255)
phone = models.CharField(max_length=255)
date_joined = models.DateTimeField(auto_now_add=True, blank=True)
def __str__(self):
return str(self.pk) + ", " + self.username
RELATIONSHIP_CHOICE = [
("follows", "follows"),
("followed_by", "followed_by"),
("none", "none"),
]
class Relationship(models.Model):
user = models.ForeignKey(User, related_name="primary_user", null=True)
related_user = models.ForeignKey(User, related_name="related_user", null=True)
relationship = models.CharField(max_length=40, choices=RELATIONSHIP_CHOICE, default=RELATIONSHIP_CHOICE[0])
Serializers.py
from rest_framework import serializers
from . import models
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = (
'username',
'name',
'email',
'phone',
'date_joined',
)
class RelationshipSerializer(serializers.ModelSerializer):
related_user = UserSerializer(many=True)
class Meta:
model = models.Relationship
fields = (
'user',
'related_user',
'relationship',
'related_user'
)
I tried to add related user to my serializer but it didnt work. I am getting an error: 'User' object is not iterable
Any help is appreciated.
class RelationshipSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
related_user = UserSerializer(read_only=True)
class Meta:
model = models.Relationship
fields = (
'user',
'related_user',
'relationship'
)
user = UserSerializer(read_only=True, many=True) is for manytomany field,user = UserSerializer(read_only=True) is for ForeignKey field.

Categories