Django OneToOneField & Foreignkey - How to get value from related model? - python

I've been banging my head against this for an whole workday now and tried various suggestions from stackoverflow and other google results, and consulted the django documentation but all to no avail.
In my django project I have the two models listed below. The related values in the models are the rowid which is unique to each "Sag". My goal is that when I query using "Sag.objects.all()" that it would also return the group attached to that specific rowid.
Ideally it should be a "OneToOneField" and not a "ForeignKey" since the rowid should only exists once in both tables but my latest try had me change it to a "ForeignKey". I have, however, been unable to get the related field with the different solutions I've tried so far
class Sag(models.Model):
id = models.IntegerField(blank=True, null=False, primary_key=True)
rowid = models.IntegerField(db_column='RowId', blank=True, null=True)
posts = models.TextField(db_column='POSTS', blank=True, null=True)
date = models.TextField(db_column='Date', blank=True, null=True)
art = models.IntegerField(db_column='Art', blank=True, null=True)
class Grouping(models.Model):
rowid = models.ForeignKey(Sag, on_delete=models.CASCADE, db_column='RowId')
group = models.IntegerField(db_column='Group', blank=True, null=True)
Any ideas/ressources as to how i would solve this problem?

With a ForeignKey as you have it here, you can pass a related_name. If you don't pass one, Django assigns one by default with the model name and then _set, here it would be sag.grouping_set.all().
sag = Sag.objects.get(id=1)
groupings = sags.grouping_set.all()
Inside a template, if you have a list of Sags:
{% for sag in sags %}
{% for grouping in sag.grouping_set.all %}
{{ grouping.group }}
{% endfor %}
{% endfor %}
To grab the values only:
sag_groups = Sag.objects.all().values('posts', 'data', 'art', 'grouping_set__group')
With a OneToOneField you could do something similar:
sag = Sag.objects.get(id=1)
grouping = sag.grouping
grouping_group = grouping.group
Inside a template:
{% for sag in sags %}
{{ sag.posts }}
{{ sag.grouping.group }}
{{ sag.grouping.rowid}}
{% endfor %}
For the values only:
sag_groups = Sag.objects.all().values('posts', 'data', 'art', 'grouping__group')

Related

Django - Can not join 2 models

Problem: Joining 2 models in Django.
Error: Error during template rendering. Direct assignment to the reverse side of a many-to-many set is prohibited. Use entity_id.set() instead.
I have read through all the threads on SO. Tried all the suggested solutions, read the Django documentation and think I just must be fundamentally misunderstanding something. Any help would be much appreciated.
I have 2 models. Entity and File.
An Entity can have multiples Files but each File only has 1 Entity.
The primary keys of each table are just auto incrementing integers. Therefore I want to join column entity_id from File with entity_id from Entity. According to the documentation I have set entity_id in File as a ForeignKey. And I have set entity_id as unique in Entity
class Entity(models.Model):
pk_entity = models.AutoField(primary_key=True)
entity_id = models.IntegerField(blank=True, null=True, unique=True)
name = models.CharField(blank=True, null=True)
class Meta:
managed = False
db_table = 'entities'
class File(models.Model):
pk_file = models.AutoField(primary_key=True)
filename = models.CharField(blank=True, null=True)
entity_id = models.ForeignKey(Entity, on_delete= models.CASCADE, to_field='entity_id')
The view is just trying to render this. I have tried using .all() rather than select_related() but no data renders.
class TestListView(ListView):
queryset = File.objects.select_related()
template_name = "operations/files/test_list.html"
And this is the html:
{% extends "base.html" %}
{% block content %}
<div>
<div>
<ul>
{% for x in object_list %}
<li>
{{x}}
</li>
{% empty %}
<p>Empty</p>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}
When using select_related you should pass your field name to be selected.
Try this:
class TestListView(ListView):
queryset = File.objects.select_related("entity").all()
template_name = "operations/files/test_list.html"

Django: How to iterate over two one-two-many table relationships?

