0

I have a Club Model as well as an Address model.

class Club(models.Model):
    name = models.CharField(max_length=100)
    owner = models.ForeignKey(UserAccount,on_delete=models.CASCADE,related_name = 'owner_of')
    ##members = models.ManyToManyField(UserAccount,related_name = 'member_of')
    ##staff = models.ManyToManyField(UserAccount,related_name = 'staff_of')
    def __str__(self):
        return f'{self.name}'

class Address(models.Model):
    name = models.CharField(max_length=100)
    street = models.CharField(max_length=150)
    city = models.CharField(max_length=100)
    state = models.CharField(max_length=100, blank=True, null=True)  # Optional for countries without states
    province = models.CharField(max_length=100, blank=True, null=True)  # Optional for countries without provinces
    country = models.CharField(max_length=50)
    postal_code = models.CharField(max_length=20)

    club = models.ForeignKey(Club,on_delete=models.CASCADE,related_name = 'address')
    def __str__(self):
        return f'{self.name}'   

They have their respected serializers.

 class AddressSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Address
            fields = ['id', 'name', 'street', 'city', 'state', 'province', 'postal_code', 'country']
    
        def create(self,validated_data):
            user=self.context['user']
            club=Club.objects.get(owner=user)
            address=Address.objects.create(club=club,**validated_data)
            return address
    
    class ClubSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Club
            fields = ['id', 'name']
    
        def create(self,validated_data):
            user=self.context['user']
            club=Club.objects.create(owner=user,**validated_data)
            return club
    
    
    class ClubRegistrationSerializer(serializers.Serializer):
    
    
       
        address= AddressSerializer2()
        club=ClubSerializer2()
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            # We pass the "upper serializer" context to the "nested one"
            self.fields['address'].context.update(self.context)
            self.fields['club'].context.update(self.context)

this is the data that I am posting

{
 "club":{"name":"someclub"},
 "address":{
   "name":"Bodrum Location",
   "street":"140",
    "city":"bodrum",
    "state":"california",
    "province":"some provence",
    "postal_code":"123",
    "country":"USA"
    }
}

I can easily implement the create method of my view and

do this:

@action(detail=False,methods=['post'])
def register(self, request):
    serializer = ClubRegistrationSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    club_data=serializer.validated_data.pop("club")
    address_data=serializer.validated_data.pop("address")
    club=Club.objects.create(owner=self.request.user,**club_data) 
    address=Address.objects.create(club=club,**address_data)
    response={}
    response['address']=AddressSerializer(address).data
    response['club']=ClubSerializer(club).data
    return Response(response, status=status.HTTP_201_CREATED)
  1. But from what I read it is not ideal to do the logic in views but in serializers. So I can move the create method from my view's create to serializer's create. But what does not make sense to me is that I can not call the create method of the nested serializers. Actually I could not even get the nested serializer objects so I can call its create method . You can think of this as if you are traversing a tree and calling create methods of each serializer and if they have create they call its child.

Ideally I should not implement all the logic in ClubRegistrationSerializer's create method. each serializer should be responsible for its own.

I should be able to call the create method of ClubRegistrationSerializer and that should call the create method of Address and Club's serializers.

Every example I see handles all the logic in the parent serializer's create.

  1. Secondly how can I call the ClubRegistrationSerializer with the address obj and club obj and get the data to send it to the user so that reponse would have the IDs.

I have tried ClubRegistrationSerializer(address=addressOBJ,club=clubOBJ) like so but this is throwing an exception. Currently I am creating a dict and attaching the model's values.

This is the response I would like to get from the serializer.

{"club":{"name":"someclub","id":"1"},
 "address":{
   "id":"1",
   "name":"Some Location",
   "street":"140",
    "city":"San Fran",
    "state":"california",
    "province":"some province",
    "postal_code":"123",
    "country":"USA"
    }
}

1 Answer 1

0

You need to think about implementation like the relations net.

First of all you can refactor club = models.ForeignKey field to OneToOne relation field. Then you can get rid of that main dict wrapper in the payload and implement club fields flat and address field as dict.

The serializer .create method only creates new instances by Model.objects.create method under the hood so you don't need really to call that serializers.create method. The field validation is the most important in serializers - they are dedicated to do that staff (logic in professional code should be delegated to services.py file at least). But let's stay with logic in the serializer for simplicity.

class ClubSerializer(serializers.ModelSerializer):
    address = AddressSerializer()

    class Meta:
        model = Club
        fields = ['id', 'name', 'address']

    def create(self, validated_data):
        address_data = validated_data.pop("address")
        instance = super().create(validated_data)
        instance.address = Address.objects.create(club=instance, **address_data)
        return instance

View action

    @action(detail=False,methods=['post'])
    def register(self, request):
        serializer = ClubSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)
Sign up to request clarification or add additional context in comments.

3 Comments

But this is only creating a club. I
Did you even test that ?
Yes My address is not part of my club. So yes you are reading the data right by poping adddress but you are creating an address and attaching that to club which is not what I want to have. Because club has not only one address but can have many locations. So club is not a child of club. request comes in as {club:{blah:blah},address:{blah,blah} and club and address is at same level. which you handle right but response also need to be same level. {club:{id:1,blah:blah},address:{id:1.blah,blah}. Like so and this response does not reflect a model in my example so my request is

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.