1

I've got a form up and working with a Vue frontend and DRF backend. It's a form for adding (creating) a new model - and has a dropdown of the related models that are FK to the model being created.

I need to access attributes of the selected FK item.

My serializers look like this:

class SubdomainSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subdomain
        fields = [
            "id",
            "domain",
            "short_description",
            "long_description",
            "character_code",
        ]

    # def get_absolute_url(self, obj):
    #     return obj.get_absolute_url()


class EvidenceSerializer(serializers.ModelSerializer):
    created_by = serializers.HiddenField(
        default=serializers.CurrentUserDefault()
    )
    updated_by = serializers.HiddenField(
        default=serializers.CurrentUserDefault()
    )
    absolute_url = serializers.SerializerMethodField()
    created_by_name = serializers.SerializerMethodField()
    updated_by_name = serializers.SerializerMethodField()

    class Meta:
        model = Evidence
        fields = "__all__"

The form is to create a new 'Evidence' item, and the 'Subdomain' is a dropdown on the form that contains all related subdomains.

The models look like this:

class Subdomain(CreateUpdateMixin):
    domain = models.ForeignKey(Domain, on_delete=models.PROTECT)
    short_description = models.CharField(max_length=100)
    long_description = models.CharField(max_length=250)
    character_code = models.CharField(max_length=5)

class Evidence(CreateUpdateMixin, CreateUpdateUserMixin, SoftDeletionModel):
    subdomain = models.ForeignKey(Subdomain, on_delete=models.PROTECT)
    evaluation = models.ForeignKey(
        Evaluation, related_name="evidences", on_delete=models.PROTECT
    )
    published = models.BooleanField(default=False)
    comments = models.CharField(max_length=500)

In my form, I just want to include the short_description of each subdomain when the user chooses it from the dropdown - I may also want to use the long_description as well.

Here is the bit in the form where I render the dropdown:

                        <div class="form-group col-sm-4">
                            <label class="" for="subdomain">Subdomain</label>
                            <select name="subdomain" id="subdomain" class="form-control" v-model="element.subdomain">
                                <option v-for="choice in subdomains" :value="choice.id" >{{ choice.character_code }}</option>
                            </select>
                        </div>
                        <div class="small" v-if="element.subdomain">
                            <!-- THIS IS WHERE I WOULD LIKE TO DISPLAY THE SHORT DESCRIPTION FOR THE CHOICE IN THE DROPDOWN -->
                            {{ choice.short_description }}
                        </div>

The Form Data looks like this when I POST:

evaluation: 2037
subdomain: 448
comments: Test comments to add to the subdomain
published: true
csrfmiddlewaretoken: 382796ryfuasiodfgyhakljyht37yaisdfaslk3r

Things I have tried - some of which worked for display purposes but seem to have broken the form/POST:

  • Adding depth=1 to the Meta of the EvidenceSerializer, which worked but made the form no longer submit appropriately. I think it's because it wanted the entire subdomain instead of just the ID? I couldn't get it working - the subdomain always threw an error.

  • Adding the following to my EvidenceSerializer, which again seemed to break the POST operation, it would cause the subdomain dropdown to throw an error.

subdomain = SubdomainSerializer(read_only=True)

Using both of those methods above the dropdown doesn't recognize the subdomain_id being selected and both end up throwing this error behind the scenes:

Cannot insert the value NULL into column 'subdomain_id', table 'local_app.dbo.myapp_evidence'; column does not allow nulls. INSERT fails.

Any advice on how to proceed would be fantastic.

TLDR; Need to be able to access attributes on a FK relationship for a dropdown using DRF, and be able to submit that item in a form.

Hanny
  • 580
  • 3
  • 16
  • 44

1 Answers1

0

Thanks to @bdbd for pointing me in the right direction.

For anyone curious, I resolved it using those links - turns out I needed to change my serializers a little bit:

class SubdomainSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subdomain
        fields = [
            "id",
            "domain",
            "short_description",
            "long_description",
            "character_code",
        ]


class EvidenceSerializer(serializers.ModelSerializer):
    created_by = serializers.HiddenField(
        default=serializers.CurrentUserDefault()
    )
    updated_by = serializers.HiddenField(
        default=serializers.CurrentUserDefault()
    )
    absolute_url = serializers.SerializerMethodField()
    created_by_name = serializers.SerializerMethodField()
    updated_by_name = serializers.SerializerMethodField()
    # add the 'subdomain' as read only - but with all the attributes
    subdomain = SubdomainSerializer(read_only=True)
    # add the 'subdomain_id' as a PrimaryKeyRelatedField with the source being the subdomain
    subdomain_id = serializers.PrimaryKeyRelatedField(
        queryset=Subdomain.objects.all(), source="subdomain"
    )

    class Meta:
        model = Evidence
        fields = "__all__"

Then I updated the HTML a little bit:

                <div class="form-group col-sm-4">
                    <label class="" for="subdomain_id">Subdomain</label>
                    <select name="subdomain_id" id="subdomain" class="form-control" v-model="element.subdomain">
                        <option v-for="choice in subdomains" :value="choice" >{{ choice.character_code }}</option>
                    </select>
                </div>
                <div class="small" v-if="element.subdomain_id">
                    {{ element.subdomain.short_description }}
                </div>

Then in the ajax call I simply assign the subdomain_id to the subdomain.id

        data: {
            evaluation : evaluationId,
            subdomain_id : vm.element.subdomain.id,
            comments : vm.element.comments,
            published: vm.element.published,
            csrfmiddlewaretoken: vm.sharedStore.state.csrftoken,
        },
Hanny
  • 580
  • 3
  • 16
  • 44