How to create a django queryset object for inner join? - python

I have two tables.
class DibbsSpiderDibbsMatchedProductFieldsDuplicate(models.Model):
nsn = models.TextField()
nsn2 = models.TextField()
cage = models.TextField()
part_number = models.TextField()
company_name = models.TextField(blank=True, null=True)
supplier = models.TextField(db_column='Supplier', blank=True, null=True) # Field name made lowercase.
cost = models.CharField(db_column='Cost', max_length=15, blank=True, null=True) # Field name made lowercase.
list_price = models.CharField(db_column='List_Price', max_length=15, blank=True, null=True) # Field name made lowercase.
gsa_price = models.CharField(db_column='GSA_Price', max_length=15, blank=True, null=True) # Field name made lowercase.
hash = models.TextField()
nomenclature = models.TextField()
technical_documents = models.TextField()
solicitation = models.CharField(max_length=32)
status = models.CharField(max_length=16)
purchase_request = models.TextField()
issued = models.DateField()
return_by = models.DateField()
file = models.TextField()
vendor_part_number = models.TextField()
manufacturer_name = models.TextField(blank=True, null=True)
product_name = models.TextField(blank=True, null=True)
unit = models.CharField(max_length=15, blank=True, null=True)
class Meta:
managed = False
db_table = 'dibbs_spider_dibbs_matched_product_fields_duplicate'
class DibbsSpiderSolicitation(models.Model):
line_items = models.IntegerField()
nsn = models.TextField()
nomenclature = models.TextField()
technical_documents = models.TextField()
purchase_request = models.TextField()
class Meta:
managed = False
db_table = 'dibbs_spider_solicitation'
What will be the equivalent django query for the inner join of two tables on the column nsn?
My views function will be like
def inner(request,nsn):
u_m = DibbsSpiderDibbsMatchedProductFieldsDuplicate.objects.filter(nsn2__icontains=id)
c_m = DibbsSpiderSolicitation.objects.filter(nsn__icontains=id)
obj = .......................
context = {'obj':obj}
return render(request,,"a.html",context)
the queryset should return the combination of two tables according to the common nsn.
the obj should return the combination of u_m and c_m. If u_m contains only one rows and c_m contains many rows then the obj must replicate the values of u_m.

You can try some of the options:
Adding foreign key constraint and use select_related as per this post
Raw query as mentioned in this stackoverflow post and another post with custom joins
3. Using IN query as per the following logic:
DibbsSpiderDibbsMatchedProductFieldsDuplicate.objects.filter(
nsn2__in=DibbsSpiderSolicitation.objects.filter(nsn__icontains='text_to_search').values('origin'))

First, great model names. Let's alias them:
DibbsSpiderDibbsMatchedProductFieldsDuplicate is Apples; DibbsSpiderSolicitation is Oranges
inner_qs = Apples.objects.all().extra(
tables=("yourapp_oranges",),
where=("yourapp_apples.nsn=yourapp_oranges.nsn",),
)
The documentation mentions that this api will be deprecated:
https://docs.djangoproject.com/en/4.0/ref/models/querysets/#extra

Option #1 - Introduce a ForeignKey (Recommended):
Under the class DibbsSpiderDibbsMatchedProductFieldsDuplicate add:
fkey = models.ForeignKey('DibbsSpiderSolicitation')
then you can easily access their join:
obj = DibbsSpiderDibbsMatchedProductFieldsDuplicate.Objects.filter(fkey__nsn).select_related()
Now it is your choice what you wish to do with nsn2
Option #2 - without a ForeigKey:
Raw SQL:
obj = DibbsSpiderDibbsMatchedProductFieldsDuplicate.objects.extra(where = ['''SELECT *
FROM DibbsSpiderSolicitation
INNER JOIN DibbsSpiderDibbsMatchedProductFieldsDuplicate
ON DibbsSpiderSolicitation.nsn = DibbsSpiderDibbsMatchedProductFieldsDuplicate.nsn2;'''])
# or
obj = DibbsSpiderDibbsMatchedProductFieldsDuplicate.objects.raw('''SELECT *
FROM DibbsSpiderSolicitation
INNER JOIN DibbsSpiderDibbsMatchedProductFieldsDuplicate
ON DibbsSpiderSolicitation.nsn = DibbsSpiderDibbsMatchedProductFieldsDuplicate.nsn2;''')
Using filter:
obj = DibbsSpiderSolicitation.objects.filter(nsn__in=DibbsSpiderDibbsMatchedProductFieldsDuplicate.objects.nsn2)
Sorry, I was not able to test any.

Related

How to get table data (including child table and sub child data) based on id which obtains from another table data? Django

