Can a django model have a hardcoded no type field?

Viewed 309

Basically my question stems from the need to create multiple models that branch off a main base class. Each of these models have a type, and I wish to keep this type on the model in the database, but I want to room for error so I thought of hardcoding it into the model.

This is pretty much a question of "Is this something that django supports?". I haven't fully tested this, I don't know / can't say that if I did this right now everything would be fine or not ... but I can't seem to find the relevant documentation for it and would like to learn the best practices.

Here is an example of what I mean:

from django.db import models
from enum import Enum


class PersonType(Enum):
    STUDENT = "Student"
    TEACHER = "Teacher"

class BaseClass(models.Model):
  """Represents a base model"""
  name = models.TextField()
  age = models.IntegerField()

class Student(BaseClass):
  """Represents a student"""
  type = PersonType.STUDENT.value

class Teacher(BaseClass):
  """Represents a teacher"""
  type = PersonType.TEACHER.value

Would something like this be accepted by django? The only other solution I can think of is to make type a TextField with the default property set, and never expose it via the serializer. There is no reason I can't do that, just checking that this could work as well since it's, aesthetically, more pleasing.

2 Answers

First of all, your base class should be abstract because there's no point of having that table on database.

For the enum question, you should use CharField with choices. Here's an example for you.

from django.db import models


PERSON_TYPE = (
  ('Student', 'Student'),
  ('Teacher', 'Teacher'),
)

class BaseClass(models.Model):
  """Represents a base model"""
  name = models.TextField()
  age = models.IntegerField()
  type = models.CharField(choices=PERSON_TYPE)
  
  class Meta:
    abstract = True

class Student(BaseClass):
  """Represents a student"""
  def __init__(self, *args, **kwargs):
    kwargs['type'] = kwargs.get('type') or 'Student'
    super().__init__(*args, **kwargs)

class Teacher(BaseClass):
  """Represents a teacher"""
  def __init__(self, *args, **kwargs):
    kwargs['type'] = kwargs.get('type') or 'Teacher'
    super().__init__(*args, **kwargs)

It might look like there's no point in using inheritance because both Student and Teacher models have the same fields. Although, you might need to add specific fields for each model and it'll come in handy for you to have a separation from the beginning.

Also, for anyone in the future not satisfied with the above. A more risky approach (risky as you need to remember to do this in every view when fetching the model, is to add this:

# TextField can be any instance of the `django.db.models` fields.
from django.db.models import TextField, Value

Students.objects.filter(...).annotate(type=Value(PersonType.STUDENT.value, TextField())

This gives you access to student.type as a field on the model, but is NOT stored in the database.

Related