django inner join same column different results - sql

I think I may have a solution here -
Django query where in
It's basically approaching my query differently than I have posted below (I hope).
Coming from an SQL background I have a query that looks like so (updated)-
SELECT a.jobmst_name AS Parent, b.jobmst_name as Job from jobmst a
inner join jobmst b on b.jobmst_prntid = a.jobmst_id
WHERE (a.jobmst_name = 'Dept' and a.jobmst_prntid IS NULL) OR b.jobmst_prntname LIKE '\Dept\%' AND b.jobmst_dirty <> 'X'
UNION
SELECT jobmst_prntname as Parent, jobmst_name as Job FROM jobmst
WHERE (jobmst_name = 'Dept' AND jobmst_prntid IS NULL)
Which will return a list like the following -
Parent Job
NULL Dept
01. Dept_sub01 01.01 Trade Recon
02. Dept_sub02 02.04 Dept_sub02.04
02.04 Dept_sub02.04 02.04.02 Dept_sub02.04
02.04 Dept_sub02.04 02.04.04 Dept_sub02.04
02.04 Dept_sub02.04 02.04.05 Dept_sub02.04
02.04.01 Dept_sub02.04.01 02.04.01.01 Dept_sub02.04.01
02.04.01 Dept_sub02.04.01 02.04.01.02 Dept_sub02.04.01
Dept 01. Dept_sub01
Dept 02. Dept_sub02
Dept 03. Dept_sub03
How do I do the following in Django? The UNION is the easy part so ignore that part of the query. The important part is as follows -
The jobmst_id is the primary key of the table. The jobmst_prntid is the the same as the jobmst_id but not in the same row (as seen above).
I'm trying to generate json so that I can do a heirarchy tree but to do that I want my json to be setup like -
{parent_name: Dept, name: Dept01234}, {parent_name: Dept, name: Dept53452}
I've done inner joins on a table but the issue is that I want to tell it that the jobmst_id and jobmst_prntid are both going to give me a value from jobmst_name and it's going to be a different value on each key.
Hope this makes sense.
UPDATE - Found this which explains exactly what I'm trying to do -
django self join query using aliases
But it doesn't necessarily explain how to get it to essentially show 'jobmst_name', 'jobmst_name' while giving me the Parent name on one and the child name on the other even though I'm querying the same column.
EDIT - Here is the model I'm dealing with.
class Jobmst(models.Model):
jobmst_id = models.IntegerField(primary_key=True)
jobmst_type = models.SmallIntegerField()
jobmst_prntid = models.IntegerField('self', null=True, blank=True)
jobmst_active = models.CharField(max_length=1, blank=True)
evntmst_id = models.IntegerField(blank=True, null=True)
jobmst_evntoffset = models.SmallIntegerField(blank=True, null=True)
jobmst_name = models.TextField(blank=True)
jobmst_mode = models.SmallIntegerField(blank=True, null=True)
jobmst_owner = models.ForeignKey('Owner', db_column='jobmst_owner', related_name = 'Jobmst_Jobmst_owner', blank=True, null=True)
jobmst_desc = models.TextField(blank=True) # This field type is a guess.
jobmst_crttm = models.DateTimeField()
jobdtl_id = models.IntegerField(blank=True, null=True)
jobmst_lstchgtm = models.DateTimeField(blank=True, null=True)
jobmst_runbook = models.TextField(blank=True) # This field type is a guess.
jobcls_id = models.IntegerField(blank=True, null=True)
jobmst_prntname = models.TextField(blank=True)
jobmst_alias = models.CharField(max_length=10, blank=True)
jobmst_dirty = models.CharField(max_length=1, blank=True)
def __unicode__(self):
return self.jobmst_name
class Meta:
managed = False
db_table = 'jobmst'

For anyone who may get the same problem as I had I found the solution (ie. how to get MPTT working with a legacy database!)
I ran through the beginning of the MPTT tutorial here -
http://django-mptt.github.io/django-mptt/tutorial.html
I then opened my SSMS and looked at how django mptt was creating the table for the test. I then recreated the columns that were missing in my legacy table -
lft, rght, tree_id, level
as well as the FK/PK connections.
From there I simply had to load up the model in shell -
from polls.models import Jobmst
Jobmst.objects.rebuild()
Bam! It then created all the values I needed by running through the model.
Now I can do the rest of the tutorial and I can get my tree view output now just need to get it in json so I can import into d3.
If anyone finds this and has questions just msg me.

Related

How to Access Data Within Django ManytoMany with SQL Query

