models with ManyToManyField - python

Suppose that you want to simulate universities system, you have courses, teachers and students.
We have some courses that are taught by some teachers and students choose some courses with some teachers.
For example:
Courses: math, physics
Teachers: Jack(math), Jane(math, physics)
Students: st1(math with Jack), st2(math with Jane), st3(physics with Jane and cant choose Jack!!), every score by default=-1.
With this code:
teachers = models.ManyToManyField(teacher.objects.filter(t_courses=s_courses), verbose_name='Ostad')
I got errors like:
raise AppRegistryNotReady("Models aren't loaded yet.") and
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet
from django.db import models
class profile(models.Model):
n_id = models.IntegerField(primary_key=True, verbose_name='code melli')
name = models.CharField(max_length=24, verbose_name='Full Name')
class Meta:
ordering = ('name',)
class course(models.Model):
name = models.CharField(max_length=24, verbose_name='Class Name')
unit = models.SmallIntegerField()
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
class teacher(profile,models.Model):
t_id = models.AutoField(primary_key=True)
t_courses = models.ManyToManyField(course, verbose_name='Dars')
def __str__(self):
return self.name
class student(profile,models.Model):
s_id = models.AutoField(primary_key=True)
s_courses = models.ManyToManyField(course, verbose_name='Dars')
#teachers = ??????????????????????????
score = models.IntegerField(default=-1, verbose_name='Nomre')
def __str__(self):
return self.name
How do I code in the teachers part?
Thanks a lot.

You're trying to do two different things here: Define which teacher gives a course for each specific student and restrict the choices (which is more a validation thing).
You're missing the actual Class model which links the course to the teacher and that you can use to define which classes the student is following:
class Teacher(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course, through="Class", related_name="teachers")
class Class(models.Model):
teacher = models.ForeignKey(Teacher, related_name="classes")
course = models.ForeignKey(Course, related_name="classes")
class Student(models.Model):
classes = models.ManyToManyField(Class)
The Class model is now your intermediate model for linking teachers to courses. You could also add more information about the class, such as its number, schedule (e.g. list of weekdays and hours the class takes place) and room number. Now you can fetch all the other things like that:
# all courses a teacher teaches
teacher.courses.all()
# all teachers of a student
[class.teacher for class in student.classes.all()] # list
Teacher.objects.filter(classes__in=student.classes.all()) # QuerySet
# all courses of a student
Course.objects.filter(classes__in=student.classes.all())
Now because you associate the student with the a Class instance, you can't select a wrong teacher. If for example you have a form where the user can pick a course, you'd present the classes belonging to that course in order to link the student to a course/teacher combination:
# teachers for a course
course.teachers.all()
If you want to keep track of the marks of the students for each class, you can add a Mark model as an intermediate model for your m2m relationship:
class Mark(models.Model):
student = models.ForeignKey(Student, related_name='marks' ...)
class = models.ForeignKey(Class, ...)
grade = models.DecimalField(max_digits=3, decimal_places=1, default=-1)
class Student(models.Model):
classes = models.ManyToManyField(Class, through='Mark', related_name='students')
But probably a more suitable model is to keep track of all the marks, e.g. when there are many exams for one class and you want to keep track of all the results. Then you just keep the Mark model above but don't use it as intermediate model to classes. In that way a student can have multiple grades for the same class:
student = Student.objects.get(id=1)
student.marks.filter(class__name='math').aggregate(Avg('grade'))
>> {'grade__avg': Decimal('8.3')}

Related

How to make more than one fields primary key and remove auto generated id in django models

