Django query complex query in one to one field model - django

I have two models of Student and Parent
Student models.py:
class StudentInfo(models.Model):
admissionNumber = models.BigIntegerField(primary_key=True,default=0)
firstName = models.CharField(max_length=20)
lastName = models.CharField(max_length=20)
fullName = models.CharField(max_length=50)
gender = models.CharField(max_length=20)
dob = models.DateField(null=True)
classSection = models.CharField(max_length=20)
Parent models.py
class ParentInfo(models.Model):
student = models.OneToOneField(StudentInfo,primary_key=True, on_delete=models.CASCADE)
fatherName = models.CharField(max_length=20)
motherName = models.CharField(max_length=20)
I have a form to search students through their fatherName.
So, what I want is to filter those students whose father's name contains 'some name'.
I tried this but it resultes in query set of ParentInfo:
parentInfo = ParentInfo.objects.all()
studentsInfo = parentInfo.filter(parent__fName = fName).select_related('student')

You should filter the opposite way, like:
StudentInfo.objects.filter(parentinfo__fatherName='name of father')
You here thus obtain a QuerySet of StudentInfos which contains zero, one, or more StudentInfos where there is a related ParentInfo object where the fatherName field is, in this case 'Name of father'.
Note: It might be better to implement a ForeignKey in the opposite order, such that multiple students can refer to the same ParentInfo object. Right now, a ParentInfo object can refer to exactly one StudentInfo. If there are students with the same parents (so siblings), then you introduce data duplication in the database.

# You can use contains attribute on the field of model and your query can be like this
student = models.ParentInfo.objects.values('student__firstName', 'student__lastName').filter(fatherName__contains='your value')
print(student[0]['student__firstName'])
print(student[0]['student__lastName'])

Related

How to call a a field of one model A into another model B so that b can work as a view

I have created a model called Department, Course. Models are as follow
This is the model for departments and course
class Departments(models.Model):
Department_Id = models.IntegerField(primary_key=True)
Department_Name = models.CharField(max_length=200)
Department_Code = models.CharField(max_length=200)
class Course(models.Model):
Course_Id = models.IntegerField(primary_key=True)
Department_Id = models.ForeignKey(Departments, on_delete=models.CASCADE)
Course_Name = models.CharField(max_length=200)
Course_Code = models.CharField(max_length=200)
I want to create a model called view which can be later on called for search. I want a view model in a such a way that it consit of the data in concat form i.e. name= Department_name+ Course_Name
class View (models.model):
view_id= models.IntegerField(primary_key=True)
Name= Department_name(I want this from Departments table)
+ Course_Name(I want this from Course table)
I try using one to one relation . I would really appricate the help
It's not clear why you'd want to do that. It's never a good idea to duplicate data from one model into another one, as it can lead to inconsistencies.
You can add a ForeignKey in View to your Course model and then when you do f"{view.course.name} {view.course.department.name}" you already have your string:
class View(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
def name(self):
return f"{self.course.name} {self.course.department.name}"
Notes:
Don't call your foreign key Department_id because it's not referring to the id but to the object itself in the Django ORM: department = models.ForeignKey(Department, on_delete=models.CASCADE). As you can see, this makes reading the code much simpler: self.course.Department_id is a Department object not an integer, so self.course.department makes more sense.
Don't prefix your field names with the class, it just makes the code so much less readable: Do you prefer department.name or department.Department_name?
The View model is still a mystery to me, as you can search without it. You can search for example for courses with a matching department name like this:
Course.objects.filter(department__name__icontains="maths")
which will return all courses with "maths" in their department name.
Remove all the ids from your models, they are created automatically by Django anyway (and called id). Again, department.id is much easier to read than department.Department_id. Also in your code, you have to generate the ids yourself since you don't set them to auto-populate.

django query with prefetch_related

I am struggling to get at the data I need from a prefetch-related query.
I have a table of events (the calendar table), a table of members and an attendee table which links the two.
My models look like:
class Member(models.Model):
firstname = models.CharField(max_length=40)
lastname = models.CharField(max_length=50)
email = models.EmailField(blank=True, verbose_name ='e-mail')
phone = models.CharField(max_length=40)
membershipnum = models.CharField(max_length=40)
class Attendee(models.Model):
memberid = models.ForeignKey(Member, on_delete=models.SET(0), related_name="attendingmembers")
calendarid = models.ForeignKey(Calendar, on_delete=models.SET(0))
attended = models.BooleanField(default=0)
paid = models.BooleanField(default=0)
class Meta:
db_table = 'attendee'
For a particular event I want a list of attending members with the attended and paid fields from the attendee table.
In my view I have
attendees = Member.objects.filter(attendingmembers__calendarid_id=id).prefetch_related('attendingmembers')
I am getting the right members, but I don't know if this is the best way to do it? And I can't figure out how to get at the attendee fields.
If I do
for thisone in attendees:
print(thisone)
print(thisone.attendingmembers)
I get the expected return from the first print, but the second just gives me
myapp.Attendee.None
Any advice much appreciated.
You still need .all() to get the list of items of your relation:
for this one in attendees:
print(thisone.attendingmembers.all())
Btw, do you wish to get all attendingmembers or only the ones with the right calendar_id ?
attendees = Member.objects.filter(attendingmembers__calendar_id=id).prefetch_related('attendingmembers')
# return all Members having at least one attendingmember with calendar_id=id, and prefetch all of their attendingmembers
attendees = Member.objects.filter(attendingmembers__calendar_id=id).prefetch_related(Prefetch('attendingmembers', queryset=Attendee.objects.filter(calendar_id=id)))
# return all Members having at least one attendingmember with calendar_id=id, and prefetch their attendingmembers matching the filter
The documentation shows you how to use the to_attr argument in the Prefetch objects ;)