I have two models in a Django site as follows, one of which is a many to many relationship:
class Seller(models.Model):
account = models.ForeignKey(Account, related_name='sellers',null=True, on_delete=models.SET_NULL)
bio = models.TextField(null=True, blank=True)
city = models.CharField(max_length=50, null=True, blank=True)
and
class Account(models.Model):
username = models.CharField(max_length=50, blank=True, null=True, unique=True)
password = models.CharField(max_length=64)
name = models.CharField(max_length=50, blank=True, null=True)
I am trying to run an SQL query on my Postgresql database, but I cannot find a clear way to to write an SQL query to access information within the many to many relationship.
I have the seller ID, and I want to get the username within Account. If I try the following, which clearly is not correct, it won't work but I'm stumped as what to do next:
SELECT seller_seller.bio, seller_seller.account.id
FROM seller_seller, admin_account
WHERE ...no clue!
Can someone point me in the right direction? Thank you!
You can simply get Seller object matching the seller_id you have by following query:
>>> seller = Seller.objects.get(pk=seller_id) # Note it would raise SellerDoesNotExists if matching pk not found
Then using above seller object you can get username by:
>>> seller.account.username
But problem with above query is that it does an extra query for fetching username.
So to avoid extra query you can use select_related which would perform InnerJoin with related Account
>>> from django.db.models import F
>>> seller_id = 1 # seller id that you have
>>> qs = (Seller.objects.filter(pk=seller_id).select_related('account')
.annotate(username=F('account__username')))
>>> print(qs.first().username) # Note : It would raise AttributeError if no object found matching the condition.

ORM Django LEFT JOIN + GROUP BY

I have following models in my Django project:
class Player(models.Model):
name = models.CharField()
class Team(models.Model):
name = models.CharField()
class Membership(models.Model):
player = models.ForeignKey(Player)
team = models.ForeignKey(Team)
date_joined = models.DateField()
date_leaved = models.DateField(blank=True, null=True)
And I want to create view on Django level which would perform that:
CREATE VIEW last_contract_expire AS
SELECT Player.name, MAX(Membership.date_leaved)
FROM player LEFT OUTER JOIN membership ON membership.player.id=player.id
GROUP BY player.id;
Django currently does not support SQL views (see this ticket). Django also does not support SQL GROUP BY directly. Anyway, you could achieve it like this:
def last_contract_expire():
return Player.objects.all().annotate(max_date_leaved=Max('membership__date_leaved'))
This will return all players and each player will have attributes name and max_date_leaved. Hope this helps

Django - Making a SQL query on a many to many relationship with PostgreSQL Inner Join

I am looking for a perticular raw SQL query using Inner Join.
I have those models:
class EzMap(models.Model):
layers = models.ManyToManyField(Shapefile, verbose_name='Layers to display', null=True, blank=True)
class Shapefile(models.Model):
filename = models.CharField(max_length=255)
class Feature(models.Model):
shapefile = models.ForeignKey(Shapefile)
I would like to make a SQL Query valid with PostgreSQL that would be like this one:
select id from "table_feature" where' shapefile_ezmap_id = 1 ;
but I dont know how to use the INNER JOIN to filter features where the shapefile they belongs to are related to a particular ezmap object
Something like this:
try:
id = Feature.objects.get(shapefile__ezmap__id=1).id
except Feature.DoesNotExist:
id = 0 # or some other action when no result is found
You will need to use filter (instead of get) if you want to deal with multiple Feature results.

django self join query using aliases

am trying to use queryset to perform the following query without using raw SQL. any idea how can do that?
select * from category_main a, category_list b, category_main c where b.main_id=c.id and a.id=c.parent_id
UPDATED
below are my models
class Main(models.Model):
slug = models.SlugField()
is_active = models.BooleanField(default=True)
site = models.ForeignKey(Site)
parent = models.ForeignKey('self', blank=True, null=True, limit_choices_to={'parent' : None})
class Meta:
unique_together = (("slug", "parent"))
def __unicode__(self):
return self.slug
class List(models.Model):
main = models.ForeignKey(Main)
slug = models.SlugField(unique=True)
is_active = models.BooleanField(default=True)
parent = models.ForeignKey('self', blank=True, null=True)
def __unicode__(self):
return self.slug
UPDATE
Hi, I just managed to find a query that does that for me, I used advised below to join main with main's parent and from there I joined list with main list using the below
Main.objects.select_related('main', 'parent').filter(list__is_active=True, maini18n__language='en', list__listi18n__language='en').query.__str__()
'SELECT `category_main`.`id`, `category_main`.`slug`, `category_main`.`is_active`, `category_main`.`site_id`, `category_main`.`parent_id`, T5.`id`, T5.`slug`, T5.`is_active`, T5.`site_id`, T5.`parent_id` FROM `category_main` INNER JOIN `category_maini18n` ON (`category_main`.`id` = `category_maini18n`.`main_id`) INNER JOIN `category_list` ON (`category_main`.`id` = `category_list`.`main_id`) INNER JOIN `category_listi18n` ON (`category_list`.`id` = `category_listi18n`.`list_id`) LEFT OUTER JOIN `category_main` T5 ON (`category_main`.`parent_id` = T5.`id`) WHERE (`category_maini18n`.`language` = en AND `category_list`.`is_active` = True AND `category_listi18n`.`language` = en )'
the returned query mapped everything I need, accept its not being added to the select statement, is there a way so i can force it to select columns from category_list.* ?
This does basically what you want:
lists = List.objects.select_related('main', 'parent')
Note you have to explicitly state the relationships to follow in select_related here, because your parent relationship has null=True which isn't followed by default.
This will give you a set of List objects, but pre-fetch the related Main and List objects which you can reference as normal without hitting the db again.