Suppose in a relational database schema we have a student, a subject and a teacher which connect to each other with a relation teaches. Also, the relation has an attribute time that stores the time of the lesson. This is the most complete yet simplified example I can think to describe my case. Now, the most pythonic and django-wise way I can think of trying to reach a correct solution is, after creating a model class for student, subject and teacher, to create a new class Teaches, which has the foreign keys for the three other classes; also it has the property date field for time. This class would look something like this:
class Teaches(models.Model):
teachers = models.ForeignKey(Teacher, on_delete_models.CASCADE)
subjects = models.ForeignKey(Subject, on_delete_models.CASCADE)
students = models.ForeignKey(Student, on_delete_models.CASCADE)
time = models.DateField
class Meta:
constraints = [
fields=['teachers', 'subjects', 'students']
name='teacher_subject_student_triplet'
]
I added the Meta class because this is what this answer recommends as the correct approach.
The problem is that that in the migrations file I can still see the id field. The only way I've seen there is to remove it is to set another field as Primary Key, but in my case I cannot do that, having more than one keys. Any suggestions?
=========== model.py =============
from django.db import models
class TeacherModel(models.Model):
teacher_code = models.CharField(max_length=255)
def __str__(self):
return self.teacher_code
class SubjectModel(models.Model):
subject_code = models.CharField(max_length=255)
def __str__(self):
return self.subject_code
class StudentModel(models.Model):
student_code = models.CharField(max_length=255)
def __str__(self):
return self.student_code
class Teaches(models.Model):
custom_primary_key = models.SlugField(primary_key=True,blank=True)
teacher = models.ForeignKey(TeacherModel, on_delete=models.CASCADE)
subject = models.ForeignKey(SubjectModel, on_delete=models.CASCADE)
student = models.ForeignKey(StudentModel, on_delete=models.CASCADE)
time = models.DateField
#property
def make_key(self):
new_key = str(self.teacher.teacher_code + self.subject.subject_code + self.student.student_code)
return new_key
def save(self, *args, **kwargs):
self.custom_primary_key = self.make_key
super(Teaches, self).save(*args, **kwargs)
========= Output ==============
You can remove autogenerated id by adding primary_key=True, see below code:
class Person(models.Model):
username = CharField(primary_key=True, max_length=100)
first_name = CharField(null=True, blank=True, max_length=100)
setting a field to primary_key=True automatically makes it unique and not null.
In settings.py:
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
Controls the automatic generation of primary keys of each model if defined in settings.
Read this article:
Set AutoField or BigAutoField on a per model basis

m2m 'through' field in django models throwing this error: 'M2M field' object has no attribute '_m2m_reverse_name_cache'

Hey guys I am trying to add a m2m through field to have assistants to my 'Department' model to call like department.assistants.all(), but while doing so, I am getting this error AttributeError: 'ManyToManyField' object has no attribute '_m2m_reverse_name_cache'.
This is my model:
class Department(models.Model):
id = models.BigAutoField(primary_key=True)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
assistants = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Assistants', related_name='dep_assistants',
symmetrical=False)
class Assistants(models.Model):
id = models.BigAutoField(primary_key=True)
department = models.ForeignKey(Department, related_name='of_department', on_delete=models.CASCADE)
assistant = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='dt_assistant',
verbose_name="Department Assistant", on_delete=models.CASCADE)
added = models.DateTimeField(auto_now_add=True)
I am pretty new to this concept. Can someone tell me what I did wrong here?
Thanks
The way you have defined your models the queries seem too confusing. Try how models are defined below and then try the query.
You did not mention the through_field attribute in the many to many field definition. check the docs: https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.ManyToManyField
class Department(models.Model):
# i think this is not needed. Also id is a protected keyword in python.
# id = models.BigAutoField(primary_key=True)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
assistants = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Assistants',
related_name='departments', through_fields=("department", "assistant"))
# model name should never be prural. It is singluar becuase it is the name of the object.
class Assistant(models.Model):
# i think this is not needed. Also id is a protected keyword in python.
# id = models.BigAutoField(primary_key=True)
department = models.ForeignKey(Department, on_delete=models.CASCADE)
assistant = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name="Department Assistant", on_delete=models.CASCADE)
added = models.DateTimeField(auto_now_add=True)
# how to query assistants from departments
# you will get objects of User model
qs = department.assistants.all()
# how to query departments from assistants
# you will get objects of Department model
qs = user.departments.all()
# If you want to query the Assistant model
# from department object
qs = department.assistant_set.all()
# from assistant object
qs = user.assistant_set.all()
# in either case you will get the objects of Assistant model
for i in qs:
print(i.added, i.department, i.assistant)
Try this and let me know if you still get the error.
My suggestion is to name the assistant field on the Assistant model as user. This way you will not need to define through_field on the many to many field.
If one assistant relates to only one department - this is relation one-to-many. (One department has many assistants) In code would be:
class Assistant(models.Model):
...
department = models.ForeignKey(Department)
No need for a special reference on Department. To get all assistants:
assistants = models.Assistant.objects.filter(department=department)
Or create a property on a class Department:
#property
def assistants(self):
return models.Assistant.objects.filter(department=self)
If one assistant relates to many departments (and each department has many assistants), it is many-to-many relationship and there should be additional class between them:
class Assignment(models.Model):
assistant = models.ForeignKey(Assistant)
department = models.ForeignKey(Department)
class Department(models.Model):
...
assignment= models.ForeignKey(Assignment)
class Assistant(models.Model):
...
assignment = models.ForeignKey(Assignment)
So here to query assistants of the department:
assistants = models.Assistant.objects.filter(
assignment__in=models.Assignment.objects.filter(
department=department
)
)

