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

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

Related

SQL to Queryset Django

i want to use this query in django, i have tried and read the django document but i still can't understand it
Sql to QuerySet Django
SELECT * FROM movie as m INNER JOIN similarity as s ON m.`movie_id` = s.first_movie WHERE `movie_id` = 1 ORDER BY `s`.`similarity_score` DESC
This is My model
from django.db import models
# Create your models here.
class Movie(models.Model):
movie_id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=200)
genres = models.CharField(max_length=200)
num_ratings = models.IntegerField(null=True)
rating_median = models.FloatField(null=True)
rating_mean = models.FloatField(null=True)
comparable = models.BooleanField(default=True)
liked = models.NullBooleanField(null=True, blank=True)
def __str__(self):
return self.title
class Similarity(models.Model):
first_movie = models.ForeignKey(Movie, related_name='first_movie', on_delete = 'CASCADE')
second_movie = models.ForeignKey(Movie, related_name='second_movie', on_delete = 'CASCADE')
similarity_score = models.FloatField()
class OnlineLink(models.Model):
movie = models.ForeignKey(Movie, on_delete = 'CASCADE')
imdb_id = models.CharField(max_length=50)
youtube_id = models.CharField(max_length=50, null=True)
tmdb_id = models.CharField(max_length=50, null=True)
You can use prefetch_related for forward foreign keys.
movies = Movie.objects.all().prefetch_related(
'first_movie'
).filter(
first_movie__id=1
).order_by(
"-similarity_score"
)
This will not create an SQL join but will do the join in Python. If you must have a join, you need to start from the other table like so:
similarities = Similarity.objects.filter(
first_movie__id=1
).order_by(
"-similarity_score"
).select_related(
'first_movie'
)
Using select_related does an inner join, so you don't hit the table every time you want to access a field from the Movie table.
See Django docs for prefetch_related and select_related.
See this StackOverflow post for prefetch_related and select_related.
A solution could be the following:
s = Similarity.objects.filter(first_movie__id=1).order_by("-similarity_score")
# and access to first_movie
for movie in s:
movie.first_movie

Django - Join 3 tables to get records

Need to join these 3 tables , can someone give me the ORM queryset . need to show the records on one template based on these three tables.
Models.py
class Item_Table(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=127)
description = models.TextField(null=True,blank=True)
qty_in_ferma_warehouse = models.IntegerField(null=True,blank=True,default=0)
status = models.BooleanField()
ListID = models.CharField(max_length=80, null=True,blank=True)
class Order_Table(models.Model):
id = models.AutoField(primary_key=True)
order_number = models.CharField(max_length=63)
notes = models.TextField()
created_at = models.DateField()
status = EnumField(Choice,max_length=2, null=True,blank=True)
Total_Amount = models.DecimalField(max_digits=18,decimal_places=2)
must_fill_in_items = models.IntegerField()
total_replenish_small_order = models.IntegerField()
order_type = EnumField(Choice3,max_length=2, null=True,blank=True)
delivery_method = EnumField(Choice6, max_length=2, null=True, blank=True)
class Orderitems_Table(models.Model):
id = models.AutoField(primary_key=True)
order_id = models.ForeignKey(Order_Table,on_delete='')
item_id = models.ForeignKey(Item_Table,on_delete='')
qty = models.IntegerField()
next_order_qty = models.IntegerField(null=True,blank=True,default=0)
Rate = models.DecimalField(max_digits=18,decimal_places=2)
Amount = models.DecimalField(max_digits=18,decimal_places=2)
status = EnumField(Choice2,max_length=2,null=True,blank=True)
accumulated_residual_item = models.IntegerField()
accumulated_damaged_item = models.IntegerField()
You can have a many to many field in Order_Table.
items = models.ManyToManyField(Person, through='Orderitems_Table')
then you can query on Order_Table model and have a queryset
you can go through the documentation to have more insights about ManyToMany Field and how to play with this
For forms and view you can follow this link

Concatenation foreign key in Django

I need a query to retrieve the name and id of the 4 model classes (P, B, M, C, Pt) with a concat.
The class Pt has the foreign key to the parents class by the id
The expected result should be like this: Pt.name, Product.num , concat(B.name, M.name, V.name)
I try several approaches and the more similar it's this one
Models:
class B(models.Model):
b_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
class M(models.Model):
m_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
class V(models.Model):
v_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
class Pt(models.Model):
pt_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
class Product(models.Model):
p_id = models.AutoField(primary_key=True)
p_ref_fk = models.DecimalField(max_digits=5, decimal_places=2, null=True)
pt_fk = models.ForeignKey(Pt, on_delete=models.CASCADE)
b_fk = models.ForeignKey(B, on_delete=models.CASCADE)
m_fk = models.ForeignKey(M, on_delete=models.CASCADE)
v_fk = models.ForeignKey(V, on_delete=models.CASCADE)
Query:
# Get all the P related
p = Product.objects.all().filter(pt_fk__pt_id=1116).filter(b_fk__b_id=3).filter(m_fk__m_id=53).filter(v_fk__v_id=352)
# check the query
print(p.query)
# get the results
print(p)
As per i get your question, you need to Concat foreignkey with - Pt.name, Product.num , concat(B.name, M.name, V.name)
queryset = Employee.objects.annotate(fullname=Concat('first_name', 'last_name'))
this is my own example please change as per your requirements
try this too

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))

How to use dynamic foreignkey in Django?

I want to connect a single ForeignKey to two different models.
For example:
I have two models named Casts and Articles, and a third model, Faves, for favoriting either of the other models. How can I make the ForeignKey dynamic?
class Articles(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
class Casts(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
class Faves(models.Model):
post = models.ForeignKey(**---CASTS-OR-ARTICLES---**)
user = models.ForeignKey(User,unique=True)
Is this possible?
Here is how I do it:
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import fields
class Photo(models.Model):
picture = models.ImageField(null=True, upload_to='./images/')
caption = models.CharField(_("Optional caption"),max_length=100,null=True, blank=True)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = fields.GenericForeignKey('content_type', 'object_id')
class Article(models.Model):
....
images = fields.GenericRelation(Photo)
You would add something like
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = fields.GenericForeignKey('content_type', 'object_id')
to Faves
and
fields.GenericRelation(Faves)
to Article and Cast
contenttypes docs
Here's an approach. (Note that the models are singular, Django automatically pluralizes for you.)
class Article(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
class Cast(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
FAVE_CHOICES = (
('A','Article'),
('C','Cast'),
)
class Fave(models.Model):
type_of_fave = models.CharField( max_length=1, choices=FAVE_CHOICES )
cast = models.ForeignKey(Casts,null=True)
article= models.ForeigKey(Articles,null=True)
user = models.ForeignKey(User,unique=True)
This rarely presents profound problems. It may require some clever class methods, depending on your use cases.

Categories