henrikstroem
henrikstroem

Reputation: 3068

Loading fixture with many-to-one relations in Django

The Django tutorial on djangoproject.com gives a model like this:

import datetime
from django.utils import timezone
from django.db import models

class Poll(models.Model):
    question = models.CharField(max_length = 200)
    pub_date = models.DateTimeField('date published')

    def __unicode__(self):
        return self.question

    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days = 1) <= self.pub_date < now

    was_published_recently.admin_order_field = 'pub_date'
    was_published_recently.boolean = True
    was_published_recently.short_description = 'Published recently?'

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice_text = models.CharField(max_length = 200)
    votes = models.IntegerField(default = 0)

    def __unicode__(self):
        return self.choice_text

Choice uses a ForeignKey, which is a many-to-one relation, so I should be able to use a Choice for more than one Poll. If I try to load this from a fixture, like so:

[
    {
        "model": "polls.Poll",
        "pk": 3,
        "fields": {
            "question": "What time do you sleep?",
            "pub_date": "2013-07-29T10:00:00+00:00"
        }
    },
    {
        "model": "polls.Poll",
        "pk": 4,
        "fields": {
            "question": "What time do you get up?",
            "pub_date": "2013-07-29T10:00:00+00:00"
        }
    },
    {
        "model": "polls.Choice",
        "pk": 4,
        "fields": {
            "poll": [{"pk": 3}, {"pk": 4}],
            "choice_text": "10:00",
            "votes": 0
        }
    }
]

I get this error:

    DeserializationError: Problem installing fixture '/.../poll/polls/fixtures/initial_data.json': [u"'[{u'pk': 3}, {u'pk': 4}]' value must be an integer."]

Or so:

{
        "model": "polls.Choice",
        "pk": 4,
        "fields": {
            "poll": [3, 4],
            "choice_text": "10:00",
            "votes": 0
        }
    }

I get this error:

DeserializationError: Problem installing fixture '/.../poll/polls/fixtures/initial_data.json': [u"'[3, 4]' value must be an integer."]

How can I load many-to-one relations from a fixture?

Upvotes: 6

Views: 4904

Answers (2)

Zhao Li
Zhao Li

Reputation: 5706

Sorry, for posting to an old thread, but I came across this post when I was searching for a way to use fixtures to create associated models without needing to manage foreign/primary keys/ids.

The Djanto tutorial didn't mention using "natural keys", but it is a great way of freeing the developer from managing foreign/primary keys/ids.

This StackOverflow asks/answers natural keys more directly: Django: Create fixtures without specifying a primary key?

This Django documentation documents the natural key feature: https://docs.djangoproject.com/en/dev/topics/serialization/#topics-serialization-natural-keys

It is helplful to use Django's dumpdata tool to reverse engineer what a fixture file with natural keys would look like: https://docs.djangoproject.com/en/dev/ref/django-admin/#cmdoption-dumpdata-natural-foreign

This is untested, but the polls/choices example referenced in this question might use natural keys like this (sorry, I prefer YAML and might have simplified the model to highlight the important parts):

- model: polls.Poll
  fields:
    question: What time do you sleep?
- model: polls.Poll
  fields:
    question: What time do you get up?

- model: polls.Choice
  fields:
    choice_text: 10:00
    owner:
      - What time do you sleep?
      - What time do you get up?

I hope this makes it easier for the next person that gets here looking for a way to work with Django fixtures with associations.

Upvotes: 1

alecxe
alecxe

Reputation: 473943

Here's a quote from the tutorial:

Finally, note a relationship is defined, using ForeignKey. That tells Django each Choice is related to a single Poll. Django supports all the common database relationships: many-to-ones, many-to-manys and one-to-ones.

Each Choice is related to a single Poll and you are trying to pass a list of keys to the Choice.poll field.

But, each poll can be related to several choices:

{
    "pk": 4, 
    "model": "polls.Choice", 
    "fields": {
        "votes": 0, 
        "poll": 2, 
        "choice_text": "Meh."
    }
}, 
{
    "pk": 5, 
    "model": "polls.Choice", 
    "fields": {
        "votes": 0, 
        "poll": 2, 
        "choice_text": "Not so good."
    }
}

Hope that helps.

Upvotes: 4

Related Questions