Not sure I understand dependancy between 2 django models

I am struggling to understand django models relationship.
I have this arborescence:
A train have cars, and those cars are divided into parts. Then those parts all contains different references.
Like, for exemple, all the trains have the 6 cars, and the cars 6 parts. Each part have x reference to be associated.
I would like to use all of them in a template later on, where the user can select the train, the car and the part he worked on, then generate a table from his selections with only the references associated to the parts he selected.
It should update the train and the car (I'm trying to update a stock of elements for a company)
I dont really understand which model field give to each of them. After checking the doc, Ive done something like this but i am not convinced:
class Train(Car):
train = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
class Meta:
abstract = True
class Car(Part):
car = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
class Meta:
abstract = True
class Part(Reference):
part = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
class Meta:
abstract = True
class Reference(models.Model):
reference = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
def __str__(self):
return self.reference
Can someone please help me understand this so I can do well ? Thanks!!
1-)if you add abstract = True in your Model Meta class, your class doesn't created on database as a table. If you store data for any class, you mustn't define abstract = True.
2-)For relations, you can use models.ForeignKey . If you add a class into brackets of another class, it names: inheritance.(You can think like parent-child relation). In database management, we can use foreignkey for one-to-many relationship.
3-)In Django ORM, id field automatically generated. So you don't need to define id field.
If I understand correctly, also you want to store parts of user's selected.
So, your model can be like that:
class Train(models.Model):
name = models.CharField(max_length=200) # I think you want to save name of train
class Car(models.Model):
train = models.ForeignKey(Train,on_delete=models.Cascade)
name = models.CharField(max_length=200)
class Part(models.Model):
car = models.ForeignKey(Car,on_delete=models.Cascade)
name = models.CharField(max_length=200)
class Reference(models.Model):
part = models.ForeignKey(Part,on_delete=models.Cascade)
name = models.CharField(max_length=200)
def __str__(self):
return self.reference
#addtional table for storing user's references
class UserReference(models.Model):
user = models.ForeignKey(User,on_delete=models.Cascade)
reference = models.ForeignKey(Reference,on_delete=models.Cascade)
name = models.CharField(max_length=200)
With this definitions, you can store user's definition on UserReference table. And with Django Orm, you can access train object from UserReferenceObject.
#user_reference: UserReference object like that result of UserReference.objects.first()
user_reference.reference.part.car.train.name

Is there a way to filter data when passing it in ModelForm and how can we edit them?