views
company = Company.objects.get(id = company_id) # getting input from django urls (<int:company_id>)
vehicles = CompanyContainVehicles.objects.filter(company_id=company.id) # Give all rows having same id (company.id)
all_vehicles = Vehicle.objects.filter(companies=company) # Gives all row with id provide by company
all_vehicles_parts = VehiclePart.objects.filter(__________) # Not Working
models
class Company(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(blank=True, null=True, unique=True)
description = models.TextField()
class Vehicle(models.Model):
vehicle_number = models.IntegerField()
name = models.CharField(max_length=255)
slug = models.SlugField(blank=True, null=True, unique=True)
companies = models.ManyToManyField(
Company,
through='CompanyVehicle',
related_name='companies'
)
class CompanyVehicle(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class VehiclePart(models.Model):
id = models.AutoField(primary_key=True)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE)
type = models.ForeignKey(PartType, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
How do I get VehiclePart's with their Vehicle? (I think I will give all the data in a variable and we should divide it and add it with their Vehicle). Also, what can we do to access data if VehiclePart contains a child class named VehiclePartDetail?
I think I will give all the data in a variable and we should divide it and add with their Vehicle.
You don't have to. Django can read ForeignKey relations in reverse. You can query with:
qs = Vehicle.objects.prefetch_related('vehiclepart_set')
then you can enumerate over the queryset, and for each Vehicle object, access this with .vehiclepart_set.all(). For example:
for item in qs:
print(vehicle_name)
for part in item.vehiclepart_set.all():
print(part.id)

Django ORM multiple inner join in query

I want to be able to do queries involving multiple inner joins using Django ORM, here's my model (showing only relevant fields)
class Students(models.Model):
class Status(models.IntegerChoices):
preRegistered = 0 #No ha aceptado terminos y condiciones
Enabled = 1
Disabled = 2
Suspended = 3
Test = 4
id = models.AutoField(primary_key=True)
user = models.ForeignKey(Users, on_delete=models.CASCADE)
trainingPath = models.ForeignKey(trainingPaths, on_delete=models.CASCADE)
status = models.IntegerField(choices=Status.choices, default=0)
creationDate = models.DateTimeField(auto_now_add=True)
modificationDate = models.DateTimeField(auto_now=True)
class Meta():
db_table = 'Students'
class trainingPaths(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=70, blank=False, null=False)
shortName = models.CharField(max_length=10, blank=True)
creationDate = models.DateTimeField(auto_now_add=True)
modificationDate = models.DateTimeField(auto_now=True)
class Meta():
db_table = 'Training_Path'
class Courses(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=70, blank=False, null=False)
path = models.URLField(max_length=500, blank=True, null=True)
shortName = models.CharField(max_length=6, blank=True)
creationDate = models.DateTimeField(auto_now_add=True)
modificationDate = models.DateTimeField(auto_now=True)
course_image = models.URLField(max_length=200, blank=True)
class Meta():
db_table = 'Courses'
class CoursesXTrainingP(models.Model):
id = models.AutoField(primary_key=True)
trainingPath = models.ForeignKey(trainingPaths, on_delete=models.CASCADE)
course = models.ForeignKey(Courses, on_delete=models.CASCADE)
alternativeName = models.CharField(max_length=70, blank=True)
order = models.PositiveIntegerField(blank=False)
creationDate = models.DateTimeField(auto_now_add=True)
modificationDate = models.DateTimeField(auto_now=True)
class Meta():
db_table = 'Courses_X_Training_Paths'
I want to get the information of the courses that a student has according to the value of the "trainingPath".
this is my SQL query
select
courses.id, courses.`name`, courses.course_image
from
students
join
courses_x_training_paths
on
students.trainingPath_id = courses_x_training_paths.trainingPath_id
join
courses
on
courses_x_training_paths.course_id = courses.id
where
students.trainingPath_id=1;
I have tried several ways and none of them have worked, could you please help me?
You can filter with:
Courses.objects.filter(
coursesxtrainingp__trainingPath_id=1
)
The join on the Students model is not necessary, since we already know that the trainingPath_id is one by filtering on the CoursesXTrainingP model.
Note: normally a Django model is given a singular name, so Student instead of Students.
Note: normally the name of the fields in a Django model are written in snake_case, not PascalCase, so it should be: modification_date instead of modificationDate.

How to get first N rows from every category in Django

I have following models, with many to many table, for which I would like to get first 20 news from every category in single response.
class Category(models.Model):
code = models.CharField(primary_key=True, max_length=45)
name = models.CharField(max_length=200, blank=True, null=True)
is_active = models.TextField(blank=True, null=True) # This field type is a guess.
class Meta:
managed = False
db_table = 'category'
verbose_name_plural = 'Categories'
class News(models.Model):
source_code = models.CharField(max_length=45, blank=True, null=True)
title = models.CharField(max_length=1000, blank=True, null=True)
image = models.CharField(max_length=2000, blank=True, null=True)
link = models.CharField(max_length=1000, blank=True, null=True)
published_at = models.DateTimeField(blank=True, null=True)
scraped_at = models.DateTimeField(blank=True, null=True)
is_active = models.TextField(blank=True, null=True) # This field type is a guess.
categories = models.ManyToManyField('Category', through='NewsCategory')
class Meta:
managed = False
db_table = 'news'
verbose_name_plural = 'News'
class NewsCategory(models.Model):
news_id = models.ForeignKey(News, db_column='news_id', on_delete=models.CASCADE)
category_code = models.ForeignKey(Category, db_column='category_code', on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'news_category'
unique_together = (('news_id', 'category_code'),)
verbose_name_plural = 'NewsCategory'
My view class looks like this, and here I would like to add some logic to return 20 rows for each category, for example if I have 5 categories it should return 100 news in single request.
class NewsViewSet(viewsets.ModelViewSet):
http_method_names = ['get']
serializer_class = NewsSerializer
def get_queryset(self):
queryset = News.objects.all().order_by('-published_at')
sources = self.request.query_params.getlist('sources')
if len(sources) > 0:
queryset = queryset.filter(source_code__in=sources)
return queryset
The typical way to do this is to use a window function. Django has support for them but I don't think they allow filtering on the output of them. I think this is further complicated by the m2m field. Given it's not too complex and doesn't seem to involve user input, you might just want to use a raw query.
Here's what it might look like:
SELECT *
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY c.code ORDER BY n.published_at DESC) AS row_num
FROM appname_news n
JOIN appname_newscategory nc
ON n.id = c.news_id
JOIN appname_category c
ON nc.category_code = c.code
) sub
WHERE
row_num <= 20
And see here for Django's guide on how to actually implement this in a view:
https://docs.djangoproject.com/en/3.2/topics/db/sql/#executing-custom-sql-directly

How to make an inner join query with same table using Django's ORM

The query to be implemented with ORM is as follows,
SELECT t2.*
FROM sub_menu AS t1
INNER JOIN sub_menu AS t2 ON (t1.sub_menu_id = t2.parent_sub_menu_id)
WHERE t1.sub_menu_id = 1;
The model is as follows,
class SubMenu(models.Model):
sub_menu_id = models.AutoField(primary_key=True)
menu = models.ForeignKey('commons.MainMenu', related_name='sub_menus', on_delete=models.CASCADE)
parent_sub_menu_id = models.IntegerField(blank=True, null=True)
name = models.CharField(max_length=50)
en_name = models.CharField(max_length=50, blank=True)
ord = models.IntegerField()
api = models.CharField(max_length=255, blank=True)
api_method = models.CharField(max_length=7, blank=True)
api_detail = models.CharField(max_length=255, blank=True)
menu_type_cd = models.CharField(max_length=5, blank=True)
menu_auth_type_cd = models.CharField(max_length=5)
is_common = models.BooleanField(default=False)
is_ns = models.BooleanField(default=False)
spc_auth = models.BooleanField(default=False)
spc_auth_cd = models.CharField(max_length=5, blank=True)
create_dt = models.DateTimeField(auto_now_add=True)
update_dt = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'sub_menu'
unique_together = ('api', 'api_method',)
Not using a raw method, Is it possible to implement with Django's ORM?
Thank you.
You should do the relationship correctly on your model: https://docs.djangoproject.com/en/3.0/ref/models/fields/#module-django.db.models.fields.related. Then the parent_sub_menu should be:
class Submenu:
parent_sub_menu = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
Then run generate & DB migration. The query below should work.
And never declare relationship like you are doing right now, use Model instead via the documentation I sent.
Django does it for you already. You can just filter the related field.
https://docs.djangoproject.com/en/3.0/topics/db/queries/#lookups-that-span-relationships
SubMenu.objects.filter(parent_sub_menu__sub_menu_id=1)

Django with python3, results of sql query in selectbox

I have 2 models:
PR_Components (models.Model):
companyID = models.ForeignKey(PO_Company, blank=True, null=True)
comp_nr = models.CharField (max_length=5, blank=True, null=True)
def __str__(self):
return self.comp_nr
PR_ComponentsData (models.Model):
compID = models.ForeignKey (PR_Components, blank=False, null=True)
valid = models.DateField (max_length=10, blank=False, null=True)
comp_image = models.ImageField (upload_to="/images", blank=True, null=True)
comp_text = models.CharField (max_length=30, blank=False, null=True)
....
I want to show now in a selectbox the components number (PR_Components.comp_nr) and their current valid name (PR_Componentsdata.comp_text).
I added a manager to model PR_Components which executes a sql-query.
SELECT a.*, b1.* FROM pool_pr_components a
JOIN pool_pr_componentsdata b1 ON (a.id = b1.compID_id)
LEFT OUTER JOIN pool_pr_componentsdata b2
ON (a.id = b2.compID__id AND (b1.valid < b2.valid OR b1.valid = b2.valid
AND b1.id < b2.id)) WHERE b2.id IS NULL
Later I write forms dynamicly and add the sql-result to the field:
self.fields[field].queryset = sql_result
Until here, everything works fine.
My problem:
In the selectbox the result of the str-Method of model PR_Components is shown (=comp_nr), but I would like to show also the component name like "Component (001)".
How could I do this? It should be a solution which works for other models too, because a lot of my models have "historical" data.
Thanks a lot
Solution:
in model PR_Components we overwrite str with
def __str__(self):
related = PR_ComponentsData.objects.filter(id=self.id).last()
return "{} ({})".format (related.comp_text, self.comp_nr)

Categories

Resources