I am trying to build a simple web page that queries three tables. There is a Company table that has a one-to-many relationship with a Position table, as well as a one-to-many relationship with a Project table.
The goal is to have the page display a given company once, along with all positions and and projects associated with said company. Then, move on to display the next company, any positions held there and projects completed.
Below is the closest I've come to getting this right. But, the obvious problem is that if there is more than one project associated with a given company, you'll see that company listed more than once.
I'm new to Django, so in the interest of learning, I wanted to beat my own head sufficiently hard before asking for help; but I could really use some fresh ideas at this point.
Also: I can see how a nested for loop might work here, but I'm just not clear on how the mechanics of that would work with the query, and then within the template.
Models:
from django.db import models
class Company(models.Model):
company_id = models.AutoField(primary_key=True)
company_name = models.CharField(max_length=20)
company_logo = models.ImageField(upload_to='images/')
def __str__(self):
return self.company_name
class Position(models.Model):
position_id = models.AutoField(primary_key=True)
position_title = models.CharField(max_length=55)
company_id = models.ForeignKey('professional.Company',
on_delete=models.CASCADE,
blank=True,
null=True)
begin_date = models.DateField()
end_date = models.DateField()
def __str__(self):
return self.position_title
class Project(models.Model):
project_id = models.AutoField(primary_key=True)
project_name = models.CharField(max_length=55)
company_id = models.ForeignKey('professional.Company',
on_delete=models.CASCADE,
blank=True,
null=True)
project_description = models.CharField(max_length=500)
project_image = models.ImageField(upload_to='images/')
def __str__(self):
return self.project_name
View:
from django.views.generic import TemplateView, ListView
from professional.models import Company
class ProfessionalHome(TemplateView):
template_name = 'professional/professional_home.html'
class TechnologyListView(ListView):
template_name = 'professional/__technology.html'
context_object_name = 'technology_list'
def get_queryset(self):
return Company.objects.values('company_name','position__position_title', 'project__project_name')
HTML and template:
{% for job in technology_list %}
<h1>{{job.company_name}}</h1>
<h1>Position: {{job.position__position_title}}</h1>
<h1>project: {{job.project__project_name}}</h1>
{% endfor %}
Instead of values in get_queryset method, you can return the actual queryset and then iterate over it to build your view.
def get_queryset(self):
return Company.objects.all()
Then in your template:
{% for job in technology_list %}
<h1>{{job.company_name}}</h1>
{% for position in job.position_set.all() %}
<h1>Position: {{position.position_title}}</h1>
{% endfor %}
{% for project in job.position_set.all() %}
<h1>project: {{project.project_name}}</h1>
{% endfor %}
{% endfor %}
If you want to iterate over companies, then you should use the Company model as the basis for your view, not Technology. Also, you should avoid values and values_list unless you know you have a good reason, which you don't here. You can use prefetch_related() to reduce the number of reverse queries. So:
class TechnologyListView(ListView):
def get_queryset(self):
return Company.objects.all.prefetch_related('project','position')
...
{% for company in company_list %}
<h1>{{company.company_name}}</h1>
{% for position in company.position_set.all %}
<h1>Position: {{ position.position_title }}</h1>
{% endfor %}
{% for project in company.project_set.all %}
<h1>project: {{ project.project_name }}</h1>
{% endfor %}
{% endfor %}
(Note, you should avoid giving your ForeignKey fields names ending in "_id". The Django field refers to the entire Company, not the ID; the fields should be called just company. The underlying database will be suffixed with _id anyway. Also, you don't need to use model_name prefixes on all your fields; it will be obvious from the object they are accessed on.)

django Get foreign-key model in view and show in html

So I have two models:
class UserMeasurements(models.Model):
user = models.CharField(max_length=20)
time = models.DateTimeField(blank=True, null=True)
set = models.ForeignKey(PhotoSets, models.DO_NOTHING)
class Meta:
managed = False
db_table = 'user_measurements'
class PhotoSets(models.Model):
image_dir = models.CharField(max_length=200)
machine = models.ForeignKey(Machine, models.DO_NOTHING)
set_id = models.AutoField(primary_key=True)
photo_taken_time = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'photo_sets'
In the view.py I have :
def Main(request):
measurement_list = UserMeasurements.objects.order_by('-time')
photo_set_list = PhotoSets.objects.all()
a = measurement_list.set.get_field('PhotoSets').rel.to
//this is where I try to get the related models
print str(a)
return render(request, 'main.html', {'measurement_list': measurement_list})
I am trying to show the image_dir in the webpage with every User Measurement.
I checked other stackoverflow questions. But still I can't figure it out.
Thanks in advance.
I don't know what you're trying to do with that a variable, but you don't need to do any of this. You should just follow the foreign key when you output each measurement, eg in your template:
{% for measurement in measurement_list %}
User: {{ measurement.user }}
Time: {{ measurement.time }}
Image dir: {{ measurement.set.image_dir }}
{% endfor %}
Note that this is explained quite fully in the docs, especially in the tutorial where they use the example of poll questions and choices.
Would you give it a try in your main.html
{% for item in measurement_list %}
{{ item.image_dir }}
{% endfor %}

How to optimize number of queries in the Django view?

I show comments in the topic detail view. Exactly two queries happens
in this view: one to get topic and one to get comments list. I
understand that select_related technique can't be used here since
there are two different querysets created in the view. Is there any
way to decrease number of queries to one?
Application code follows below.
app/models.py
class Topic(models.Model):
headline = models.CharField(max_length=400)
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User, related_name='topics')
class Comment(models.Model):
headline = models.CharField(blank=True, max_length=400)
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User, related_name='comments')
topic = models.ForeignKey(Topic, related_name='comments')
app/views.py
class TopicDetail(DetailView):
queryset = Topic.objects.select_related('author').all()
context_object_name = 'topic'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
topic = self.object
context['comments'] = topic.comments.select_related('author').all()
return context
app/templates/app/topic_detail.html
{{ topic.headline }}
{{ topic.description }}
{{ topic.created }}
{{ topic.modified }}
{{ topic.author }}
{% for comment in comments %}
{{ comment.headline }}
{{ comment.description }}
{{ comment.author }}
{% endfor %}
Have a look at the prefetch_related method for reversed foreignKey lookups. This won't actually reduce the number of SQL queries, but django will merge in python related comments and Topics.
In the docs : https://docs.djangoproject.com/en/1.8/ref/models/querysets/#prefetch-related
Also, you should make sure to give different related_name to your fields, otherwise you'll have issues calling them :
author = models.ForeignKey(User, related_name='user_comments')
topic = models.ForeignKey(Topic, related_name='topic_comments')
This way you can write a single line query (but 2 SQL hits) :
topics = Topic.objects.prefetch_related('topic_comments').select_related('author').all()
Then for example :
{% for topic in topics %}
{% for comment in topic.topic_comments.all %}
{{ ... }}
{% endfor %}
{% endfor %}
Even in pure SQL such "single" query would be hard.
Basically You would need to repeat all the topic data in each comment! Like in this pseudo code:
Select comment.*, topic.*
From comments
Right Join topic
That is massive data processing/transfer overhead. Whole operation should take more time, resources then separate queries.
If You really need it, then just write custom function in comment topic model, with pure SQL.