I'm creating a site to register students. So basically it is divided into 3 parts
1. A student register model which take the name, fathername, etc of student.
2. A student fee model which use foreignkey to get register student.
3. ModelForm for showing student fee model to enter data.
Now the problem if I want to fill a student fee of class 1 it shows all the student of other classes, but I want the student filter according to their classes and their name show and in front of them editable fee and pending fee form.
By some reach I got to know about ModelForm instance I wrote code for automatically add register students to student fee.
def student_fee(request):
# add a selection field to a variable for filtering student_class below this.
students = StudentRegister.objects.filter(student_class="1")
....
for x in students:
if not StudentFee.objects.filter(student=x):
StudentFee.objects.create(student=x, fee=0, pending_fee=0)
But for instance I have to know primary key of every student I can loop through them but it only get the last element.
models.py
class StudentRegister(models.Model):
student_image = models.ImageField(upload_to="register_student", blank=True)
student_class = models.CharField(max_length=2, choices=STUDENT_CLASS, default="1")
mobile_number = models.CharField(max_length=50)
student_name = models.CharField(max_length=50)
father_name = models.CharField(max_length=50)
mother_name = models.CharField(max_length=50)
address = models.CharField(max_length=200)
student_fee = models.CharField(max_length=10, default="0")
Date_Of_Birth = models.DateField(auto_now=False)
admission_fee = models.CharField(max_length=10)
Admission_date = models.DateField(auto_now=False)
adhaar_no = models.CharField(max_length=100)
def __str__(self):
return "%s class: %s" % (self.student_name, self.student_class)
class StudentFee(models.Model):
student = models.ForeignKey(StudentRegister, on_delete=models.CASCADE)
fee = models.CharField(max_length=20)
pending_fee = models.CharField(max_length=20)
def __str__(self):
return "%s " % (self.student)
forms.py
class StudentFeeForm(forms.ModelForm):
class Meta:
model = StudentFee
fields = '__all__'
views.py(its messy sorry)
def student_fee(request):
# add a selection field to a variable for filtering student_class below this.
students = StudentRegister.objects.filter(student_class="1")
real = StudentFee.objects.all()
# student_form = StudentFeeForm(request.POST or None)
student_form = StudentFeeForm(request.POST)#, instance=students)
# print(dir(students))
q = (students.get(pk=1))
global list_student_pk
list_student_pk = []
for x in students:
list_student_pk.append(x.pk)
student_get_instance = StudentFeeForm(instance=q)
# print(student_get_instance)
# This thing done don't touch----------------------------------
for x in students:
if not StudentFee.objects.filter(student=x):
StudentFee.objects.create(student=x, fee=0, pending_fee=0)
if request.method == "POST":
if student_form.is_valid():
pass # this thing will done after the form problem solved.
# till here ==========================================================
return render(request, "student_fee_home.html", {"students": students, "real":real, "student_form":student_form, "list_student_pk":list_student_pk, "student_get_instance":student_get_instance})
I want that modelforms filter according to class.
Then the student fee model which already having register student(student name, fee, pending fee) can edit. So that it shows the student name and right in front of him a editable fee form and a pending fee form.
It is working right now like this showing all student of all classes, but I want that students name will show instead of selection field. In my knowledge only option to display names of student display names direct from models then use a form to take input of fee and pending fee then create it in models.
djang0-filter will help you for filtering
pip install django-filter
Model.py
class Dispatcher(models.Model):
_safedelete_policy = SOFT_DELETE
Individual = 'Individual'
Company = 'Company'
TYPES = [
(Individual, 'Individual'),
(Company, 'Company')
]
type = models.CharField(max_length=255,
choices=TYPES,
default=Individual)
Views.py
class DispatcherViewSet(viewsets.ModelViewSet):
queryset = Dispatcher.objects.all()
serializer_class = DispatcherSerializer
filter_backends = (DjangoFilterBackend,)
filterset_fields = ['type']

Get all teachers that have at least 1 subject on table - Django Query Set

I Would like to get all the Teachers that have at least 1 subject. Currently I'm using...
user = users.objects.all().order_by('-karma')[:100]
Because people who does not have any subjects related is a Student.
Here is my models.py
class subjects(models.Model):
id_user = models.IntegerField(db_column='ID_user') # Field name made lowercase.
s = models.CharField(max_length=90)
def __unicode__(self):
return self.s
class Meta:
db_table = 'subjects'
class users(models.Model):
email = models.EmailField(max_length=160)
nick = models.CharField(unique=True, max_length=60)
karma = models.IntegerField(max_length=11)
pass_field = models.CharField(db_column='pass', max_length=160)
One option is to do this in two steps:
get id_user list from subjects model with the help of values_list():
user_ids = subjects.objects.values_list('id_user', flat=True).distinct()
get all users by the list of id_users using __in:
print users.objects.filter(pk__in=user_ids)
Also, since models are not related, you can make a raw query that would do the same in one go.

Categories