I'm using a single User model for authentication and creating multiple "type" accounts using the User model. Every type has a different dashboard so different things to show.
Organization
-> Teacher
-> Student
Q - I want to list the teachers and their corresponding students when inside a organization account ? It is a listView so I want to know how would I use revere relations to list all the students under specific teachers from an Organization account ?
class User(AbstractUser):
...
class Organization(models.Model):
user_id = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='superAdmin')
...
class Teacher(models.Model):
user_id = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='faculty')
super_admin_id = models.ForeignKey(
SuperAdmin, on_delete=models.CASCADE, related_name='faculty')
...
class Student(models.Model):
user_id = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='user')
faculty_id = models.ForeignKey(
Teacher, on_delete=models.CASCADE, related_name='student')
...
If there's any advice on how I can improve the existing model schema, I'd like to know that as well.
You can fetch the teachers along with their students (in two db hits) like this:
teachers = Teacher.objects.filter(
user_id__superAdmin=request.user.superAdmin
).prefetch_related('student')
for teacher in teachers:
print(f'Teacher: {teacher.pk}')
for student in teacher.student.all():
print(f'Student: {student.pk}')
EDIT:
You can also annotate per teacher the number of students assigned to them so:
teachers = Teacher.objects.filter(
user_id__superAdmin=request.user.superAdmin
).annotate(num_students=Count('student'))
for teacher in teachers:
print(f'Teacher {teacher.pk} has {teacher.num_students} students')
Related
I have what i think is a simple question but I am struggling to find out how it works. I get how related name works for foreign keys but with many to many fields it seems to break my brain.
I have two 3 models at play here. A User, TeamMember and Team Model as seen below.
User model is the built in django model.
#TeamMember Model
class TeamMember(models.Model):
member = models.ForeignKey(User, on_delete=models.SET(get_default_team_member), verbose_name='Member Name', related_name="team_members")
...
#Team Model
class Team(models.Model):
name = models.CharField(max_length=50)
manager = models.ForeignKey(TeamMember, on_delete=models.SET_NULL, related_name="managers", null=True, blank=True)
team_lead = models.ForeignKey(TeamMember, on_delete=models.SET_NULL, related_name="tls", null=True, blank=True)
tps = models.ForeignKey(TeamMember, on_delete=models.SET_NULL, related_name="tps", null=True, blank=True)
members = models.ManyToManyField(TeamMember, blank=True, related_name="members")
...
Now in a view i want to access a specific users team. I thought i could do this by doing something like this:
member = TeamMember.objects.get(pk=1)
member_team = member.members.name
However if I print member_name than it prints nothing. If I try to access any of the other fields on that model like member.members.team_lead.first_name it fails to find the team_lead field. I understand that this has a .all() attribute but i thought it was tied to the team object through the members field. So if that member matches the team it would give me the team. So I thought it might be an issue if the same member was linked to more than one team (which is possible) so i tired something like this member.members.all().first().name and i get an error that states it cannot get name from NoneType.
Is there an easy way to get the team name from a relationship like this or am i better off just doing a team query with the user?
Thanks,
jAC
First of all, I would like to point out that you are not using the related_name (and related_query_name parameters in a proper way). I think this SO post will help you to understand the concept in a better way.
So, I would change the related_name (and related_query_name) values in the Team model as below,
class Team(models.Model):
name = models.CharField(max_length=50)
manager = models.ForeignKey(
TeamMember,
on_delete=models.SET_NULL,
related_name="teams",
related_query_name="team",
null=True,
blank=True,
)
team_lead = models.ForeignKey(
TeamMember,
on_delete=models.SET_NULL,
related_name="teams",
related_query_name="team",
null=True,
blank=True,
)
tps = models.ForeignKey(
TeamMember,
on_delete=models.SET_NULL,
related_name="teams",
related_query_name="team",
null=True,
blank=True,
)
members = models.ManyToManyField(
TeamMember, blank=True, related_name="teams", related_query_name="team"
)
...
Now in a view i want to access a specific user's team.
Since the Team and TeamMember models are connected via ManyToManyField, you may have "zero or more" Teams associated with a single TeamMember
So, the following query will get you all the teams associated with a particular TeamMemeber
team_member = TeamMember.objects.get(pk=1)
all_teams = team_member.teams.all()
You can also iterate over the QuerySet as,
team_member = TeamMember.objects.get(pk=1)
for team in team_member.teams.all():
print(team.name)
For anyone wondering what I did based on JPG's advice was the for loop option
team_member = TeamMember.objects.get(pk=1)
teams = [t.name for t in team_member.members.all()]
I personally do not care which team i get as my need in this case is just to pass a team through even if it is none. So i just use team = team[0] if teams.count() > 0 else "No team"
I want to create a custom admin view. It should list specific objects from a model and add attributes to it.
More precisely, I have the tables Student and Lecture. Lecture has an attribute enrolled_students, which is a Many-to-Many relationship (Student can be enrolled in multiple lectures; a lecture can have many enrolled students). Students can gain points in a lecture, which determines their grade.
Therefore, I would like to provide an admin view, in which all students of a specific lecture are listed, along with their reached points and grade. The grade is computed lazily, as points are changing frequently.
How can I implement this? Is this possible using the built-in methods?
Thanks a lot!
EDIT 1
I added the source code (simplified):
Lecture:
class Lecture(UUIDModel, TimeFramedModel):
participants = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name="enrolled_lectures",
)
title = models.CharField(
max_length=100,
unique=True,
)
grading_scale = models.ForeignKey(
GradingScale, on_delete=models.SET_NULL, blank=True, null=True
)
def get_score(self, user):
""" Returns score of a particular user. """
score = (
user.submission_set
.filter(exercise__lesson__lecture=self, score__isnull=False, exercise__rated=True)
.values("exercise")
.annotate(max_exercise_score=models.Max("score"))
.aggregate(total_score=models.Sum("max_exercise_score"))["total_score"]
)
return score if score else 0
Student (User model):
class CustomUser(AbstractUser):
username = None
email = models.EmailField(_("email address"), unique=True)
full_name = models.CharField(_("full name"), max_length=130)
)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["full_name"]
objects = UserManager()
GradingScale contains the method get_grade(score). You can find the whole source code here.
I am new to Django only 2 weeks and I am stuck with scenario I need help with.
This is kind of core concept and I apologize in advance for my lack of knowledge.
I've extended the base user model like below with multiple roles. Now each role has distinct requirements for their profiles.
I need help to understand that how to extend Students or Staff further. There are two scenario I need to extend them.
Both can have similar extended models like addresses below.
Both can have different extended models like Students have CurrentCourse and Staff does not. Staff has Salary model and Students does not.
class User(AbstractUser):
is_student = models.BooleanField(default=True)
is_staff = models.BooleanField(default=True)
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
dob = models.CharField(max_length=30, blank=True)
location = models.CharField(max_length=30, blank=True)
class CurrentCourse(models.Model):
student = models.OneToOneField(Student,
on_delete=model.CASCADE)
....
class Staff(models.Model):
ROLES = (('Teacher', 'Teacher'), ('Professor', 'Professor'), ('Administration', 'Administration'))
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
role = models.CharField(max_length=100, choices=ROLES)
website = models.CharField(max_length=100, blank=True)
Both types of Users can have multiple addresses and both Students and Staff needs addressess, not sure how to extend this with ForeignKey.
class Address(models.Model):
user = ?
street = models.CharField(max_length=200)
city = models.CharField(max_length=50)
country = models.CharField(max_length=50)
class Salary(models.Model):
staff = models.OneToOneField(Staff, on_delete=models.CASCADE)
current_salary = models.DecimalField(max_digits=10, decimal_places=2)
Finally please let me know how can I apply validators on each model for instance I did sub-classed 'User' to all models instead of Student or Staff. How to apply a validator on OneToOneField like below? I thought that if I apply a validator like:
staff_validator(value):
I can't call user in validator function and not sure if it will be global or within model its applied on with indent.
class Salary(models.Model):
staff = models.OneToOneField(User, on_delete=models.CASCADE, validators=[staff_validator])
Thank you in advance for your kind help for this.
Imagine the real world scenario where your Users are People and People can have different roles with the time. Like if I am a Student today I belong to "People" category and if I will be Professor tomorrow still I will belong to People category.
So People can wear different hats or adapt different roles but they still will be People. Similarly your User model will always be part of all the roles you need to define in database. So you need to extend your User model for not only all the roles in fact for models you believe User can be part of and it should be like below:
class User(AbstractUser):
is_student = models.BooleanField(default=False)
is_teacher = models.BooleanField(default=False)
.....
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='foo')
.....
class Courses(models.Model):
user = models.ManyToMany(User)
You need to consult official docs with three types of models. Abstract models, Proxy models and Inherited models for more clarity. Also for creating view for your application you can create checks for your users with decorators. Here is a good example as per your question.
I'm currently building a website with the Django Framework. I want on the homepage of my website to display all posts made by people the user is following. Here are the classes for Profile, Story and Follow:
class Profile(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=30, null=True)
last_name = models.CharField(max_length=30, null=True)
class Follow(models.Model):
following = models.ForeignKey('Profile', on_delete=models.CASCADE, related_name="following")
follower = models.ForeignKey('Profile', on_delete=models.CASCADE, related_name="follower")
follow_time = models.DateTimeField(auto_now=True)
class Story(models.Model):
author = models.ForeignKey('accounts.Profile', on_delete=models.CASCADE, related_name="author")
title = models.CharField(max_length=50)
content = models.TextField(max_length=10000)
As you can see Follow uses two Foreign Keys to represent the following and the follower. Is there a way to query all stories from people the user is following?
I really don't know what to filter. Or is this maybe a job for aggregation? If someone could help me, that would be awesome!
following_feed = Story.object.filter(???).order_by('-creation_date')
One can use double underscores (__) to look "through" relations (like ForeignKeys, etc.).
So here we can filter like:
Story.objects.filter(
author__following__follower=my_profile
)
So by using author we obtain a reference to the Profile of the author, then with following we look at the Follow model and then finally with follower we again obtain a reference to Profile(s): the profile(s) of the follower(s).
my_profile of course need to be substituted with a Profile object (the profile of the person that is a follower of the authors of the Storys you wish to obtain).
This will generate a query like:
SELECT s.*
FROM story AS s
JOIN follow AS f ON f.following_id = s.author_id
WHERE f.follower_id = 123
where 123 is the id of the my_profile.
If a person is following another person multiple times (here this can happen since you do not enforce that the follower, following tuples are unique in the Follow model), then the corresponding Storys will be yielded multiple times.
It is therefore probably better to add a unique_together constraint in the Follow model:
class Follow(models.Model):
following = models.ForeignKey(
'Profile',
on_delete=models.CASCADE,
related_name="following"
)
follower = models.ForeignKey(
'Profile',
on_delete=models.CASCADE,
related_name="follower"
)
follow_time = models.DateTimeField(auto_now=True)
class Meta:
unique_together = (('following', 'follower'), )
It might also be worth to see the Follow model as the through model of a ManyToManyField [Django-doc].
Note that I haven't tested the code I am posting so tell me if something is missing.
First, you need to get all the Profiles that your users follow. Then you have to get the Stories that they have.
followed_people = Follow.objects.filter(follower=current_user).values('following')
stories = Story.objects.filter(author__in=followed_people)
My Users has phone contact LIST(s) - [3121234567,2121234567,6601234567]
Now, I want each user to be able to store as many LIST as possible. Each List must have a name(or description) attached to them under each USER account. Note: the number of LIST is dependent on Users needs. Example:
Students
[3121234567,2121234567,6601234567]
Club Member
[8101234567,8151234567,8171234567]
Now, how do I store it in a database.
Django User Model
class CustomUser(AbstractBaseUser):
username = models.CharField(max_length=254, unique=True)
first_name = models.CharField(max_length=24)
last_name = models.CharField(max_length=30)
phone = models.CharField(max_length=10)
email = models.EmailField(max_length=128)
street = models.CharField(max_length=128)
city = models.CharField(max_length=128)
state = models.CharField(max_length=2, choices=STATE_CHOICES, default=STATE)
zip_code = models.IntegerField(max_length=5, null=True, blank=True
USERNAME_FIELD = 'email'
REQUIRED_FIELD = ['username', 'first_name', 'last_name', 'phone', 'street', 'city', 'state']
objects = CustomUserManager()
Edit(Added):
I am not looking to create Student or ClubMember models. This name is use to identify the python phone contact list. PhoneAddress one can be labelled(named) as Student for one user but called "Conference Attendant" for another. Each user have different # of Lists.
PhoneAdress PhoneAdress2 PhoneAdress3
[3121234567,2121234567,6601234567] [4121234567,3121234567,6601234567] [7121234567,8121234567,9601234567]
Lemme guess, you're coming from a NoSQL background where the database is a document in a JSON form?
If so, I am sorry, in a Relational Database, used by Django in the likes of PostgreSQL or MySQL, they call something Foreign Keys, and that is your way of storing multiple "Lists" related to a particular field.
If you want many users to store as many lists as possible, you're looking at something like this, roughly speaking:
class myUserModel(models.Model):
# your arbitrary fields here
# then you do something like this:
class Student(models.Model):
user = models.ForeignKey(User)
class clubMember(models.Model):
user = models.ForeignKey(User)
With the above setup, you can add as many Student objects associated to the myUserModel class, so as the clubMember
However, if you wish to use PostgreSQL specifically, as your backend (perhaps as perpetual storage backend), you might find some sense in using Django's support for the ArrayField
And ooh, you might need to extend the Django User Model to add any extra fields easily, unless you're willing to go down the road of a custom User Model.
More info:
Django ForeignKey
This SO answer on 'OneToManyFields', similar to adding multiple items to a single field.
I hope the above helps
Create some models:
class Club(models.Model):
name = models.CharField(max_length=256,blank=True,null=True)
date_open = models.DateField()
class Institution(models.Model):
name = models.CharField(max_length=256,blank=True,null=True)
address = models.CharField(max_length=256,blank=True,null=True)
type = models.CharField(max_length=256,blank=True,null=True) #university, college
Rather than using
class CustomUser(AbstractBaseUser):
username = models.CharField(max_length=254, unique=True)
first_name = models.CharField(max_length=24)
last_name = models.CharField(max_length=30)
use composition in the form of OneOnOneField
class UserProfile(models.Model):
user = models.OneOnOneField(User,blank=True,null=True)
club_member = models.ManyToManyField(Club,blank=True, null=True)
institutions = models.ManyToManyField(Institution,blank=True, null=True) # student in
Once you have this, you will be able to get and add as many institutions and clubs to the lists:
user = User.objects.get(id=user_id)
club = Club.objects.get(id=club_id)
institution = Institution.objects.get(id=institution_id)
user.profile.clubs.add(club)
user.profile.institutions.add(institution)
So to verify if the user is a member of a club
club = user.proile.clubs.get(id=club_id)
and to verify the user is a student in an institution use
institution = user.profile.institutions.get(id=institution_id)