0

Preliminary note: this is a rather newbie question, though I have not found a sufficient answer on StackOverflow; many similar questions, but not this one. So I am asking a new question.

The problem: I'm having difficulty creating records where one field is a foreign key to an existing record, and I do not know what I'm doing wrong in my code.

In my app there are two models in question, a one-to-many relationship between Company and BalanceSheet:

models:

class Company(models.Model):
    cik = models.IntegerField(default=0, unique=True)
    symbol = models.CharField(max_length=4, unique=True)
    name = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return self.symbol

class BalanceSheet(models.Model):
    company = models.ForeignKey(Company,
        null=True,
        on_delete=models.CASCADE,
        related_name='balance_sheets',)
    date = models.DateField()
    profit = models.BigIntegerField()
    loss = models.BigIntegerField()

    class Meta:
        unique_together = (('company', 'date'),)

    def __str__(self):
        return '%s - %s' % (self.company, self.date)

serializers:

class BalanceSheetSerializer(serializers.ModelSerializer):
    company = serializers.StringRelatedField()

    class Meta:
        model = BalanceSheet
        fields = ('company','date','profit','loss')


class CompanySerializer(serializers.ModelSerializer):
    class Meta:
        model = Company
        fields = ('cik', 'symbol', 'name')

Views:

class BalanceSheetCreate(generics.CreateAPIView):
    model = BalanceSheet
    queryset = BalanceSheet.objects.all()
    serializer_class = BalanceSheetSerializer

urls include:

url(r'^(?P<symbol>[A-Z]{1,4})/create-balance-sheet/$', views.BalanceSheetCreate.as_view(),
        name='create_balance_sheet'),

To this point, I have zero problem reading data. However, when trying to create records, I get errors I don't understand:

curl http://localhost:8000/financials/AAPL/create-balance-sheet/ -X POST -d "company=AAPL&date=1968-04-17&profit=1&loss=1"
IntegrityError at /financials/AAPL/create-balance-sheet/
null value in column "company_id" violates not-null constraint

Dropping the company data from that curl command results in the same error.

How do I get around this error? I thought I was telling the api what company I'm interested in, both explicitly in the url and in the post data.

Using python3.6, django 1.11, and djangorestframework 3.7.7

2
  • Do you really need null=true for the foreign key? Commented Jan 21, 2018 at 1:43
  • toggling that value doesn't change any outcomes, unfortunately. Commented Jan 21, 2018 at 2:06

2 Answers 2

1

You get the IntegrityError because your code will try to create a new BalanceSheet without a company. That's because StringRelatedField is read-only (see docs) and therefore it's not parsed when BalanceSheetSerializer is used in write mode.

SlugRelatedField is what you need here:

class BalanceSheetSerializer(serializers.ModelSerializer):
    company = serializers.SlugRelatedField(slug_field='symbol')

    class Meta:
        model = BalanceSheet
        fields = ('company','date','profit','loss')
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. This is really helpful, though I'm now running into queryset issues. Do you know a resource on writing to the API, other than the official docs? The official docs seemed to be geared towards reading, and appear outdated at time. (see the python 2 style print statement in "Inspecting relationships.")
I don't know, there are many resources & tutorials if you do a web search... If you have a specific issue or something you don't understand, please post it to SO so people can help you. :)
0

To answer my own question, here's what I wound up with. Thanks again go to dukebody.

models:

class Company(models.Model):
    cik = models.IntegerField(default=0)
    symbol = models.CharField(max_length=4)
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.symbol


class BalanceSheet(models.Model):
    company = models.ForeignKey(Company,
        null=True,
        on_delete=models.CASCADE,
        related_name='balance_sheets',)
    date = models.DateField()
    profit = models.BigIntegerField()
    loss = models.BigIntegerField()

    class Meta:
        unique_together = (('company', 'date'),)

    def __str__(self):
        return '%s - %s' % (self.company, self.date)

serializers:

class CompanySerializer(serializers.ModelSerializer):
    class Meta:
        model = Company
        fields = ('cik', 'symbol', 'name')


class BalanceSheetSerializer(serializers.ModelSerializer):
    company = CompanySerializer(many=False)

    class Meta:
        model = BalanceSheet
        fields = ('company', 'date', 'profit', 'loss')

    def create(self, validated_data):
        company_data = validated_data['company']
        company, created = Company.objects.get_or_create(**company_data)
        validated_data['company'] = company
        sheet = BalanceSheet.objects.create(**validated_data)
        return sheet

I also had to include the full company data within my curl statement as a nested dict.

Comments

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.