Django Models - SQL Equivalent multiple tables join in DJANGO - python

How to join 3 or more tables in django (ORM ) and fetch the results?
Have created 3 models:
1.student
2.marks
3.details
class Student:
s_id = models.IntegerField(primary_key=True)
s_name = models.CharField(max_length=100)
class Marks:
school_id = models.IntegerField(primary_key=True)
s_id = models.ForeignKey(Student, on_delete=models.CASCADE)
score = models.IntegerField()
status = models.CharField(max_length=30)
class Details:
address_city = models.CharField(max_length=30)
emailid = models.EmailField()
school_id = models.ForeignKey(Marks,on_delete=models.CASCADE)
accomplishments = models.TextField()
I need to join these 3 tables and fetch student name,score,status,address_city,email_id,accomplishments.
In SQL we can write like this:
select s_name, score, status, address_city, email_id,
accomplishments from student s inner join marks m on
s.s_id = m.s_id inner join details d on
d.school_id = m.school_id;
Please let me know how to achieve same thing using DJANGO codes.

Some Suggestions for given models:
models.py:
class Student:
name = models.CharField(max_length=100)
class Marks:
student = models.ForeignKey(Student, on_delete=models.CASCADE)
score = models.IntegerField()
status = models.CharField(max_length=30)
class Details:
address_city = models.CharField(max_length=30)
emailid = models.EmailField()
school = models.ForeignKey(Marks,on_delete=models.CASCADE)
accomplishments = models.TextField()
Now, Create your mapping model as we create mapping table in sql:
class StundetMarksDetailsMap:
student = models.ForeignKey(Student, on_delete=models.CASCADE)
marks = models.ForeignKey(Marks, on_delete=models.CASCADE)
details = models.ForeignKey(Details, on_delete=models.CASCADE)
Now you can create your own queryset as you want in views.py or where you want:
class StudentView(generic.DetailsView):
model = Student
template = student_details.html
//this will return all values related student
def get_queryset(self):
return StundetMarksDetailsMap.objects.filter(student=Student)
Basically this get_queryset returns objects of models which are related to stundet which was picked, use returned objects of Details and Marks to call their corresponding fields in your template. Also use only those fields which you want to display.
Hope this works for you too. All the best :)

I think you should keep it simple rather than complicating models which won't be great when you have a lot of data. Keep the student model as the main Class. And store all other info in other tables.
class Student:
name = models.CharField(max_length=100)
marks = models.ForeignKey(Marks,on_delete=models.CASCADE)
school = models.ForeignKey(School,on_delete=models.CASCADE)
class Marks:
score = models.IntegerField()
status = models.CharField(max_length=30)
class Details:
address_city = models.CharField(max_length=30)
emailid = models.EmailField()
accomplishments = models.TextField()
class School:
name = models.CharField(max_length=30)
details = models.ForeignKey(Details)
Now do the below query
student = Student.objects.filter(pk=student_id).filter(school_pk=school_id)
print student.name
print student.marks.score
print student.marks.status
print student.details.address_city
For your current models, it's difficult to query. But you can try writing SQL queries itself. Something like this
Blog.objects.extra(
select={
'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id'
},
)
Follow this
https://docs.djangoproject.com/en/2.1/ref/models/querysets/#extra

Related

Django Query Foreign Key and Many to many field

Struggling with Django query, need some help.
So I would like to get data for 5 different variables, accounts_data = (only username), user_account_data = (name), country_data = (iso_code), category_data = (choice), permisisons_data = (permissions).
All data of the models is connected with UserAccount table, so need to get data from other tables only for existing user_accounts
Example of models:
class Account(models.Model):
username = models.CharField(max_length=30)
password = models.CharField(max_length=50)
status = models.CharField(max_length=60)
class UserAccount(models.Model):
name= models.CharField(max_length=100)
surname = models.CharField(max_length=100)
category= models.ManyToManyField(Category)
account = models.ForeignKey(Account)
country = models.ForeignKey(Country)
permissions = models.ForeignKey(Permissions)
class Country(models.Model):
iso_code = models.CharField(max_length=6)
zip_code = models.CharField(max_length=10)
class Category(models.Model):
choice = models.CharField(max_length=12)
connected_category = models.CharField(max_length=12)
class Permissions(models.Model):
permission = models.CharField(max_length=50)
restriction = models.CharField(max_length=30)

How to filter a table based on data from another table

