Django complex filter with two tables - python

I am in need of some help. I am struggling to figure this out.
I have two models
class Orders(models.Model):
order_id = models.CharField(primary_key=True, max_length=255)
channel = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return str(self.order_id)
class Meta:
managed = False
db_table = 'orders'
class OrderPaymentMethods(models.Model):
id = models.CharField(primary_key=True, max_length=255)
payment_type = models.CharField(max_length=75, blank=True, null=True)
fk_order = models.ForeignKey('Orders', models.DO_NOTHING, blank=True, null=True)
class Meta:
managed = False
db_table = 'order_payment_methods'
My goal is to count the number of orders that have a OrderPaymentMethods specific payment_type
Example:
orders = Orders.object.filter(Q(channel="Green_House"))
method_money = orders.filter(payment_methods = "credit").count()
How can I get the count based on the orders that were filtered?
Thank You

You don't need Q nor separating such query. You need to use relation lookup:
Orders.object.filter(channel="Green_House", orderpaymentmethods_set__payment_type="credit").count()

Related

Django: Select count of foreign key field

I have the following models:
class SystemTable(models.Model):
id = models.FloatField(primary_key=True)
system_name = models.CharField(max_length=50)
system_desc = models.CharField(max_length=200, blank=True, null=True)
system_url = models.CharField(max_length=2000, blank=True, null=True)
status = models.ForeignKey(StatusLkp, models.DO_NOTHING, db_column='status',default=1)
date_created = models.DateTimeField(default=datetime.now())
objects = models.Manager()
class Meta:
app_label = 'system_table'
managed = True
db_table = 'system_table'
class SystemPagesTable(models.Model):
id = models.FloatField(primary_key=True)
system = models.ForeignKey(SystemTable, on_delete=models.CASCADE)
page_name = models.CharField(max_length=200)
page_link_xpath = models.CharField(max_length=2000)
flag = models.FloatField(blank=True, null=True)
status = models.ForeignKey(StatusLkp, models.DO_NOTHING, db_column='status',default=1)
date_created = models.DateTimeField(default=datetime.now())
objects = models.Manager()
class Meta:
app_label = 'system_pages_table'
managed = True
db_table = 'system_pages_table'
I want to perform the following SQL query:
select
s.*,
(select count(*) from page p where p.system_id = s.id) as NUM_OF_PAGES
from system s;
How do I perform the above query with Django's ORM without having to use a for loop?
I do not want a result based on one system, I want to retrieve all systems with their page counts.
Try to use annotate:
from django.db.models import Count
System.objects.annotate(num_pages=Count('page'))

How to save/create thousands of records to postgres using queryset in Django?

I want to store thousands of records in the Student table which has id_users foreign key field. I tried without having foreign key it works smooth but when I added fk constraint it becomes very very slow and took more than 20mins to store 500 records and same without fk took only few seconds.
models - Below models are from different apps
./users/models.py
class Users(models.Model):
id = models.IntegerField(primary_key=True)
cts = models.DateTimeField(blank=True, null=True)
uts = models.DateTimeField(blank=True, null=True)
id_states = models.ForeignKey(States, models.DO_NOTHING, db_column='id_states', blank=True, null=True)
id_user_type = models.ForeignKey(UserType, models.DO_NOTHING, db_column='id_user_type')
firstname = models.TextField(blank=True, null=True)
lastname = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'users'
./student/models.py
class Student(models.Model):
id = models.BigAutoField(primary_key=True)
cts = models.DateTimeField()
uts = models.DateTimeField()
id_users = models.ForeignKey('users.Users', models.DO_NOTHING, db_column='id_users')
num_subjects = models.IntegerField()
num_teachers = models.IntegerField()
class Meta:
managed = False
db_table = 'students'
bulk_create() method -
def saveCounts(cumulative_counts: pd.DataFrame, model: django.db.models.base.ModelBase) -> None:
records = cumulative_counts.to_dict('records')
model_instances = [model(
cts=timezone.now(),
uts=timezone.now(),
id_users=Users.objects.get(
id=record['id_users']),
num_subjects=record['num_subjects'],
num_teachers=record['num_teachers'],
) for record in records]
model.objects.bulk_create(model_instances)

Trying to convert Left Join of SQL into Django query-set?

here is my models.py file
class Customer(models.Model):
"""All Customers details goes here"""
name = models.CharField(max_length=255, null=False)
firm_name = models.CharField(max_length=255, null=False)
email = models.EmailField(null=False)
phone_number = models.CharField(max_length=255, null=False)
location = models.CharField(max_length=255,null=True)
date_created = models.DateTimeField(auto_now_add=True)
class Meta:
"""Meta definition for Customer."""
verbose_name = 'Customer'
verbose_name_plural = 'Customers'
def __str__(self):
"""Unicode representation of Customer."""
return self.name
class Order(models.Model):
"""All order details goes here.It has OneToMany relationship with Customer"""
STATUS = (
('CR', 'CR'),
('DR', 'DR'),
)
customer = models.ForeignKey(Customer, null=True, on_delete=models.SET_NULL)
bill_name = models.CharField(max_length=255, null=False)
payment_date=models.DateField(auto_now=False)
status = models.CharField(max_length=255, choices=STATUS, null=False)
amount = models.FloatField(max_length=255, null=False)
date_created = models.DateTimeField(auto_now_add=True)
description = models.TextField(null=True)
class Meta:
"""Meta definition for Order."""
verbose_name = 'Order'
verbose_name_plural = 'Orders'
def __str__(self):
"""Unicode representation of Order."""
return self.bill_name
i want to access only Customer's name and all fields of Order,in short i want to convert the following SQL in Django Query-set
select name ,bill_name ,status from accounts_customer left join
accounts_order on accounts_customer.id = accounts_order.customer_id
where accounts_order.status="DR";
To attach the customer's name on the order object, you can use annotate with an F expression. https://docs.djangoproject.com/en/3.0/ref/models/expressions/#using-f-with-annotations
orders = Order.objects.annotate(
customer_name=F('customer__name')
).filter(status='DR')
for order in orders:
print(order.customer_name)
If you suspect you will want to access more customer attributes, you may want to select_related (slightly more memory, larger query). What's the difference between select_related and prefetch_related in Django ORM?
orders = Order.objects.select_related('customer').filter(status='DR')
for order in orders:
print(order.customer.name)
You can perform join operation using two ways:
1st: via using select_related
I.e. Order.objects.select_related('customer')
And
2nd: via using filter:
I.e. Order.objects.filter(status__iexact="DR")