How to filter joined models in Django?

I have the following models:
class Street(models.Model):
name = models.CharField(max_length=40)
class House(models.Model):
street = models.ForeignKey(Street, models.PROTECT)
number = models.CharField(max_length=10)
class Meta:
unique_together = ('street', 'number')
class Room(models.Model):
house = models.ForeignKey(House, models.PROTECT)
number = models.CharField(max_length=10)
class Meta:
unique_together = ('house', 'number')
I already know the ID of a Street and would like to get all the rooms of all the houses in that street. In SQL that is easy:
SELECT *
FROM room JOIN house ON house.id = room.house_id
WHERE house.street_id = xyz;
Now how do I do this in Django? I tried
Room.objects.select_related('house').filter(street=xyz)
But I get an exception saying I can't access this field:
django.core.exceptions.FieldError: Cannot resolve keyword 'street' into field. Choices are: house, house_id, id, number
Because of the amounts of data I am facing, I would really like to be able to join and filter using a single query! When giving up one or the other, I would have to resort to either making multiple queries or filtering in Python, both of which are inherently inefficient. An alternative would be raw queries, I guess...
You can get access to related object's field with __ syntax:
Room.objects.select_related('house').filter(house__street=xyz)
This can be done as much time as you need, to select rooms by street name you can do this:
Room.objects.select_related('house').filter(house__street__name=xyz)
Chech details here.

Insert values in related models based on foreign key

I'm trying to insert values for related one to many models through a form. I'm having some difficulties.
I have three related models:
class Member(models.Model):
member_id = models.AutoField(primary_key=True)
first_name = models.CharField(max_length=50, default='test first name')
last_name = models.CharField(max_length=50, default='test last name')
class Loans (models.Model):
loan_id = models.AutoField(primary_key = True)
member = models.ForeignKey('Member',related_name='loans')
class Transactions (models.Model):
transaction_id = models.AutoField(primary_key=True)
amount = models.DecimalField(max_digits = 10, decimal_places = 2)
loans = models.ForeignKey('Loans', related_name='transactions')
Basically, one Member can have one to many Loans, one Loan can have one to many Transactions (although it should be 0 to many, but I'm just testing at the moment).
I have a form that asks for member's full name, loan id and amount.
Basically I'm trying to find the Member based on the full name, then get the loan based on the id provided, and then create a new transaction for that loan.
What I have done in my view:
fullName = request.GET.get('full_name')
loanID = request.GET.get('loan_id')
amount = request.GET.get('amount')
#to get first name
firstName = fullName.partition(' ')[0]
#to get last name
lastName = fullName.rsplit(None, 1)[-1]
#to get the member
member = Member.objects.filter(first_name=firstName, last_name = lastName)
#get member's loan based on loanID
l = Loans(loan_id = loanID)
t = Transactions.objects.create(amount = amount, transactions = l)
t.save()
I get an error when I submit the form:
'transactions' is an invalid keyword argument for this function
Am I using the correct approach but not understanding model relationships?
Or am I totally in the wrong track?
Any help/direction would be appreciated,
Thanks in advance
Try t = Transactions.objects.create(amount = amount, loans = l)
There's no transactions field in Transactions. There are amount and loans and loans is the Loans instance you got in l
It can also work as follows:
l = Loans.objects.get(loan_id = loanID)
t = Transactions.objects.create(loans = l,amount = amount, ,transaction_id=uuid.uuid4())
t.save()
And you can get transaction_id also as a primary key , which further you can use as foreign key for another table.

How to group ManyToMany in template

Here is my model:
class Teacher(models.Model):
name = models.CharField(max_length=50)
subjects = models.ManyToManyField(Subject)
class Subject(models.Model):
name = models.CharField(max_length=50)
type = models.ForeignKey(SubjectType)
class SubjectType(models.Model):
name = models.CharField(max_length=50)
I want to do something like that in template:
{regroup subjects by subject.type}
{foreach subject_type}
#display all teachers for subject_type
Each subject has its teacher_set.
How can i union all teachers for a particular subject_type?
I think you can't get this functionality from the template. However, you could do it in your view:
subject_type = # Some SubjectType object
my_teachers = []
for subject in subject_type.subject_set:
my_teachers += subject.teacher_set
# What we do with the duplicates?
my_teachers = list(set(my_teachers))
Now, you have a list of unique teachers in my_teachers for a particular SubjectType.
EDIT: You could do this for every SubjectType object and pass the resulting list (of lists) to the template.