Django - Reference data from another model using a foreign key - python

I'm new to Django so please tell me if I'm not on the right track.
I have a Django project that I'm building and just wanted to ask what is the correct Django way to retrieve data from one model and use it in another.
I have a for loop to assign the required fields to variables but I was looking for a cleaner solution and if one exists, one that uses the foreign key.
Here's an example of the code involved:
#MODELS
class Class1(models.Model):
tag_number = models.CharField(max_length = 300, null = True, blank = True)
example1 = models.CharField(max_length = 300, null = True, blank = True)
example2 = models.CharField(max_length = 300, null = True, blank = True)
class Class2(models.Model):
tag = models.ForeignKey(Class1, related_name = 'tag_foreignkey')
example3 = models.CharField(max_length = 300, null = True, blank = True)
example4 = models.CharField(max_length = 300, null = True, blank = True)
#VIEWS - This view is for Class2 but referencing fields from Class1
tag_number = str(instrumentform.cleaned_data['tag'])
query_results = Class1.objects.filter(tag_number = tag_number)
for query_result in query_results:
example5 = query_result.example1
example6 = query_result.example2
The above works but I assume it's not the Django way of doing things and is not taking advantage of the foreign key.
If someone could give me a nudge in the right direction that would be greatly appreciated.

Still, not hundred percent know what you want, since there are some missing info out there.
For you Class1, You should do what you have done to Class2, using foreign key to store tag.
For you code at bottom, there is a easy way to do it. (Assume you have used foreign key)
tag_number = int(instrumentform.cleaned_data['tag'])
for query_result in Class1.objects.filter(tag = tag_number).values_list('example1', 'exmaple2'):
example5, example6 = query_result

I'm not sure what you are trying to achieve here. I am imagining that PstCalc = Class1
Nevertheless, if you have
c1 = Class1()
c2 = Class2()
c3 = Class2()
and
c2.tag = c1
c3.tag = c1
using the foreignkey you would have:
c1.class2_set = (c2, c3) <- this would be a queryset, not a list
c2.tag = c1 => c2.tag.example1 = c1.example1
...hope I made my self understood :)

Related

How to change join and group by SQL to ORM in Django

I'm new in Django. So, I want to join two models which are company and client and count the number of clients for each of the company. Here the SQL
SELECT Company_company.name, count(Client_client.cid)
FROM Company_company
LEFT JOIN Client_client
ON Company_company.comid = Client_client.comid_id
GROUP BY Company_company.name;
But since in Django, we use ORM. So I'm a little bit confusing since I'm a beginner. I already refer few SQL to ORM converter website such as Django ORM and do some try and error. But, I didn't know where the problem since I want the output from the ORM to be classified into a different array. Here is my code:
labels = []
data = []
queryClientCompany = client.objects.values('comid').annotate(c=Count('cid')).values('comid__name','c')
for comp in queryClientCompany:
labels.append(comp.comid__name)
data.append(comp.c)
Here some of the relevant things in the client and company models:
class client (models.Model):
#client info
cid = models.AutoField(primary_key = True)
comid = models.ForeignKey(company,related_name='companys',
on_delete = models.DO_NOTHING,verbose_name="Company",null = True, blank = True)
class company(models.Model):
comid = models.AutoField(_('Company'),primary_key = True)
#company info
name = models.CharField(_('Company Name'),max_length = 50)
The error stated that the comid__name is not defined. So actually how to append the result? I hope someone can help me. Thank you for helping in advanced.
You should query from the opposite side to perform the LEFT OUTER JOIN between company and client (and not client and company):
from django.db.models import Count
labels = []
data = []
queryClientCompany = company.objects.annotate(
c=Count('companys__cid')
)
for comp in queryClientCompany:
labels.append(comp.name)
data.append(comp.c)
The companys part is due to the related_name='copanys', but it does not make much sense to name this relation that way. The related_name=… parameter [Django-doc] specifies how to access the Clients for a given Company, so clients is a more appropriate value for the related_name:
class client (models.Model):
cid = models.AutoField(primary_key=True)
comid = models.ForeignKey(
company,
related_name='clients',
on_delete = models.DO_NOTHING,
verbose_name="Company",
null = True,
blank = True
)
then the query is:
from django.db.models import Count
labels = []
data = []
queryClientCompany = company.objects.annotate(
c=Count('clients__cid')
)
for comp in queryClientCompany:
labels.append(comp.name)
data.append(comp.c)

