Django - Problem with DB query using foreign key - django

I'm using Django for a Web app and I have the following data model:
class classi(models.Model):
nome = models.TextField(null=True)
class Meta:
db_table = 'classi'
class users(models.Model):
name = models.TextField(null=True)
email = models.TextField(null=True)
password = models.TextField(null=True)
classe = models.ForeignKey(classi, db_column='classe', on_delete=models.CASCADE, null=True)
class Meta:
db_table = 'users'
class smartphone(models.Model):
marca = models.TextField(null=True)
modello = models.TextField(null=True)
possessore = models.ForeignKey(users, db_column='possessore', on_delete=models.CASCADE, null=True)
class Meta:
db_table = 'smartphone'
My goal is to show, on an HTML page, all classi, and for each classi all users and for each user all smartphone.
How can I implement my view.py and my html file?
The only solution that I found is to scan all table with a for loop and, through a condition, select the row using foreign key:
{% for c in classi %}
<p>{{ c.nome }}</p>
{% for u in users %}
{% if u.classe == c %}
<p>{{ u.name }}, {{ u.email }}, {{ u.password }}</p>
{% for s in smartphone %}
{% if s.possessore == u %}<p>{{ s.marca }}, {{ s.modello }}</p> {% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
Is there a better solution?

You could use backwards relations, which will only have items that have a connection
The general format is obj.{model}_set but you can also set it to something different in the models.py with the related_name attr in the ForeignKey field
{% for c in classi %}
<p>{{ c.nome }}</p>
{% for u in c.users_set.all %}
<p>{{ u.name }}, {{ u.email }}, {{ u.password }}</p>
{% for s in u.smartphone_set.all %}
<p>{{ s.marca }}, {{ s.modello }}</p>
{% endfor %}
{% endfor %}
{% endfor %}
Or, and this is probably cleaner:
Go the reverse way by looping through the smartphones in the top and just order smartphones by nome in the view with smartphoneList = smartphone.objects.all().order_by('possessore__classe__nome') so they are grouped together
{% for s in smartphoneList %}
<p>{{ s.possessore.classe.nome }}</p>
<p>{{ s.possessore.name }}, {{ s.possessore.email }}, {{ s.possessore.password }}</p>
<p>{{ s.marca }}, {{ s.modello }}</p>
{% endfor %}
Note after writing this I noticed #Lucas Grugru as already posted the reverse relations way.. From my testing it doesn't require pre-fetching, but it's probably a good idea to do that (I also have no idea). I'm still posting this because I think looping through smartphones might be a cleaner way of doing it

You can use *prefetch_related for prefetching related object from foreign key. More information: https://docs.djangoproject.com/fr/4.1/ref/models/querysets/#prefetch-related
The view:
def view(request):
classe = Classi.objects.all().prefetch_related("users_set").prefetch_related("users_set__smartphone_set")
context = {}
context["classe"] =) classe
return render(request, 'template.html', context)
And the html:
{% for c in classe %}
<p>{{ c.nome }}</p>
{% for u in c.users_set.all %}
<p>{{ u.name }}, {{ u.email }}, {{ u.password }}</p>
{% for s in u.smartphone_set.all %}
<p>{{ s.marca }}, {{ s.modello }}</p>
{% endfor %}
{% endfor %}
{% endfor %}

Related

In which variable is the model instance stored in customized view?