there are three tables:
class Course(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
start_date = models.CharField(max_length=255)
end_date = models.CharField(max_length=255)
def get_count_student(self):
count = CourseParticipant.objects.filter(course=self.id)
return len(count)
def __str__(self):
return f'{self.name}'
class Student(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
email = models.CharField(max_length=255)
def __str__(self):
return f'{self.first_name}'
class CourseParticipant(models.Model):
course = models.ForeignKey(Course, related_name='course', on_delete=models.CASCADE)
student = models.ForeignKey(Student, related_name='student', on_delete=models.CASCADE)
completed = models.BooleanField(default=False)
I want to get students who do not participate in specific courses, I fulfill the following request
potential = Student.objects.exclude(courseparticipants__course=pk)
where in pk I indicate the id of the course, in response I get:
django.core.exceptions.FieldError: Cannot resolve keyword 'courseparticipants' into field. Choices are: email, first_name, id, last_name, student
The related_name is the name that is used by the related model's reverse accessor and is also the default related query name. Hence when you write the following in CourseParticipant:
student = models.ForeignKey(Student, related_name='student', on_delete=models.CASCADE)
It means Student now will have a reverse accessor named student which can be used to access its related CourseParticipant instances. Looking at this you would realize that you have chosen an incorrect (at least semantically) value for the related_name. Hence with this related name your query should be:
potential = Student.objects.exclude(student__course=pk) # Very confusing, yes?
A better solution would be to change the related name to something more suitable:
student = models.ForeignKey(Student, related_name='course_participants', on_delete=models.CASCADE)
Now you can write your query as:
potential = Student.objects.exclude(course_participants__course=pk)
Since you did not specify related_name in CourseParticipant model it is set to courseparticipant_set by default, so your query should be:
potential = Student.objects.exclude(courseparticipant_set__course=pk)

Querying Django on a derived field

Using Django 1.8
I have an unmanaged model that points to a view in the db and I'm trying to return a queryset based on a derived field.
Here is a basic example of the model structure:
class Book(models.Model):
book_id = models.IntegerField()
publisher = models.CharField(max_length=20, null=True) # Can join to Publisher.publisher_name
sku = models.CharField(max_length=15, null=True) # Can join to Sku.sku
class Meta:
managed = False
db_table = 'vw_book'
class Sku(models.Model):
sku = models.CharField(max_length=15)
publisher = models.ForeignKey('myapp.Publisher')
class Publisher(models.Model):
publisher_name = models.CharField(max_lenth=20)
region = models.ForeignKey('myapp.Region')
class Region(models.Model):
region_name = models.CharField(max_length=20)
I'm looking for a way to return a queryset of Book based on the region, derived from the publisher as the preferred field and then the sku. It is possible for these fields in Book to refer to different region fields as the data is unlcean and has been derived from multiple sources. I can add a method to the Book model to derive the region, but trying to get a queryset from this is too slow.
class Book(models.Model):
publisher = models.CharField(max_length=20, null=True)
sku = models.CharField(max_length=15, null=True)
class Meta:
managed = False
db_table = 'vw_book'
def get_region(self):
if not self.publisher:
if not self.sku:
return ''
try:
sku = Sku.objects.get(sku=self.sku)
return sku.publisher.region.region_name
except Sku.DoesNotExist:
return ''
try:
publisher = Publisher.objects.get(publisher_name=self.publisher)
return publisher.region.region_name
except Publisher.DoesNotExist:
return ''
region_dict = {}
for book in Book.objects.all():
region_dict.setdefault(book.get_region(), []).append(book.book_id)
Book.objects.filter(book_id__in=region_dict.get('UK', []))
I am unable to add an extra field to the Book model. Is there a more efficient way I can do this?
I would filter the skus and then filter books based on the skus you receive
skus = Sku.objects.filter(publisher__region__region_name=region).values_list('sku', flat=True)
Book.objects.filter(sku__in=skus)
You can do the same with publishers if need be and do an Or query.
.filter(Q(publisher__in=publishers) |Q(sku__in=skus))

Django: How to join tables based on a lookup tables' value

I am trying to do what one might consider an advanced sql query and would like to know if its possible in Django without resorting to raw sql (I will if its necessary).
I want to join 1 or another table based on a value located in a table lookup table and would like to do this entirely in python/django.
The following are rough examples of the models I am using:
class SpecificProduct(models.Model):
specific_product_id = models.AutoField(primary_key=True)
a_field = models.TextField()
something_specific_to_this_model = models.CharField()
class GeneralProduct(models.Model):
other_product_id = models.AutoField(primary_key=True)
text = models.TextField()
TABLE_CATEGORIES = {
1 : SpecificProduct,
2 : GeneralProduct,
}
class ProductCategory(models.Model):
category_id = models.AutoField(primary_key=True)
table_category = models.IntegerField() # Technically represents a table.
category_text = models.CharField(max_length=20)
class Inventory(models.Model):
inventory_id = models.AutoField(primary_key=True)
product_category = models.ForeignKey(ProductCategory, on_delete=models.CASCADE)
product_pk = models.IntegerField() # Technically foreign key to a product table.
quantity = models.IntegerField()
What I want is a method like:
def get_product(category_id, product_pk):
# SQL query magic
return one_object_of_a_specific_product_type
This method should be able to do things like...
Give me the product (model) where the product_category = 1 and the
product_pk = 1. (returns a SpecificProduct model)
Give me the product where product_category = 2 and the product_pk = 50
(returns a GeneralProduct model)
How do you do this query in Django and is this even possible?
Edit:
Based on Kireeti K's response I have created models that look like the following:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
class SpecificProduct(models.Model):
specific_product_id = models.AutoField(primary_key=True)
specific_text = models.TextField()
class GeneralProduct(models.Model):
general_product_id = models.AutoField(primary_key=True)
text = models.TextField()
class ProductCategoryLookup(models.Model):
category_id = models.ForeignKey(ContentType, on_delete=models.CASCADE, primary_key=True)
category_text = models.CharField(max_length=20)
class Inventory(models.Model):
inventory_id = models.AutoField(primary_key=True)
product_category = models.ForeignKey(ContentType, on_delete=models.CASCADE)
product_id = models.PositiveIntegerField()
product = GenericForeignKey('product_category', 'product_id')
quantity = models.IntegerField()
def get_product(category_id, product_pk):
content_type = ContentType.objects.get(id=category_id)
inventory = Inventory.objects.get(product_category=content_type, product_id=product_pk).first()
return inventory.product
You can use a generic-foreign-key to get foreign-key relation with any model sort of dynamically, read about it here. https://docs.djangoproject.com/en/2.1/ref/contrib/contenttypes/#generic-relations
If you rewrite your models using generic-foreign-key then it looks something like this.
class ProductCategory(models.Model):
category_id = models.AutoField(primary_key=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
table_category = models. GenericForeignKey() # Technically represents a table.
category_text = models.CharField(max_length=20)
class Inventory(models.Model):
inventory_id = models.AutoField(primary_key=True)
product_category = models.ForeignKey(ProductCategory,
on_delete=models.CASCADE)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
product = models. GenericForeignKey() # Technically foreign key to a product table.
quantity = models.IntegerField()
Now to achieve what you want, you can implement your function like this.
def get_product(model=None, category_id, product_pk):
model = "specificproduct" if model else "generalproduct"
content_type = ContentType.objects.get(model=model)
inventory = Inventory.objects.get(product_category_id=category_id, object_id=product_pk, content_type=content_type)
return inventory.product

Models in Python Django not working for Many to Many relationships

I am trying to create the proper Django model that could fit the following reqs:
Person Class has 1 to many relations with the Address Class
Person Class has many to many relations with the Group Class
Book Class contains the collections of the Persons and the Groups
This is my code:
class Person(models.Model):
first_name = models.CharField(max_length=15)
last_name = models.CharField(max_length=20)
def __str__(self):
return self.first_name+ ' - ' + self.last_name
class Address(models.Model):
person = models.ForeignKey(Person)
address_line = models.CharField(max_length=200)
def __str__(self):
return self.address_line
class Group(models.Model):
group_name = models.CharField(max_length=12)
persons = models.ManyToManyField(Person)
def __str__(self):
return self.group_name
class Book(models.Model):
record_name = models.CharField(max_length=12)
person = models.ForeignKey(Person )
group = models.ForeignKey(Group )
def __str__(self):
return self.record_name
However it's not correct:
1) A Group can now contain multiple Persons but the Persons do not contain any Group.
I am not sure if I should add to the Person class the following code:
groups = models.ManyToManyField(Group)
2) The Book class now contains only 1 record of Person & Group per Book record.
3) When I added the Foreign Keys to the models, I removed
on_delete tag:
person = models.ForeignKey(Person, on_delete=models.CASCADE())
because it does not compile it, asking for some params.
I know how to make all this for C#, but I am a kinda stucked with this simple task in Python/Django.
1) The ManyToMany field should appear only in one of the models, and by looks of things you probably want it in the Person model.
Its important to understand that the data about the ManyToMany field is saved in a differant table. Django only allows this field to be visable through buth models (so basiclly, choose where it is move convinient).
2)By the look of your structure I will suggest you use a ManyToMany field through a different table. here is an example:
class Activity(models.Model):
name = models.CharField(max_length=140)
description = models.TextField(blank=True, null=True)
class Route(models.Model):
title = models.CharField(max_length=140)
description = models.TextField()
activities_meta = models.ManyToManyField(Activity, through = 'RouteOrdering')
class RouteOrdering(models.Model):
route = models.ForeignKey(Route, on_delete=models.CASCADE)
activity = models.ForeignKey(Activity, on_delete=models.CASCADE, related_name='activita')
day = models.IntegerField()
order = models.IntegerField(default=0)
that way the data is binded to the ManyToMany field

Categories