Django views query and the foreign key lookup - python

I'm having trouble running a Django query (v1.9) within my views page. I'm basically retrieving one record from the primers table (that's working fine), then trying to retrieve the corresponding record from the 'gene' table:
models.py:
class Gene(models.Model):
GeneName = models.CharField(max_length=10, unique=True)
ChromosomeNo = models.CharField(max_length=2)
class Primer(models.Model):
PrimerName = models.CharField(max_length=20, unique=True)
Gene = models.ForeignKey(Gene)
views.py
def PrimerDetail(request, pk):
primer_info = get_object_or_404(Primer, pk=pk)
Gene_info = Gene.objects.get(Gene_id = primer_info.GeneName)
The problem appears to be with my use of primer_info.GeneName. I get:
'Primer' object has no attribute 'GeneName'
Change it to primer_info.Gene and I get:
Cannot resolve keyword 'Gene_id' into field. Choices are: ChromosomeLoc, ChromosomeNo, Comments, Disease, Disease_id, GeneName, NoExons, id, primer`
I can substitute for a string value and it works fine. How should I reference a field that is a foreign key object in this context?

with the above description you can try out one more way of getting the Gene object this is follows as.
Gene_info = Gene.objects.get(pk = primer_info.Gene.id)
this would give you the object of Gene object that is foreign key in the Primer Table.

Well it's because you never have a field called Gene_id in your Primer model. Since Gene is the foreign key in Primer, it's easy to get Gene_info:
Gene_info = primer_info.Gene
If you want to query directly on Gene model(which is pretty unnecessary in your case), do:
Gene_info = Gene.objects.get(primer__id=primer_info.id)

Related

Django ForeignKey Which field?

I have just started learning Django and one thing in models about ForeignKey was unclear to me.
So lets say I have one model like this:
class Webpage(models.Model):
name = models.CharField(max_length=264, unique=True)
url = models.URLField(unique=True)
name2 = models.CharField(max_length=264, unique=True)
class Records(models.Model):
site = models.ForeignKey(Webpage)
So when creating Records entry for testing I see ForeignKey is referenced to Name field of Webpage. My confusion is why exactly name? As I know ForeignKey is referencing to primary key and if you are not giving primary_key attribute to any fields it will create 'id' field and make that as primary_key. So then why not 'id' field but 'name'.
Sorry if this is repeat question, I just couldn`t find answer.
#Orkhan Rustamli
If you are try to list all yours records and get the name of webpage I think it's because your print method on webpage class is set to return the name of attribute.
if you want to return the id you will set the return function like:
class webpage(models.Model):
pass
def str(self):
return self.id

Django foreign key not working for One-To-Many relationship

I'm trying to write a query that when an artist id is searched, that artist's info and all of their pieces are the result, basically a join query between Artist and ArtistPiece
#models.py
class Artist(models.Model):
name = models.CharField(max_length=100)
artist = models.CharField(max_length=150)
created_by = models.ForeignKey(User)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class ArtistPiece(models.Model):
artist = models.ForeignKey(Artist, on_delete = models.CASCADE)
piece_name = models.CharField(max_length=50)
details = models.CharField(max_length=100)
#views.py
from .models import Artist, ArtistPiece
class EditArtistViewSet(viewsets.ViewSet):
def edit(self,request,artist_id):
artist = Artist.objects.select_related().get(pk=artist_id)
data = model_to_dict(artist)
return render(
request, 'ArtistInfo/edit.html', {
'editing': 'true',
'edit_data': json.dumps(data),
})
def submit_edit(self,request):
return render(request, 'ArtistInfo/edit.html')
The above works, but it doesn't select any of the data from the ArtistPiece model, despite the primary key. Then after reading this post about using select_related I realized that rather than looking up the artist, I'd have to look up ArtistPiece by the foreign_key, and use select_related to find the info about the artist.
I then tried this
def edit(self, request, artist_id):
artist = ArtistPiece.objects.get()
data = model_to_dict(artist)
Which resulted in the following error get() returned more than one ArtistPiece -- it returned 5!. Searching, I found this post which states that I need to use filter rather than get. I then tried this
def edit(self, request, artist_id):
artist = ArtistPiece.objects.select_related('artist').filter(artist=artist_id)
data = model_to_dict(artist)
Which gave the following error 'QuerySet' object has no attribute '_meta'. I tried searching for this error and found this which basically suggests to use get, which seems to go against everything else I found.
To summarize, I just want to select an artist and their pieces but I can't seem to figure out how.
Edit - I tried a few things and made some progress, but it's still not working correctly
Just to be clear I'm trying to write a query that will look up an artist by their id, and the result will be the artist's info and the artist's pieces. I can query and retrieve the Artist model based on an artist_id or the ArtistPiece model, but not one query that will return both Artist and all of the ArtistPiece entries associated to that artist.
Adding related_name='pieces'to my model as #bignose has suggested, I then tried artist = Artist.objects.select_related('pieces').filter(pk=artist_id) but that still gave the no attribute.... error above. I then changed data = model_to_dict(artist) to data = [model_to_dict(a) for a in artist] and it worked, however the data was only the artist and nothing from the ArtistPiece model.
Instead of querying the Artist model I instead tried to query the ArtistPiece model as such: artist = ArtistPiece.objects.select_related('artist').filter(artist=artist_id), but then only the artist's pieces were returned and no info from the Artist model.
model_to_dict accepts single model object, but your are passing into that the object of QuerySet type (which is quite similar to list). To get the list of dicts all the objects, you need to explicitly call model_to_dict on each model object like:
data = [model_to_dict(a) for a in artist]
Better way is to use QuerySet.values() as:
data = artist.values()
As per the Queryset.Values() document:
Returns a QuerySet that returns dictionaries, rather than model instances, when used as an iterable.
Each of those dictionaries represents an object, with the keys corresponding to the attribute names of model objects.
Also take a look at Serializer class. I think it will be useful for you.
ArtistPiece.objects.filter(artist=artist_id), this will return a queryset.
If you want a list you can do ArtistPiece.objects.filter(artist=artist_id).values_list('id', flat=True)
https://docs.djangoproject.com/en/1.10/ref/models/querysets/#values-list
The ForeignKey field type automatically also sets an attribute on the foreign model; in your case, Artist.artistpieces_set.
favourite_artist = Artist.objects.get(pk="foo") # However you get an Artist instance.
favourite_artist.artistpieces_set # Is a QuerySet of all the ArtistPiece instances that reference this instance.
# Because it's a QuerySet, you can iterate it, filter it, etc.
for piece in favourite_artist.artistpieces_set.all():
put_in_exhibition(piece)
Thus, when you get an Artist instance, it automatically has an attribute that is a QueryManager for the ArtistPiece instances referencing that Artist.
This is good because it means the foreign key relationship is defined in one place, but it is also accessible from the other end.
See the documentation for “Following relationships “backwards”” for how to use these features.

concatenate object querys results on foreign key in django

I have two models from which I'd like to get one result on a foreign key.
# Master List:
class Block(models.Model):
lesson = models.ForeignKey('Lesson')
name = models.CharField(max_length=100)
# Contains revisions, i.e. updates:
class BlockContent(models.Model):
block = models.ForeignKey('Block')
content = models.TextField()
type = models.IntegerField(default=1)
revision = models.IntegerField(default=0)
latest = models.BooleanField(default=True)
Ideally for template purposes, I'd like to be able to call block.content or block.type for instance, but this doesn't seem to be easily possible.
Getting the BlockContent by foreign key seems to be a pain- despite there only being a one-to-one result in this specific view, it requires .all() and a lots of iteration.
Stuff I've tried:
additional = BlockContent.objects.get(block=block.id)
block.blockcontent_set.add(additional)
What you have is a one to many relationship in which Block can have many BlockContent objects.
The way to get content from a block is by using reverse relation, I don't see a problem with that:
content = block.blockcontent_set.all()[0].content
to get the content of the first blockcontent in the queryset.
Of course order_by and all other queryset Django goodnes can be used here.
You could also define a related_name to:
class BlockContent(models.Model):
block = models.ForeignKey('Block', related_name=blockcontent)
and use it like:
content = block.blockcontent.all()[0].content

How do you join two tables on a foreign key field using django ORM?

Let's assume I have the following models:
class Position(models.Model):
name = models.CharField()
class PositionStats(models.Model):
position = models.ForeignKey(Position)
averageYards = models.CharField()
averageCatches = models.CharField()
class PlayerStats(models.Model):
player = models.ForeignKey(Player)
averageYards = models.CharField()
averageCatches = models.CharField()
class Player(models.Model):
name = models.CharField()
position = models.ForeignKey(Position)
I want to perform the equivalent SQL query using django's ORM:
SELECT *
FROM PlayerStats
JOIN Player ON player
JOIN PositionStats ON PositionStats.position = Player.position
How would I do that with django's ORM? The query isn't exactly correct, but the idea is that I want a single query, using django's ORM, that gives me PlayerStats joined with PositionStats based on the player's position.
I've been working with django for a while now and I have had a pretty rough time figuring out the table joins, but I think I finally understand and I would like to pass this on to others so they may avoid the frustration that I had with it.
Consider the following model.py:
class EventsMeetinglocation(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100)
address = models.CharField(max_length=200)
class Meta:
managed = True
db_table = 'events_meetinglocation'
class EventsBoardmeeting(models.Model):
id = models.IntegerField(primary_key=True)
date = models.DateTimeField()
agenda_id = models.IntegerField(blank=True, null=True)
location_id = models.ForeignKey(EventsMeetinglocation)
minutes_id = models.IntegerField(blank=True, null=True)
class Meta:
managed = True
db_table = 'events_boardmeeting'
Here we can see that location_id in EventsBoardmeeting is a foreign key for the id in EventsMeetinglocation. This means that we should be able to query the information in EventsMeetinglocation by going through EventsBoardmeeting.
Now consider the following views.py:
def meetings(request):
meetingData = EventsBoardmeeting.objects.all()
return render(request, 'board/meetings.html', {'data': meetingData })
As stated many times before in may other posts, django takes care of joins automatically. When we query everything in EventsBoardmeeting we also get any related information by foreign key as well, But the way that we access this in html is a little different. We have to go through the variable used as the foreign key to access the information associated with that join. For example:
{% for x in data %}
{{ x.location_id.name }}
{% endfor %}
The above references ALL of the names in the table that were the result of the join on foreign key. x is essentially the EventsBoardmeeting table, so when we access x.location_id we are accessing the foreign key which gives us access to the information in EventsMeetinglocation.
select_related() and prefetch_related() is your solution. They work almost same way but has some difference.
select_related() works by creating an SQL join and including the fields of the related object in the SELECT statement. For this reason, select_related gets the related objects in the same database query. But it only works for one-to-one or one-to-many relation. Example is below-
entry = Entry.objects.select_related('blog').get(id=5)
or
entries = Entry.objects.filter(foo='bar').select_related('blog')
prefetch_related(), on the other hand, does a separate lookup for each relationship and does the ‘joining’ in Python. This allows it to prefetch many-to-many and many-to-one objects, which cannot be done using select_related. So prefetch_related will execute only one query for each relation. Example is given below-
Pizza.objects.all().prefetch_related('toppings')
It isn't one query, but it's pretty efficient. This does one query for each table involved, and joins them in Python. More on prefetch_related here: https://docs.djangoproject.com/en/dev/ref/models/querysets/#prefetch-related
Player.objects.filter(name="Bob").prefetch_related(
'position__positionstats_set', 'playerstats_set')
In Django 3.2, the framework automatically follows relationships when using method QuerySet.filter()
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
This compiles to the following SQL query:
SELECT
"polls_choice"."id",
"polls_choice"."question_id",
"polls_choice"."choice_text",
"polls_choice"."votes"
FROM
"polls_choice"
INNER JOIN "polls_question" ON
("polls_choice"."question_id" = "polls_question"."id")
WHERE
"polls_question"."pub_date" BETWEEN 2020-12-31 23:00:00 AND 2021-12-31 22:59:59.999999
See tutorial here: https://docs.djangoproject.com/en/3.2/intro/tutorial02/
From django.db import connection In your view include the below statement:
cursor = connection.cursor()
cursor.execute("select * From Postion ON Position.name = Player.position JOIN
PlayerStats ON Player.name =
PlayerStats.player JOIN PositionStats ON Position.name = PositionStats.player")
solution = cursor.fetchall()

Correct way to access related objects

I have the following models
class Person(models.Model):
name = models.CharField(max_length=100)
class Employee(Person):
job = model.Charfield(max_length=200)
class PhoneNumber(models.Model):
person = models.ForeignKey(Person)
How do I access the PhoneNumbers associated with an employee if I have the employee id?
Currently I am using
phones = PhoneNumbers.objects.filter(person__id=employee.id)
and it works only because I know that the employee.id and person.id are the same value, but I am sure this is the incorrect way to do it.
Thanks
Andrew
You can (and should) filter without knowing the foreign key field:
PhoneNumber.objects.filter(employee=your_employee).all()
You could do:
employees = Employee.objects.filter(id=your_id).select_related()
if employees.count() == 1:
phone_numbers = employees[0].phonenumber_set.all()
That should get you all your phone numbers in one db query.
By default you can access models related through a foreignkey on the "opposite" side by using "model name in all lower case" followed by "_set". You can change the name of that accessor by setting the related name property of the foreignkey.

Categories