How can I actually pick up values from my model in a customised template?
Let's assume for this example, the model for Request has an attribute "title".
Following views.py:
class RequestModelView(ModelView):
datamodel = SQLAInterface(Request)
extra_args = {'my_extra_arg':Request}
show_template = 'show_request.html'
list_columns = ['title','description','request_status']
and here the show_requests.html
{% extends "appbuilder/general/model/show.html" %}
{% block show_form %}
This Text is before the show widget
{{ super() }}
This Text is shown below..
<hr>
{{ (self.title) }}; {{pk}}; {{pk['title']}}
{% endblock %}
In which variable can I find my object?
The only parameter that works is {{pk}} (it shows the ID of the Request).
I was thinking of something like item['title']
Thanks.
A very similar question was asked on their Github. I think you would have to use something along these lines:
views.py
class RequestModelView(ModelView):
datamodel = SQLAInterface(Request)
list_columns = ['title','description','request_status']
add_template = 'show_request.html'
#expose('/show_request', methods=['GET', 'POST'])
#has_access
def show_request(self):
get_filter_args(self._filters)
self.extra_args = {'item': list_columns}
return super(RequestModelView, self).add()
show_requests.html:
{% extends "appbuilder/general/model/show.html" %}
{% block show_form %}
This Text is before the show widget
{{ super() }}
This Text is shown below..
<hr>
{{ item['title'] }}
{% endblock %}
Documentation is certainly not very clear in this aspect, but please check template-extra-arguments
I'm not sure if you would still need extra_args = {'my_extra_arg':Request}.
The following example should make things clear:
from .models import cars
beautiful_names = ["John", "Jane", "Bob"]
flowers = ["Aster", "Buttercup", "Roses"]
class IndexView(View):
template_name = "dashboard/dashboard_index.html"
def get(self, request, *args, **kwargs):
all_car_objects=cars.objects.all()
return render(request,
self.template_name,
context={"my_addresslist": beautiful_names,
"my_rose_list": roses},
"all_cars":all_car_objects)
In your template you can then do something like:
<h1>Beautiful Names</h1>
{% for name in my_addresslist %}
<p>{{ name }}</p>
{% endfor %}
<h1>Roses</>
{% if my_rose_list %}
{% for rose in my_rose_list %}
<p>{{ rose }}</p>
{% endfor %}
{% endif %}
<h1>Cars</>
{% if all_cars %}
{% for car in all_cars %}
<div>
<p>{{ car.name }}</p>
<p>{{ car.make }}</p>
<p>{{ car.model }}</p>
</div
{% endfor %}
{% endif %}
So for your example, it comes down to this:
class MyView(ModelView):
datamodel = SQLAInterface(MyTable)
extra_args = {'my_request_list':Request}
show_template = 'show_request.html'
<h1>Requests</>
{% if my_request_list %}
{% for request in my_request_list %}
<p>{{ request }}</p>
{% endfor %}
{% endif %}

Django Fetching Data From DataBase Based on join Using ORM

I want to join two models as shown below and the join should be Harsha to Bank only(not Bank to Harsha)
model.py
class Harsha(models.Model):
name = models.CharField(max_length=255)
email = models.CharField(max_length=255)
class Bank(models.Model):
user = models.ForeignKey(Harsha, on_delete=models.CASCADE)
accountnumber = models.BigIntegerField()
ifsc = models.CharField(max_length=255)
branch = models.CharField(max_length=255)
bank = models.CharField(max_length=255)
views.py
test = Harsha.objects.all()
test1 = Bank.objects.all() # its working for me but i want join from Harsha table
in templates
# I want this
{% for tes in test %}
{{ tes.name }}
{{ tes.email }}
{{ tes.bank.accountnumber }} # how can I get this field
{{ tes.bank.ifsc }} # how can I get this field
{% endfor %}
# its working
{% for tes in test1 %}
{{ tes.user.name }}
{{ tes.user.email }}
{{ tes.accountnumber }}
{{ tes.ifsc }}
{% endfor %}
You can get it like this(using reverse relationship):
{% for tes in test %}
{{ tes.name }}
{{ tes.email }}
{% for bank in tes.bank_set.all %}
{{ bank.accountnumber }}
{{ bank.ifsc }}
{% endfor %}
{% endfor %}

combining two or more querysets from different models in django

Main model:
class MainCategory(models.Model):
title = models.CharField(max_length=120, unique=True)
App#1:
class Category(models.Model):
title = models.CharField(max_length=120, unique=True)
main_category = models.ForeignKey(MainCategory, default=1, related_name='car_category')
App#2:
class Category(models.Model):
title = models.CharField(max_length=120, unique=True)
main_category = models.ForeignKey(MainCategory, default=1, related_name='classifieds_category')
on home page I want a combined list of both category list items as follows.
{% for object in main_cat_list %}
{{ object.title }}
{% for item in object.car_category %}
{{ item.title }}
{% endfor %}
{% endfor %}
How I can insert classifieds category also inside this list?
If you merely want to also display the classified_category as you have the car_category.
{% for object in main_cat_list %}
{{ object.title }}
{% for item in object.car_category %}
{{ item.title }}
{% endfor %}
{% for item in object.classified_category %}
{{ item.title }}
{% endfor %}
{% endfor %}

Displaying Nested ManyToMany Relation

