I have 3 different account types in a Django project. Should I use 1 or 3 apps?
I'm new to Django. I've been making a project with 3 types of accounts: Schools, professors and users. But I feel like some things would be too "repetitive" like some model fields and views the views. I thought that having 3 apps would be too much, so I decided to delete them and create a single app as "accounts" where I manage schools, professors and users as 3 separate classes in models.py. Is this a good option? I'm kind of not sure
maintain a common User models those have common filed for login and signup like email , role username password etc..,then create separte models for school,proffesor,students with onetoone relation to the user,thats a good production grade approach with the field for proffesor and student may differ so you can normalise the table
Yes, your option is a common and great option for managing user types in Django. Adding 3 apps is kind of repetitive, and it also increases how much code you have to manage over time, possible locations for vulnerabilities, and server load. It is OK to store 3 user types in one models.py (also that is what I do too, and my project's almost done and working fine).
It depends on what you want to achieve. If all of them should be able to log in to the app, you are asking the wrong question. I'll expand on that in particular in this answer. If you want only one of them to be able to log in, and the others exist as things the logged-in users manage ... then you can disregard the rest of this answer.
TL;DR: If all the different user accounts should be able to log in to the app - do not have three models for the users, have just one.
Multi-model login is non-trivial in Django - all Django's built-ins operate on the assumption that there's one canonical user model, so in order to facilitate multiples you would at a minimum look at re-implementing all infrastructure for account creation, login, etc. and then also session middleware and authentication backends. Which, while doable, is in my opinion not advisable for someone who is new to the stack - it's a large (and often fruitless) undertaking.
So centralizing everything in one app is the first correct step. But arguably you should make one more: centralize the user model, too.
If you want database-centric checks of how they are distinct from eachother, implement that with a field as such:
class AccountType(models.TextChoices):
STUDENT = "STUDENT"
PROFESSOR = "PROFESSOR"
SCHOOL = "SCHOOL"
class User(...):
type = models.CharField(
max_length=255,
choices=AccountType.choices # this part limits what you can input in this field to the items listed in `AccountType`
)
Then, whenever in your code you need to do anything conditional depending on whether they're a student or a professor, you can check the user's type by simply asking for that label on the user object:
if user.type == AccountType.STUDENT:
# the logic for students
elif user.type == AccountType.PROFESSOR:
# etc.
But for anything that's supposed to segregate what a user can or cannot do in the application, the far better approach - best practice by far - is to make use of Django's groups & permissions.
Both groups and permissions can be delegated manually in the admin portal, or you can customize the account creation logic to delegate correct groups/permissions automatically.
How I'd implement it:
- Use Groups to segregate user accounts into buckets of
Student,Professor,School - Set permissions (who can perform which actions) on each Group
- In your code, build the conditional logic around what behavior is permitted (not which group the user belongs to) - because that lets you have the granularity of also assigning special permissions to non-group members.
Like this:
class Exam(models.Model):
# let's say this contains upcoming exam questions
class Meta:
permissions = [
("can_view_exam", "Can view exam"),
]
Then you would go in the admin panel (also possible with code, but for simplicity I'll skip that) and change the Professor group to have the can_view_exam permission. Do not give it to the Student group.
In the view that's built to let professors see and edit upcoming exam questions, you would use a guard clause to make sure the user has sufficient permission to access (either because somebody gave it to them directly or because they belong to a group that has it):
def exam_detail(request, exam_id):
if not request.user.has_perm("exam.can_view_exam"):
return HttpResponseForbidden("You can't access this resource")
Now, any user who belongs to Professor group will be let through to see those resources - and anyone who doesn't, will get an error message instead.
This is idiomatic Django design - don't use separate user models to re-implement this, it's a mountain of work to at best gain the exact same behavior that already exists by leveraging permissions.
You can combine this with the AccountType style of approach too, if you want easier ergonomics for checking, debugging, or visibility of "account type" in the admin panel for instance. But the business logic and the gatekeeping internally in your application should be centralized around permissions.