Conversion of SQL to Django

Below are my django models
class SourceFile(models.Model):
full_path = models.TextField(unique = False)
project_name = models.TextField(blank = True)
def __str__(self):
return self.full_path
class Coverage(models.Model):
line_pct = models.IntegerField(default = 0, blank = True)
source_file = models.ForeignKey(SourceFile, related_name = 'coverage', null = True)
date_generated = models.DateTimeField(default = timezone.now, blank = True)
def source_file_full_path(self):
return self.source_file.full_path
Now i want count of distinct id of source file table present in coverage table based on project_name.
I wrote a sql query for the same but unable to write django equivalent for the same.
select count(distinct(sf.id)), sf.project_name from coverage c inner join sourcefile sf on c.source_file_id = sf.id group by sf.project_name;
Please help with this
You should take a look at this:
https://docs.djangoproject.com/en/1.11/topics/db/queries/#related-objects
You can use raw sql in django but it is much easier to use django manager.
I'm sorry I can't provide more specific help but I am not entirely sure what exactly you need.

How do I display Django data from a related model of a related model?

I am trying to display data from several models that are related together through a QuerySet. My ultimate goal is to display some information from the Site model, and some information from the Ppack model, based on a date range filter of the sw_delivery_date in the Site model.
Here are my models:
class Site(models.Model):
mnemonic = models.CharField(max_length = 5)
site_name = models.CharField(max_length = 100)
assigned_tech = models.ForeignKey('Person', on_delete=models.CASCADE, null = True, blank = True)
hw_handoff_date = models.DateField(null = True, blank = True)
sw_delivery_date = models.DateField(null = True, blank = True)
go_live_date = models.DateField(null = True, blank = True)
web_url = models.CharField(max_length = 100, null = True, blank = True)
idp_url = models.CharField(max_length = 100, null = True, blank = True)
def __str__(self):
return '(' + self.mnemonic + ') ' + self.site_name
class Ring(models.Model):
ring = models.IntegerField()
def __str__(self):
return "6." + str(self.ring)
class Ppack(models.Model):
ppack = models.IntegerField()
ring = models.ForeignKey('Ring', on_delete=models.CASCADE)
def __str__(self):
return str(self.ring) + " pp" + str(self.ppack)
class Code_Release(models.Model):
Inhouse = 'I'
Test = 'T'
Live = 'L'
Ring_Location_Choices = (
(Inhouse, 'Inhouse'),
(Test, 'Test'),
(Live, 'Live'),
)
site_id = models.ForeignKey('Site', on_delete=models.CASCADE)
type = models.CharField(max_length = 1, choices = Ring_Location_Choices, blank = True, null = True)
release = models.ForeignKey('Ppack', on_delete=models.CASCADE)
def __str__(self):
return "site:" + str(self.site_id) + ", " + self.type + " = " + str(self.release)
If I use the following,
today = datetime.date.today()
future = datetime.timedelta(days=60)
new_deliveries = Site.objects.select_related().filter(sw_delivery_date__range=[today, (today + future)])
I can get all of the objects in the Site model that meet my criteria, however, because there is no relation from Site to Code_Release (there's a one-to-many coming the other way), I can't get at the Code_Release data.
If I run a for loop, I can iterate through every Site returned from the above query, and select the data from the Code_Release model, which allows me to get the related data from the Ppack and Ring models.
site_itl = {}
itl = {}
for delivery in new_deliveries:
releases = Code_Release.objects.select_related().filter(site_id = delivery.id)
for rel in releases:
itl[rel.id] = rel.release
site_itl[delivery.id] = itl
But, that seems overly complex to me, with multiple database hits and possibly a difficult time parsing through that in the template.
Based on that, I was thinking that I needed to select from the Code_Release model. That relates back to both the Site model and the Ppack model (which relates to the Ring model). I've struggled to make the right query / access the data in this way that accomplishes what I want, but I feel this is the right way to go.
How would I best accomplish this?
You can use RelatedManager here. When you declare ForeignKey, Django allows you to access reverse relationship. To be specific, let's say that you have multiple code releases that are pointing to one specific site. You can access them all via site object by using <your_model_name_lowercase>_set attribute. So in your case:
site.code_release_set.all()
will return QuerySet of all code release objects that have ForeignKey to object site
You can access the Releases from a Site object. First, you can put a related_name to have a friendly name of the reverse relation between the models:
site_id = models.ForeignKey('Site', on_delete=models.CASCADE, related_name="releases")
and then, from a Site object you can make normal queries to Release model:
site.releases.all()
site.releases.filter(...)
...

counting/filtering database-entries over multiple foreign key-relations

These are my DB-Models:
class Category(models.Model):
name = models.CharField(max_length = 20, unique = True)
...
class Feed(models.Model):
title = models.CharField(max_length = 100)
category = models.ForeignKey(Category)
...
class Article(models.Model):
title = models.CharField(max_length = 100)
read = models.BooleanField(default = False)
feed = models.ForeignKey(Feed)
...
Every Article belongs to one Feed (source) and each Feed is in a Category.
Now, i want to create a view to display all categories with some meta-information,
e.g. how many unread articles are in category x.
I tried things like this, but nothing worked:
categories = Category.objects.filter(feed__article__read=False)\
.annotate(Count('feed__article'))
What is the proper way to extract those information?
Especially if i want to add further information like: number of feeds in category and
number of favored articles in one QuerySet (If possible)...
Any ideas?
Thanks.
EDIT: Since i had no idea how to 'solve' this problem, i've written an ugly workaround:
result = categories.values_list('name',
'feed__title',
'feed__article__title',
'feed__article__read')
for i in range(0, len(result)):
#if pointer changed to a new category
#dump current dict to list and clear dict for the new values
if last != result[i][0]:
category_list.append(category_info.copy())
category_info.clear()
last = result[i][0]
if some values None:
insert values
elif some other values None:
insert values
else:
category_info['name'] = result[i][0]
category_info['feed_count'] = category_info.get('feed_count', 0) + 1
category_info['all_article_count'] = category_info.get('all_article_count', 0) + 1
#if a article has not been read yet
if result[i][3] == False:
category_info['unread_article_count'] = category_info.get('unread_article_count', 0) + 1
#if this category is the last in the result-list
if i+1 == len(result):
category_list.append(category_info.copy())
i += 1
I am pretty sure there is a quicker and nicer way to get those information, but at least i can work with it for the moment :/
You must label the information. You should be able to use category.article_count for the items in the queryset if you use the query below.
categories = Category.objects.filter(feed__article__read=False)\
.annotate(article_count=Count('feed__article'))

Django ORM with Postgres: rows unexpectedly deleted - Bug?

I have the problem that objects were unexpectedly deleted and created a minimal example. I dont't know whether it's a bug or if a made a thinking error.
The models are something like that:
class A(models.Model):
related = models.ForeignKey('C', blank = True, null = True)
class B(models.Model):
title = models.CharField(max_length = 255, blank = True, null = True)
class C(models.Model):
b = models.OneToOneField('B', blank = True, null = True, related_name = 'c')
This is the test case:
a1 = A()
a1.save()
b1= B()
b1.save()
c1 = C()
c1.b = b1
c1.save()
b1 = B.objects.all()[0]
b1.c.delete()
b1.delete()
self.failUnlessEqual(A.objects.count(),1)
I deleted b1.c explicitly before deleting b1. When deleting b1.c, b1.c is NULL. It seems that then all entries of A were deleted where A.related is NULL.
Is this a bug? I really did not expect that all entries of all tables that have a NULL reference to model C are deleted.
I am using Postgres 8.4 and psycopg2 as DB Backend.
Best regards!
Django implements foreign keys by default with an "ON DELETE CASCADE", which means that records pointing to a deleted record will also be deleted. It's not a bug, it's designed on purpose this way.
Workarounds are discussed elsewhere on stackoverflow.

Categories

Resources