Query using Joins in Django

class Students(models.Model):
id = models.BigAutoField(primary_key=True)
admission_no = models.CharField(max_length=255)
roll_no = models.CharField(unique=True, max_length=50, blank=True, null=True)
academic_id = models.BigIntegerField()
course_parent_id = models.BigIntegerField()
course_id = models.BigIntegerField()
first_name = models.CharField(max_length=20)
middle_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
user_id = models.BigIntegerField()
date_of_birth = models.DateField(blank=True, null=True)
date_of_join = models.DateField(blank=True, null=True)
class Courses(models.Model):
id = models.BigAutoField(primary_key=True)
parent_id = models.IntegerField()
course_title = models.CharField(max_length=50)
slug = models.CharField(unique=True, max_length=50)
tenant_user = models.ForeignKey('Users', models.DO_NOTHING, default='')
course_code = models.CharField(max_length=20)
course_dueration = models.IntegerField()
grade_system = models.CharField(max_length=10)
is_having_semister = models.IntegerField()
is_having_elective_subjects = models.IntegerField()
description = models.TextField()
status = models.CharField(max_length=8)
created_at = models.DateTimeField(blank=True, null=True)
updated_at = models.DateTimeField(blank=True, null=True)
class Meta:
managed = True
db_table = 'courses'
def __unicode__(self):
return self.course_title
class StudentProfileSerializer(ModelSerializer):
class Meta:
model = Students
depth = 0
fields = '__all__'
The first two tables/class contains the course and student table and the third contains the serializer. Can anyone please help how to query using the joins in django. I need to fetch the course_title from Courses table and first_name from Students table.
IMHO, you should review your models; course_id in Students should be a course=models.ForeignKey('Courses', ...); this way you can refer to the course title using dot notation;
student=Student.objects.filter(pk=...)
to refer to your required fields:
student.last_name, student.course.course_title
Besides, if I understood your models, you could get some incongruence... what if the value stored in course_parent_id in Students model is different from the value stored in parent_id in Courses model? maybe the first one is redundant.
To query a field from a related object use a double underscore. So you could do
Student.objects.filter(**kwargs).values('first_name', 'last_name', 'course__course_name')

Pulling Data From Multiple Tables in Django

I'm kind of new to Django and am having some trouble pulling from existing tables. I'm trying to pull data from columns on multiple joined tables. I did find a solution, but it feels a bit like cheating and am wondering if my method below is considered proper or not.
class Sig(models.Model):
sig_id = models.IntegerField(primary_key=True)
parent = models.ForeignKey('self')
state = models.CharField(max_length=2, db_column='state')
release_id = models.SmallIntegerField(choices=releaseChoices)
name = models.CharField(max_length=255)
address = models.CharField(max_length=255, blank=True)
city = models.CharField(max_length=255, blank=True)
zip = models.CharField(max_length=10, blank=True)
phone1 = models.CharField(max_length=255, blank=True)
fax = models.CharField(max_length=255, blank=True)
email = models.EmailField(max_length=255, blank=True)
url = models.URLField(max_length=255, blank=True)
description = models.TextField(blank=True)
contactname = models.CharField(max_length=255, blank=True)
phone2 = models.CharField(max_length=255, blank=True)
ratinggroup = models.BooleanField()
state_id = models.ForeignKey(State, db_column='state_id')
usesigrating = models.BooleanField()
major = models.BooleanField()
class Meta:
db_table = u'sig'
class SigCategory(models.Model):
sig_category_id = models.IntegerField(primary_key=True)
sig = models.ForeignKey(Sig, related_name='sigcategory')
category = models.ForeignKey(Category)
class Meta:
db_table = u'sig_category'
class Category(models.Model):
category_id = models.SmallIntegerField(primary_key=True)
name = models.CharField(max_length=255)
release_id = models.SmallIntegerField()
class Meta:
db_table = u'category'
Then, this was my solution, which works, but doesn't quite feel right:
sigs = Sig.objects.only('sig_id', 'name').extra(
select = {
'category': 'category.name',
},
).filter(
sigcategory__category__category_id = categoryId,
state_id = stateId
).order_by('sigcategory__category__name', 'name')
Now since the items in filter() join the sigcategory and category models, I was able to pull category.name out by using extra(). Is this a proper way of doing this? What if I did not have the reference in filter() and the join did not take place?
SigCategory has a ForeignKey pointing at Category, so you can always get from the SigCategory to the Category simply by doing mysigcategory.category (where mysigcategory is your instance of SigCategory.
If you haven't previously accessed that relationship from that instance, doing it here will cause an extra database lookup - if you're concerned about db efficiency, look into select_related.

Categories