Django foreign key relation in template

i know you will say that this question is asked before many times but i havent solved it yet...
models.py
class Doc(UploadModel):
doc_no = models.CharField(max_length=100, verbose_name = "No", blank=True)
date_added = models.DateTimeField(verbose_name="Date", default=datetime.now,
editable=False)
class DocImage(models.Model):
property = models.ForeignKey(Doc, related_name='images')
image = FileBrowseField("Docs", max_length=200,
directory="doc_img/%Y/%m/%d/%H/%M/%S/",
extensions=[".jpg",".tif"], blank=True, null=True)
views.py
def doc_detail(request, dosc_no):
res = Doc.objects.filter(doc_no = dosc_no)
return render_to_response("doc/doc_detail.html", {"result": res})
templates:
{% for i in docimage.property_set.all %}
{{ i.image.url }}
{% endfor %}
i have tried above template but i didnt get any result. so i want to get imageurl adress in DocImage class...
all helps
If you review the foreign key documentation, if you have a relationship like
Doc -> has many DocImages
you need to define your foreign key on the DocImages class like so:
class DocImage(models.Model):
property = models.ForeignKey(Doc, related_name='images')
If you don't set related names, you can access the DocImages from the Doc like:
Doc.docimage_set.all()
Docs on Related Objects
But setting related_name in the property field lets you do
Doc.images.all()
Just make sure whatever you pass to the template in the view context matches what is used in the template, e.g.
# in the view
return render_to_response('mytemplate.html', { 'mydoc' : doc, 'mydocimage' : img }
This can then be used in the template as follows:
# and in your template to get the images attached to the document
{% for i in mydoc.images.all %}
...
{% endfor %}
# or to get the document the image belongs to
{{ mydocimage.property.date_added }}
first you iterate over the result
the images related to a Doc are retrieved by the images property of doc which is generated from the related_name attribute in the ForeignKey
code:
{% for doc in result %}
{% for docimage in doc.images.all %}
{{ docimage.image.url }}
{% endfor %}
{% endfor %}

Categories