user11879807
user11879807

Reputation: 301

Database normalization in django

I need an optimally normalized database structure to achieve the following requirement.

models.py

class Learning_Institute(models.Model):
    name = models.TextField()
    user = models.ManyToManyField(settings.AUTH_USER_MODEL)

class Course(models.Model):
    title = models.CharField(max_length=50)
    instructor = models.ForeignKey(User, on_delete=models.PROTECT, related_name='courses_taught')
    institute = models.ForeignKey(Learning_Institute, on_delete=models.PROTECT, related_name='courses')

I need the instructor field in the Course table to be limited to the set of users in Learning_Institute instead of all the users in the system.
How do I achieve this on the DB level?

Upvotes: 0

Views: 1781

Answers (3)

Daniel Backman
Daniel Backman

Reputation: 5241

I don't know if it's suitable for your scenario but changing the relations slightly may achieve what you want. Removing the many to many for User and create a concrete association model for it, will at least make sure the Course can only have users that also are instructors, by design.

Consider the following model structure:

class LearningInstitute(models.Model):
    name = models.TextField()

class InstituteInstructor(models.Model):
    class Meta:
        unique_together=('user','institute')

    user = models.ForeignKey(User, on_delete=models.PROTECT)
    institute = models.ForeighKey(LearningInstitute, on_delete=models.PROTECT)

class Course(models.Model):
    title = models.CharField(max_length=50)
    instructor = models.ForeignKey(InstituteInstructor, on_delete=models.PROTECT)
  1. You have LearningInstitutes
  2. A user can be an instructor with a related institute, a User can only be related to the same institute once
  3. A Course can only have an instructor (and by that also the related institute)
  4. Design can easily be extended to let Courses have multiple instructors.

By design the Course can only have users that are also instructors.

Upvotes: 2

cezar
cezar

Reputation: 12012

There is a possibility in Django to achieve this in your model class. The option that can be used in models.ForeignKey is called limit_choices_to.

First I'd very strongly recommend to rename the field user in the class LearningInstitute to users. It is a many to many relation, which means an institute can have many users, and a user can perform some work in many institutes.

Naming it correctly in plural helps to better understand the business logic.

Then you can adapt the field instructor in the class Course:

instructor = models.ForeignKey(
    'User', # or maybe settings.AUTH_USER_MODEL
    on_delete=models.PROTECT,
    related_name='courses_taught',
    limit_choices_to=~models.Q(learning_institute_set=None)
)

This is not tested and probably will need some adjustment. The idea is to get all User objects, where the field learning_institute_set (default related name, since you haven't specified one) is not (the ~ sign negates the query) None.

This has however nothing to do with normalisation on the database level. The implementation is solely in the application code, and the database has no information about that.

As suggested by @TreantBG, a good approach would be to extend the class User and create class Instructor (or similar). This approach would affect the database by creating an appropriate table for Instructor.

Upvotes: 1

TreantBG
TreantBG

Reputation: 1222

I don't think that you can limit in the model itself.

One of the things that you can do is on form save to have validations using form clearing methods like so

And you can create a check that does something like this:

def clean_ instructor(self):
    instructor = self.cleaned_data['instructor']
    if instructor.type != "instructor":
        raise forms.ValidationError("The user is not instructor!")

Another option is to create another User object that will inherit User and you can call it InstrcutorUsers

I have used this tutorial to extend the user model in django

Upvotes: 2

Related Questions