Django LEFT OUTER JOIN on TWO columns where one isn't a foreign key

I have two models like so:
class ObjectLock(models.Model):
partner = models.ForeignKey(Partner)
object_id = models.CharField(max_length=100)
class Meta:
unique_together = (('partner', 'object_id'),)
class ObjectImportQueue(models.Model):
partner = models.ForeignKey(Partner)
object_id = models.CharField(max_length=100)
... # other fields
created = models.DateTimeField(auto_now_add = True)
modified = models.DateTimeField(auto_now = True, db_index=True)
class Meta:
ordering = ('modified', 'created')
There is nothing notable about the third model mentioned above (Partner).
I'd like to get something like:
SELECT * FROM ObjectImportQueue q LEFT OUTER JOIN ObjectLock l ON
q.partner_id=l.partner_id AND q.object_id=l.object_id WHERE l.object_id
IS NULL and l.partner_id IS NULL;
I came across this page that tells how to do custom joins, and I tried passing in a tuple of the column names to join in place of the column name to join, and that didn't work. The Partner table shouldn't need to be included in the resulting sql query but I will accept an answer that does include it as long as it effectively does what I'm trying to do with one query.
If you're using Django 1.2+ and know the SQL you want, you could always fall back to a Raw Query.
I also meet a similar question.but finally,I found I asked a wrong question to be solve.
in the Django ORM,the condition of SQL join is base on what the models.Model fields defined.
there are Many-to-one relationships (ForeignKey),Many-to-many relationships(ManyToManyField),One-to-one relationships(OneToOneField).
in your situation.ObjectLockModel and ObjectImportQueueModel have the same part of fields, the partnerfield and object_idfield.yon should use One-to-one relationships.
you can change your Model like this:
class ObjectImportQueue(models.Model):
partner = models.ForeignKey(Partner)
object_id = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add = True)
modified = models.DateTimeField(auto_now = True, db_index=True)
def __unicode__(self):
return u"%s:%s" % (self.partner, self.object_id)
class Meta:
ordering = ('modified', 'created')
class ObjectLock(models.Model):
lock = models.OneToOneField(ObjectImportQueue, null=True)
class Meta:
unique_together = (('lock',),)
order of Model is import,OneToOneField argument model must come first.
>>> p1 = Partner.objects.get(pk=1)
>>> p2 = Partner.objects.get(pk=2)
>>> Q1 = ObjectImportQueue.objects.create(partner=p1,object_id='id_Q1')
>>> Q2 = ObjectImportQueue.objects.create(partner=p2,object_id='id_Q2')
>>> ObjectImportQueue.objects.filter(lock__isnull=True)
[<ObjectImportQueue: Partner object:id_Q1>, <ObjectImportQueue: Partner object:id_Q2>]
>>> L1 = ObjectLock.objects.create(lock=Q1)
>>> ObjectImportQueue.objects.filter(lock__isnull=True)
[<ObjectImportQueue: Partner object:id_Q2>]
ObjectLock.objects.createlock a object
ObjectImportQueue.objects.filter(lock__isnull=True) select object don't be lock.
if you use the appropriate relationships, generate the ORM query will be easy.In Django,Define the relationships during you build the Model is better than use Query statement to relation the relationships between tables.
I just found a solution to this problem.
You have to create a view that does the join for you
CREATE VIEW ImporQueueLock AS (
SELECT q.id, l.id
FROM ObjectImportQueue q
LEFT OUTER JOIN ObjectLock l
ON q.partner_id=l.partner_id AND q.object_id=l.object_id
)
Then make a django model for that view
class ImportQueueLock(models.Model):
queue = models.ForeignKey(ObjectImportQueue, db_column='q')
lock = models.ForeignKey(ObjectLock, db_column='l')
Then make a ManyToMany on your Django model from ObjectLock to ObjectImportQueue through ImportQueueLock
class ObjectLock(models.Model):
partner = models.ForeignKey(Partner)
object_id = models.CharField(max_length=100)
queue = models.ManyToManyField(ObjectImportQueue, through = ImportQueueLock)
and you will be able to do
ObjectLock.objects.filter(importqueuelock__objectimportqueue__ .....)