django: Switch the inherited model base class to different child - django

The base class model and inherited models are as follow:
class Profile(models.Model):
user = models.ForeignKey(User, unique=True)
...
class Student(Profile):
...
class Teacher(Profile):
...
I have object of Student class(the data for which are stored in two tables in db - Profile table and a Student table which has a pointer to Profile table). I need to assign that Profile table row to a Teacher object and delete the Student object, i.e. without losing the content of Profile table data and also retaining the same id for that row. How it should be done?
Thank you,
Any help would be appreciated..

Behind the scenes, Django creates a field on the Student and Teacher models called profile_ptr_id. So you'll probably have to manipulate that. Because of how Django's concrete inheritance works, each Student and Teacher object's primary key is actually this profile_ptr_id field. Thus, I am not sure having both a Student and Teacher object with the same profile_ptr_ids is allowed.
One way you can try to get around this is the following:
Create a new Profile object
Set the Student object's profile_ptr_id to the id of the new Profile object
Set the Teacher object's profile_ptr_id to the id of the old Profile object that the Student object was previously pointing to.
Delete the Student object
I've never tried this, so I really can't say if it will work or not...

If you have a student that you want to turn into a teacher with some or all fields, and you can afford to type out the fields, you can just do:
teacher = Teacher(id=student.id, user=student.user,
name=student.name, email=student.email, ...)
student.delete()
teacher.save()
It's important to set the ID and delete the old object before saving the new one with the same ID.

Related

Changing a ForeignKey to a be the parent class/model

I have a situation where there are various entities, with ForeignKeys to models that are subclasses of another model. I need to update these FKs to point to the parent, so that any of the various subclasses could be linked.
For example, the code might be:
class School(Model):
...
class University(School):
...
class Student(Model):
university = ForeignKey(University)
... and I need to change that FK from Student to University, to be an FK to School.
I think that all Universities will have the same ID as the School that is in the database for that university. So is it safe/reliable to define the Student:
class Student(Model):
university = ForeignKey(University)
school = ForeignKey(School)
Then make a migration that creates the new school attribute, and uses RunPython to copy the id from university into school, then delete the university attribute and makemigrations for that?
Is it ever possible that this method would break, or produce bad data?

Identify what model belongs a unique field (primary key)

I would like with an unique field, to obtain the name of the model where it belongs. I was trying to use the get_model but it has another purpose.
Suppose that you have a school with multiple classrooms (each classroom is a model), and you wan to search a student with just the ID of him, receiving the name of the classroom where the student is.
So you type the ID (primary key) and the function will return you the class (model) where the student belong
I would like with an unique field, to obtain the name of the model where it belongs.
To get the model, to which a field belongs, you can use the model attribute:
Returns the model on which the field is defined. If a field is defined
on a superclass of a model, model will refer to the superclass, not
the class of the instance.
The above will answer your question, but a bigger issue is with this:
Suppose that you have a school with multiple classrooms (each
classroom is a model), and you wan to search a student with just the
ID of him, receiving the name of the classroom where the student is.
This is not a sustainable model and will cause issues as each classroom means a new table. This structure will cause you problems when you are doing searches across classrooms.
The second issue is you are confusing the value of a field with the name of the field. A primary key (the id) is a value. You can search for values if you know what field there are in, if you want to retrieve a record with primary key value 1, you would write a query like this:
SELECT * FROM classroom_table WHERE id = 1
In Django, this is Classroom.objects.get(id=1)
You can't go the other way around, that is "find me the table, that has the value "1" in the field of id" which is what you are asking to do here.
You need to fix your database structure so that there is one model for students, and one model for classroom; and a student can belong to many classrooms:
class Student(models.Model):
name = models.CharField(max_length=200)
class Classroom(models.Model):
students = models.ManyToMany(Student)
Now you can do things like find all classrooms for a student with id 1:
student = Student.objects.get(id=1)
for room in student.classroom_set.all():
print(room)
This question isn't very clear.
If you have a Student model and a Classroom model, and there is a ForeignKey from Student to Classroom, then you need to get the student from the primary key:
student = Student.objects.get(pk=my_pk_value)
and then get its classroom:
student.classroom

Get Primary key of an element(Teacher) which is referred in student model django

