Django - Generic One-to-One Relation

298 views Asked by At

I have a website with different kinds of activities:

  • Lessons ;
  • Exercises ;
  • Quizzes.

So far, each type of activity corresponds to a specific Model and a table in the database. I would like to create an ordered path through these activities. For instance:

  • Lesson 1
  • then Exercise 1
  • then Lesson 2
  • then Quizz 1
  • etc.

I am considering to create a model named Activity that would store the following data:

  • A number: the number of the activity in the path ;
  • A One-To-One relationship to one given activity (lesson, exercise, quizz etc.).

(1) I have seen that Django offers a GenericForeignKey to handle many-to-one relationship to different kinds of models, like many comments associated to a single lesson or a single exercise. Is there something similar for Generic OneToOne relationship?

(2) I would like to track the progress of my users. To do so, I am considering having a many-to-many relationship called "done_activities" built in my User model and linked to my Activity model. Do you think this is an efficient way of approaching the problem ?

1

There are 1 answers

2
Milo Persic On

I'm not sure you would need or want self-referential fields in this case. Consider the following structure as an example. I do not propose this 'in stone' as THE solution, but more to spur your own ideas about the solution you need. Please note that I'm leaving out __str__ methods and such for brevity:

ACTIVITY_TYPE = ['Lesson', 'Exercise', 'Quiz']

class Activity(models.Model):
    activity_name = models.CharField(max_length=200, blank=True)
    activity_type = models.CharField(max_length=100, choices=ACTIVITY_TYPE, blank=True, db_index=True)
    activity_desc = models.CharField(max_length=200, blank=True) #description of the lesson, exercise, or quiz

class Program(models.Model):
    program_name = models.CharField(max_length=200, blank=True, db_index=True)
    description  = models.Charfield(max_length=200, blank=True)

class ProgramActivity(models.Model):
    program     = models.ForeignKey(Program, on_delete=models.CASCADE, your args etc...)
    activity     = models.ForeignKey(Activity, on_delete=models.CASCADE, your args here etc...)
    path_order  = models.PositiveSmallIntegerField() # stores a number for the order in the program path

class UserProgram(models.Model):
    student    = models.ForeignKey(User, etc...) # FK to the user
    program    = models.ForeignKey(Program, etc...) # connects users to programs
    progress   = models.DecimalField(max_digits=5, decimal_places=2) # stores percentage of program completed (for example)

In this schema scenario, the following are true:

  1. Activitys are all tracked and stored together on a single table, organized by type, and each one can have its own description.
  2. Programs are stored on their own model, and represent a named object that unites all their constituent activities.
  3. ProgramActivity connects activities to specific programs, and allows you to set the order in the path for that activity relative to the program, and change it easily if you have to. You can easily query activities = ProgramActivity.objects.filter(program=some_program).order_by('path_order') and get a very usable list of a Program's activities.
  4. Finally the UserProgram model records a User's "enrollments" and progress in each, in this example, by percentage of the program completed.

This is just one possible approach. You may, for example, want to create an activity type table instead of a list dropdown, which may be a more robust way of managing activities over time.