Django - expected type pk, received str [Many to many]

Viewed 145

I have a webapp where we can create communities with django as a backend, but when i try to send a POST to create a community, I get:

community_configuration: ["Incorrect type. Expected pk value, received str."]

My POST:

{
title: knkn kn .k jbjnmn,
logo: [object File],
is_active: true,
description: test,
welcome_message: Welcome message,
org_id: 114,
community_configuration: About us,Community news,FAQs,Supporters,Resources,
}

Here are my serializers:

class MicroConfigurationSerializer(serializers.ModelSerializer):

    class Meta:
        model = MicroConfiguration
        fields = [
            'name',
        ]

    def to_representation(self, instance):
        return instance.name


class CommunityConfigurationSerializer(serializers.ModelSerializer):
    micro_configurations = MicroConfigurationSerializer(many=True, read_only=True)
    
    class Meta:
        model = CommunityConfiguration
        fields = '__all__'


class CommunitySerializer(serializers.ModelSerializer):
    logo = serializers.SerializerMethodField()
    organisation = serializers.SerializerMethodField()
    community_configuration = CommunityConfigurationSerializer()

    class Meta:
        model = Community
        fields = (
            ...
            etcetc
            ...
        )

Heres my model:

class MicroConfiguration(core_models.BaseModel):
    name = models.CharField(max_length=32, unique=True)
    permissions = models.ManyToManyField(Permission)

    def __str__(self):
        return self.name


class CommunityConfiguration(core_models.BaseModel):
    name = models.CharField(max_length=32)
    micro_configurations = models.ManyToManyField(MicroConfiguration)
    permission_group = models.ForeignKey(
        Group,
        on_delete=models.SET_NULL,
        null=True
    )

    def __str__(self):
        return self.name


class Community(core_models.BaseModel):
    accounts = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        through='associates.AccountCommunity',
        through_fields=('community', 'account')
    )
    goals = models.ManyToManyField(Goal, through='associates.CommunityGoal')
    type = models.ManyToManyField(CommunityType, through='CommunityAttribute')
    communities = models.ManyToManyField(
        'self',
        through='associates.CommunityCommunity',
        symmetrical=False,
    )
    crossposts = models.ManyToManyField(
        Action,
        through='activities.CommunityCrosspost',
        through_fields=('community', 'action'),
        related_name='communities',
    )

    title = models.CharField(max_length=64)
    logo = models.ImageField(null=True, blank=True)
    intro_media = models.ForeignKey(
        'associates.MediaStore',
        null=True,
        on_delete=models.SET_NULL
    )
    website_url = models.CharField(max_length=256, blank=True)
    is_invite_only = models.BooleanField(default=False)
    description = models.TextField(blank=True)
    intro_message = models.TextField(blank=True)
    welcome_message = models.TextField(blank=True)
    signup_autojoin = models.BooleanField(default=False)

    community_configuration = models.ForeignKey(
        CommunityConfiguration,
        default=1,
        null=True,
        on_delete=models.SET_NULL,
        help_text='Do not edit directly, create a new custom config instead, \
            as it is reference by difference community.',
    )

    objects = models.Manager()
    associates = AssociationManager()

    @property
    def url(self):
        return reverse(
            'communities:detail',
            kwargs={
                'id': self.id,
            }
        )

    class Meta:
        verbose_name = 'Community'
        verbose_name_plural = 'Communities'

    def __str__(self):
        return self.title

Here's my views:

class CommunityCreateAPIView(CreateAPIView):
    serializer_class = CommunityCreateSerializer

    def create(self, request, **kwargs):
        organisation_id = request.data.get('org_id')
        micro_configurations_qd = request.data.copy()
        micro_configurations = micro_configurations_qd.pop('community_configuration', False)

        if micro_configurations:
            micro_configurations = micro_configurations[0].split(',')

        user = request.user
        data = {}

        permitted = AccountOrganisation.objects.filter(
            account=user,
            organisation_id=organisation_id,
            is_active=True,
            association__permissions__codename='add_community',
        ).exists()

        if not permitted and not request.user.is_superuser:
            data['message'] = 'Action not allowed'

            return Response(
                data=data,
                status=status.HTTP_400_BAD_REQUEST,
            )

        response = super().create(request, **kwargs)

        if response.status_code == 400:
            data['message'] = 'Failed to update community'

            return Response(
                data=data,
                status=status.HTTP_400_BAD_REQUEST,
            )

        community_id = response.data.get('id')
        OrganisationCommunity.objects.create(
            community_id=community_id,
            organisation_id=organisation_id,
        )
        association = Group.objects.get(name='Community Admin')
        AccountCommunity.objects.create(
            community_id=community_id,
            account=user,
            association=association,
        )

        if micro_configurations:
            com_config_qs = CommunityConfiguration.objects.filter(
                micro_configurations__name__in=micro_configurations
            ).annotate(
                num_micro_config=Count('micro_configurations__name')
            ).filter(
                num_micro_config=len(micro_configurations)
            )

        community_configuration = None

        if micro_configurations:
            for com_config in com_config_qs:
                micro_config_count = com_config.micro_configurations.count()

                if micro_config_count == len(micro_configurations):
                    community_configuration = com_config

                    break

        if community_configuration:
            Community.objects.filter(
                pk=community_id
            ).update(
                community_configuration=community_configuration
            )
        elif micro_configurations:
            micro_qs = MicroConfiguration.objects.filter(
                name__in=micro_configurations
            )
            community_config = CommunityConfiguration.objects.create(
                name='Custom'
            )
            community_config.micro_configurations.set(micro_qs)
            community_config.save()
            Community.objects.filter(
                pk=community_id
            ).update(
                community_configuration=community_config
            )

        return response

I've been stuck with this for hours, any help?

1 Answers

Your serializer expects a pk (or id) of the community_configuration instance.

basically you have it set up so that you need to create a community_configuration entry first, then fetch the id of the new created entry and use that id when creating your community.

If you want to have the community_configuration instance to be created while creating the community then an option to do that would be to override the create method in the serializer and extract the community_configuration data then create a new entry for that model and use it, something like this

class CommunitySerializer(serializers.ModelSerializer):
    # .. put your serializer code here
    def create(self, validated_data):
        community_configuration= validated_data.pop('community_configuration')
        config_instance, created = CommunityConfiguration.objects.get_or_create(<community_configuration>) # modify this however you need to create the CommunityConfiguration instance
        community_instance = community.objects.create(**validated_data, community_configuration=config_instance)
        return community_instance

you will need to probably modify the code for it to follow your needs.
I didn't read all of your models, not sure how you are gonna create the nested models from the input value but you get the point.

Related