I am new to Django and I don't know how to get access the primary key. I have a scenerio in which there are two users teacher and student one-to-one relationship to abstract user.In my case teachers create student accounts, I have created teachers accounts successfully,I also want to save students registered by the teachers which also know how to do it,but my problems is I also want to save the teacher in student model who registered that particular student, for this I want to get that teacher object(The teacher which is adding student at that time) which I can't,can someone provide suggestions or code how to do it?
Models.py File:
class User(AbstractUser):
is_student=models.BooleanField(default="False")
is_prof=models.BooleanField(default="False")
class Teacher(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE)
designation=models.CharField(max_length=30)
class Student(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE)
teacher=models.ForeignKey(Teacher,on_delete=models.CASCADE,default="")
batch=models.CharField(max_length=30)
Below my piece of approach to add student, user is created successfully but what to do in last line student= so I can teacher object through teacher_id:
obj=User.objects.create_user(
username=myids[iterate],password=passwords[iterate],
is_student=True,is_prof=False)
obj.save()
Student.objects.create(user=obj,teacher=(What to do?),batch="F-14")
If you create a field that is a ForeignKey, Django automatically creates a "twin field" named field_id, that stores the id. Updates to the some_object.field reflect in the some_object.field_id, and vice versa.
You can thus use the teacher_id field instead here:
obj= User.objects.create_user(
username=myids[iterate],
password=passwords[iterate],
is_student=True,
is_prof=False
)
obj.save()
Student.objects.create(user=obj, teacher_id=myteacherid, batch="F-14")
An alternative is to first fetch the Teacher object with the given id, and then use this, but this is less efficient, since it will result in two queries to construct the object: one to fetch the Teacher, and one to create the Student:
obj= User.objects.create_user(
username=myids[iterate],
password=passwords[iterate],
is_student=True,
is_prof=False
)
obj.save()
my_teacher = Teacher.objects.get(pk=myteacherid)
Student.objects.create(user=obj, teacher=myteacher, batch="F-14")
You can get the teacher object and assign it to 'teacher' field in Student model.
teacher = get_object_or_404(Teacher, pk=primary_key_of_teacher)
student = Student(user=user_object teacher=teacher batch=student_batch)
student.save()

How to support user polymorphism in Django without raw sql?

Subtitle: when the student becomes the teacher
Imagine two classes, Student and Teacher, that each inherit from User. None of these models is abstract. How to you modify an instance of student so that they can also be a teacher, without just dropping into raw sql and inserting the teacher record?
First, you have to think about your design. Not in terms of Django, but generally.
If you have a User class, and a Student class and a Teacher class, then you can create instances of User, of Student and of Teacher. You cannot create an instance that is both a student and a teacher, unless you create a fourth class StudentTeacher that inherits from both Student and Teacher. This is a special case of multiple inheritance, called "diamond shape" inheritance and it is rarely a good idea.
Django does support inheritance for its models. If B inherits from A, and A isn't declared abstract to Django, then Django will create a table for A and a table for B with a foreign key to A and it will join the tables B and A if you access an instance of B.
But I don't think that Django supports multiple model inheritance, which you would need here. And even if it does, it will lead you into a world of pain.
The solution is, I think, to favor composition over inheritance. Instead of creating User, Student, Teacher and StudentTeacher, you create User, StudentRole and TeacherRole.
Then, you can compose the user with the student role or the teacher role, or both. The user has a teacher role or has a student role.
In Django, you use a OneToOneField for this:
class User(Model):
name = CharField()
class StudentRole(Model):
user = OneToOneField(User, related_name='student_role')
average_grade = FloatField()
class TeacherRole(Model):
user = OneToOneField(User, related_name='teacher_role')
age = IntegerField()
`And then you create objects like this:
user = User()
user.save()
student_role = StudentRole(user=user)
student_role.save()
teacher_role = TeacherRole(user=user)
teacher_role.save()
(Of course you can also use StudentRole.objects.create and so on.)
And you can use the object like this:
user.student_role.average_grade = 4
user.save()
user = User.objects.get(whatever_id)
student = Student.objects.create(user=user)
teacher = Teacher.objects.create(user=user)
Here you have the same user having an instance in Student object and Teacher object.
But as pointed in a comment, a Student instance can't be a Teacher one. That's impossible. It's not about django.

django nested models field access

Here's the scenario:
I have a Student Model with subject field connected with Many-to-Many relationship to Subject Model
class Student(models.Model):
(...)
subject = models.ManyToManyField(Subject)
In Subject Model i have a program field connected with Many-to-Many relationship to Programs Model. Subject Model got also CharField name.
class Subject(models.Model):
(...)
program = models.ManyToManyField(Programs)
In Programs Model i have a field:
class Programs(models.Model):
name = models.CharField(max_length=40)
(...)
Django creates additional table for many-to-many fields. In my application I create (with form) a Program and Subject corresponding to Program. Then i create some Students and choose some subjects.
How can i access program name field (Student.objects.all()) and display what program name Student is using ? Is it possible, or i need to create additional fields in Student Model connected with Many-to-Many relationships with Program Model ?
Am I right in thinking that you want to return a list of the program names for the programs belonging to each subject in student.subject?
If so you could use this as a method of the Student model:
def get_program_names(self)
programs = []
for subject in self.subjects:
programs += [program.name for program in subject.program]
return programs
It sounds from your question, however, that you expect only one program name to be returned. if this is the case then maybe you should replace your manyToMany fields with ForeignKey fields, which give a one to one relationship between two models, meaning that this method should work:
def get_program_name(self):
return self.subject.program.name
(either way, there is no reason you should have to create a direct link between your student model and the program model, unless you wish to reduce the number of database calls, in which case you could add a field to Student which is updated with the latest program name or similar.)
I hope that I haven't misunderstood your question.