Error inserting data into Django database field with a OneToOnefield

I’ve asked this question before and tried to Google it but I’ve had no luck, so I have simplified my question. I have two very simple models: one holds some shift numbers and the other holds some data related to the sale of gift cards during a shift. In this case, we have an employee who worked shift “1234” and sold $200.45 and $43.67 worth of gift card from each of two terminals. The models are below:

class Data_Shifts(models.Model):
    shift_id = models.CharField(max_length=25, primary_key=True, db_column="shift_id", verbose_name="Shift ID")

    def __str__(self):
        return str(self.shift_id)

class Data_GiftCards(models.Model):
    shift_id = models.OneToOneField('Data_Shifts', on_delete=models.CASCADE, primary_key=True, db_column="shift_id", verbose_name="Shift ID")
    net_sales_terminal_1 = models.DecimalField(max_digits=8, decimal_places=2, default=0)
    net_sales_terminal_2 = models.DecimalField(max_digits=8, decimal_places=2, default=0)

    def __str__(self):
        return str(self.shift_id)

I then try to insert some test data into the table using the following command:

Data_GiftCards.objects.create(shift_id="1234", net_sales_terminal_1="200.45", net_sales_terminal_2="43.67")

Upon submitting the web form, I get the following error:

Cannot assign “‘1234′”: “Data_GiftCards.shift_id” must be a “Data_Shifts” instance.

I am boggled by this. I have a workaround that bypasses django and inserts directly into the table successfully, but this is dirty and I’d prefer to use the proper Pythonic Django way. What am I doing wrong here?

Many thanks in advance.

Answer

That is because you name your field shift_id. Django’s ORM maps the name of the field in your model to an instance of the related model, not to an ID.

You can still work with IDs instead of instances, but then you have to add _id to the end of your field name.

In your case, you have two options, you can simply do that, which would mean your query should look like:

Data_GiftCards.objects.create(shift_id_id="1234", net_sales_terminal_1=200.45, net_sales_terminal_2=43.67)

But shift_id_id looks redundant, so you can tweak the other end and remove the _id suffix in your model:

class Data_GiftCards(models.Model):
    shift = models.OneToOneField('Data_Shifts', on_delete=models.CASCADE, primary_key=True, db_column="shift_id", verbose_name="Shift ID")
    net_sales_terminal_1 = models.DecimalField(max_digits=8, decimal_places=2, default=0)
    net_sales_terminal_2 = models.DecimalField(max_digits=8, decimal_places=2, default=0)

    def __str__(self):
        return str(self.shift)

Then you will have to query as you are doing, but you should not use strings if the field types are numeric.

Data_GiftCards.objects.create(shift_id="1234", net_sales_terminal_1=200.45, net_sales_terminal_2=43.67)

Also, you don’t need the attribute db_column=”shift_id”. If your field name is shift, the name of the field in the database table will already be shift_id.