I'm having nested many to many relation in my models.py and I've got the display partially working. I have 2 questions:
Is there a way to simplify the presentation, e.g. by inlineformset?
How to I access nested context variables in the template form (see line {% for objective in selected_objectives %} )?
Please let me know if there is a way to make my question more clear
models.py
class Process(models.Model):
title = models.CharField(max_length=200)
desc = models.TextField('process description', blank=True)
def __str__(self):
return self.title
class Objective(models.Model):
process = models.ManyToManyField(Process, verbose_name="related processes", blank=False)
title = models.CharField(max_length=200)
desc = models.TextField('objective description', blank=True)
def __str__(self):
return self.title
class Risk(models.Model):
objective = models.ManyToManyField(Objective, verbose_name="related objectives", blank=False)
title = models.CharField(max_length=200)
desc = models.TextField('risk description', blank=True)
def __str__(self):
return self.title
views.py
#login_required
def detailed_list(request):
#context = RequestContext(request)
obj = []
ri = []
all_processes = Process.objects.order_by('id') #[:1]
for p_index,p in enumerate(all_processes):
obj.append(p.objective_set.all()) #appending objectives for each process
for o_index,o in enumerate(obj[p_index]):
ri.append(o.risk_set.all().values()) #appending risks for each objective
context = {'all_processes': all_processes,
'selected_objectives': obj,
'selected_risks': ri
}
return render(request, 'repository/detailed.html', context)
template detailed.html
<p>Create new Process
</p>
{% if all_processes %}
No: {{ all_processes|length }}
<ul>
{% for process in all_processes %}
<li>{{ process.title }} {{ forloop.counter0 }}</li>
<ul>
{% if selected_objectives %}
{% for objective in selected_objectives %}
<!-- see here --> <li>{{ objective.title }} {{ forloop.counter0 }} - {{ objective.desc }}</li>
{% endfor %}
{% else %}
<p>No objectives are available.</p>
{% endif %}
</ul>
{% endfor %}
</ul>
{% else %}
<p>No processes are available.</p>
{% endif %}
all you have to do is just pass the Process object as the context to your template
context = {'all_processes': all_processes}
and in you template :
<p>Create new Process
</p>
{% if all_processes %}
No: {{ all_processes|length }}
<ul>
{% for process in all_processes %}
<li>{{ process.title }} {{ forloop.counter0 }}</li>
<ul>
{% if all_processes.objective_set.all %}
{% for objective in all_processes.objective_set.all %}
<li>{{ objective.title }} {{ forloop.counter0 }} - {{ objective.desc }}
</li>
{% endfor %}
{% else %}
<p>No objectives are available.</p>
{% endif %}
</ul>
{% endfor %}
</ul>
{% else %}
<p>No processes are available.</p>
{% endif %}
<!-- this is if you want to show risks -->
{% for process in all_processes %}
{% for objective in all_processes.objective_set.all %}
{% for risk in objective.risk_set.all %}
{{ risk.desc }}
{% endfor %}
{% endfor %}
{% endfor %}
I hope this is what you were expecting !

ForeignKey. How to get data?

class Property(models.Model):
title = models.CharField(max_length=255)
class CurrentPrice(models.Model):
current = models.ForeignKey(Current)
prop = models.ForeignKey(Property)
price = models.DecimalField(max_digits=5, decimal_places=2)
class Current(models.Model):
name = models.CharField(max_length=20)
views.py:
...
p = Property.objects.all()
return render_to_response('index.html',{'p':p},context_instance=RequestContext(request))
How to get price of Property and display it in my template?
template:
{% for item in p %}
{{ item.title }}
{{ item.price }} # ???
{% endfor %}
I'm not sure what is your purpose/design of models, which doesn't look appropriate from what you have shown.
You will have many CurrentPrice per Property object, so in template you can do is
{% for item in p %}
{{ item.title }}
{% for cp in item.currentprice_set.all %}
{{ cp.price }}
{% endfor %}
{% endfor %}
If Property can have multiple CurrentPrice objects (what is by default):
{% for item in p %}
{{ item.title }}
{% for current_price in item.currentprice_set.all %}
{{ current_price.price }}
{% endofor %}
{% endfor %}
If only one (but in that case it is better to use o2o field instead of the FK fiel else it is up on you to prevent multiple CurrentPrice records pointing to the same Property):
{% for item in p %}
{{ item.title }}
{{ item.currentprice_set.get.price }}
{% endfor %}
I think what you're trying to do is something like that below.
class Property(models.Model):
title = models.CharField(max_length=255)
#property
def current_price(self):
# The current price is the last price that was given.
if self.pricing_history.count() > 0:
return self.pricing_history.order_by('-when')[0].amount
return None
class Price(models.Model):
prop = models.ForeignKey(Property, related_name='pricing_history')
amount = models.DecimalField(max_digits=5, decimal_places=2)
when = models.DateTimeField(auto_now_add=True)
example in template:
{% for item in p %}
{{ item.title }}
{{ item.current_price }}
{% endfor %}