1

Following this answer, I tried to split my SQL Story table into parent/children - with the children holding the specific user data, the parent more generic data. Now I've run into a problem that betrays my lack of experience in Django. My user page attempts to show a list of all the stories that a user has written. Before, when my user page was only pulling data from the story table, it worked fine. Now I need to pull data from two tables with linked info and I just can't work out how to do it.

Here's my user_page view before attempts to pull data from the parent story table too:

def user_page(request, username):
    user = get_object_or_404(User, username=username)
    userstories = user.userstory_set.order_by('-id')
    variables = RequestContext(request, {
    'username': username,
    'userstories': userstories,
    'show_tags': True
    })
    return render_to_response('user_page.html', variables)

Here is my models.py:

class story(models.Model):
    title = models.CharField(max_length=400)
    thetext = models.TextField()

class userstory(models.Model):
    main = models.ForeignKey(story)
    date = models.DateTimeField()
    user = models.ForeignKey(User)

I don't really know where to start in terms of looking up the appropriate information in the parent table too and assinging it to a variable. What I need to do is follow the 'main' Key of the userstory table into the story table and assign the story table as a variable. But I just can't see how to implement that in the definition.

EDIT: I've tried story = userstory.objects.get(user=user) but I get 'userstory matching query does not exist.'

Community
  • 1
  • 1
Laurence
  • 661
  • 7
  • 24

1 Answers1

1

Reading through your previous question that you linked to, I've discovered where the confusion lies. I was under the impression that a Story may have many UserStorys associated with it. Note that I'm using Capital for the class name, which is common Python practise. I've made this assumption because your model structure is allowing this to happen with the use of a Foreign Key in your UserStory model. Your model structure should look like this instead:

class Story(models.Model):
    title = models.CharField(max_length=400)
    thetext = models.TextField()

class UserStory(models.Model):
    story = models.OneToOneField(Story) # renamed field to story as convention suggests
    date = models.DateTimeField()
    user = models.ForeignKey(User)

class ClassicStory(models.Model)
    story = models.OneToOneField(Story)
    date = models.DateTimeField()
    author = models.CharField(max_length=200)

See the use of OneToOne relationships here. A OneToOne field denotes a 1-to-1 relationship, meaning that a Story has one, and only one, UserStory. This also means that a UserStory is related to exactly one Story. This is the "parent-child" relationship, with the extra constraint that a parent has only a single child. Your use of a ForeignKey before means that a Story has multiple UserStories associated with it, which is wrong for your use case.

Now your queries (and attribute accessors) will behave like you expected.

# get all of the users UserStories:
user = request.user
stories = UserStory.objects.filter(user=user).select_related('story')

# print all of the stories:

for s in stories:
    print s.story.title
    print s.story.thetext

Note that select_related will create a SQL join, so you're not executing another query each time you print out the story text. Read up on this, it is very very very important!

Your previous question mentions that you have another table, ClassicStories. It should also have a OneToOneField, just like the UserStories. Using OneToOne fields in this way makes it very difficult to iterate over the Story model, as it may be a "ClassicStory" but it might be a "UserStory" instead:

# iterate over ALL stories

allstories = Story.objects.all()
for s in allstories:
    print s.title
    print s.thetext
    print s.userstory     # this might error!
    print s.classicstory  # this might error!

See the issue? You don't know what kind of story it is. You need to check the type of story it is before accessing the fields in the sub-table. There are projects that help manage this kind of inheritance around, an example is django-model-utils InheritanceManager, but that's a little advanved. If you never need to iterate over the Story model and access it's sub tables, you don't need to worry though. As long as you only access Story from ClassicStories or UserStories, you will be fine.

Josh Smeaton
  • 47,939
  • 24
  • 129
  • 164
  • Hi Josh, I've tried using that for loop and I get a `local variable referenced before assignment` error when I try to use main_story or us.main after the for loop, which I guess means that it's not doing anything. – Laurence Oct 21 '13 at 22:50
  • @Tennant that's because `main_story` variable is local to the loop scope only. It doesn't exist outside the loop. There are potentially many main stories, so it doesn't make sense to only have a single `main_story` outside the loop. See my update/edit. – Josh Smeaton Oct 21 '13 at 22:57
  • Thanks Josh. Finally I need help with a kind of catch 22 situation that arises from this. On the story display page, I get the username and story name from the url. But the username and story name are now in different tables. Looks something like this: `try: user = User.objects.get(username=username) story = userstory.objects.get( title=story, user=user, )` I don't know how to get one before getting the other without losing the unique story? – Laurence Oct 21 '13 at 23:27
  • To answer myself, a slightly hacky solution that I've derived is on the specific story view page, to iterate over all stories by a user, finding the story with the title that matches the string. Seems redundant, but then maybe that's the price to pay for splitting up the table. – Laurence Oct 21 '13 at 23:50
  • @Tennant can you please make an edit in your question, and explain exactly what it is you wish to display, and I will answer that question. Be specific. Are you trying to show a single users userstories? Are you trying to show a single story with all the userstories under it? Did you want to show all stories, and all userstories. Etc. Be specific. – Josh Smeaton Oct 21 '13 at 23:52
  • @Tennant see my new answer (I edited the original). I looked at your previous question, and I now understand where you were having problems. – Josh Smeaton Oct 22 '13 at 00:30
  • Thanks Josh. Your help has been very much appreciated. My solutions felt hacky, and I see now that it's because they were wrong! Cheers. – Laurence Oct 22 '13 at 00:40
  • Hi Josh. Your edited answer appears to be cut off. I'd be very interested to hear what you have to say about accessing children from the parent, as it's something that I'll need to do in the future. – Laurence Oct 22 '13 at 02:23
  • @Tennant I moved that sentence to the middle of the paragraph (the inheritancemanager), but forgot to remove the final sentence. I would google around for projects that help manage django inheritance, and open a new question if you run into this problem specifically. – Josh Smeaton Oct 22 '13 at 05:06