using curly double brackets inside another curly double brackets in django template - sql

I started studying Django few days back, while doing a project I came across a situation.
In the views.py, I'm passing
def chat_rooms(request):
context = dict()
context['title'] = 'Chat'
rooms = Chat_rooms.objects.filter(Q(user1 = request.user.id) | Q(user2 = request.user.id) )
context['rooms'] = rooms
for room in rooms:
rmssg = Chats.objects.filter(room_id = room.id)
lmssg = Chats.objects.latest('id')
context[str(room.id)] = lmssg
return render(request,'previlageGroup/chat_rooms.html',context)
In the Django template,chat_room.html
{% for room in rooms %}
<div class="card" style="width: 100%;">
<a href="{% url 'pgroup:chat.screen' room.id %}">
<div class="card-body">
<p class="card-text">
{{ {{room.id}}.message}}
</p>
</div>
</a>
</div>
{% endfor %}
In models.py
class Chat_rooms(models.Model):
user1 = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user1')
user2 = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user2')
created_on = models.DateTimeField(auto_now_add=True)
class Chats(models.Model):
date = models.DateTimeField(auto_now_add=True)
has_viewed = models.BooleanField(default= False)
message = models.CharField(max_length = 200)
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sender')
receiver = models.ForeignKey(User, on_delete=models.CASCADE, related_name='receiver')
room = models.ForeignKey(Chat_rooms, on_delete=models.CASCADE)
Django is giving me a error, that {{ {{room.id}}.message }} is not possible.
How can I do this? Is there any other way? Thanks in advance

At the end of the day, a django Model object is just like any other object in python, so you could easily do:
for room in rooms:
rmssg = Chats.objects.filter(room_id = room.id)
lmssg = Chats.objects.latest('id')
room.last_msg = lmssg
And access {{ room.last_msg }} in your template. This might cause errors if you ever happen to implement a last_msg field though.
Perhaps a more elegant solution would be implementing a related_name to your room field. If you change the last line of your views.py to:
room = models.ForeignKey(Chat_rooms, on_delete=models.CASCADE, related_name="chats")
You would be able to do things like:
room.chats.all() #get all messages from a chatroom
And, in your templates, you could do:
{{ room.chats.last }}
To get the last message in the chat.

Related

Django template "image.url" blank when "QuerySet" contains multiple object

I'm having problem with Django templates not receiving any data in image.url when having more than one object in context QuerySet all other data is working fine. If there is only one object in QuerySet, image.url works fine. I'm storing images on S3 and there is nothing wrong with the images or the bucket permission.
I'm new with Django templates is there anything I'm missing?
Here is the code:
models.py
`
from django.db import models
from django.core.validators import MaxValueValidator, MinValueValidator
from django.utils.translation import gettext_lazy as _
from site_content.models import PageCategory, Page
from site_content.utils import file_upload_to
class PpcOffersPagesManager(models.Manager):
def get_queryset(self):
return super(PpcOffersPagesManager, self) \
.get_queryset() \
.filter(page_category__category_name="ppc-offer-page")
class PpcOfferPages(Page):
objects = PpcOffersPagesManager()
class Meta:
proxy = True
def __str__(self):
return self.page_name
def save(self, *args, **kwargs):
self.page_category = PageCategory.objects.get(category_name="ppc-offer-page")
self.page_site_type = "ppc"
super(PpcOfferPages, self).save(*args, **kwargs)
class PpcOfferContent(models.Model):
related_page = models.ForeignKey(
Page, verbose_name=_("Page"),
related_name="ppc_offer_content",
on_delete=models.SET_NULL,
null=True
)
order = models.IntegerField(verbose_name=_("Order of offer appearance"), default=0)
image = models.ImageField(
verbose_name=_("Casino banner image"),
upload_to=file_upload_to,
null=True,
blank=True,
help_text="Casino banner image, Size (300x500)"
)
header = models.CharField(
verbose_name=_("Header text"),
max_length=500,
null=False,
blank=False
)
is_active = models.BooleanField(verbose_name=_("Is offer active"), default=True)
class Meta:
verbose_name = _('PPC_Offer Page Content')
verbose_name_plural = _('PPC_Offer Page Content')
def __str__(self):
return self.related_page.page_name
`
views.py
`
from django.views.generic import DetailView
from ppc_site_control.models import PpcOfferContent
class PpcOffersDetailView(DetailView):
model = PpcOfferContent
def get_template_names(self):
page = self.model.objects \
.filter(related_page__slug = self.kwargs['slug']) \
.first().related_page.page_template.template_path
return page
def get_object(self):
return self.model.objects \
.filter(related_page__slug=self.kwargs['slug']) \
.filter(is_active=True) \
.order_by('offer_order')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
`
.html
`
{% extends "slotsselection/base.html" %}
{% load i18n static reviews sstags %}
{% for offer in object %}
<div>
<div >
<img loading="lazy" src="{{offer.offer_image.url}}" alt="image">
</div>
<div>
<p>{{offer.header}}</p>
<p>{{offer.is_active }}</p>
</div>
<div>
{% endfor %}
`
I tried sending data in the context as new key and adding QuerySet object again with .values()(to exclude possibility of problem with "get_object" query) still have the same result. When I remove the images from template, all other data is displayed accordingly, without images.
I also tried with ListView same error
ValueError The 'image' attribute has no file associated with it.
Data in context:
`{'object': <QuerySet [<PpcOfferContent: PPC PageName>,
<PpcOfferContent: PPC PageName>, <PpcOfferContent: PPC PageName>,
<PpcOfferContent: PPC PageName>]>, 'view':
<ppc_site_control.views.PpcOffersDetailView object at 0x7f2d8230f430>}
`
The problem was caused from missing images because they were not required in the model the simplest solution is adding "if" condition in the template:
`{% if offer.offer_image %}
<img loading="lazy" src="{{offer.image.url}}" alt="Casino image">
{% endif %}`
I also manage to find workaround:
views.py
`from slotsselection.components.s3_settings import MEDIA_URL`
then in the context data I added
`def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['media_url'] = MEDIA_URL
return context
`
.html
and in html I removed .url to get the half of the link as string and added media_url
`<img loading="lazy" src="{{media_url}}{{offer.offer_image}}" alt="Casino image">`
not best approach but it works to.

Foreign key django - Database Relationships

I have a model for the Product Perfume, the product have different volume and prices.
I need to render the volume and price for each but i get "'function' object has no attribute 'prices'"
Any ideas? iam grateful for any suggestions
View:
from django.shortcuts import render
from django.views.generic import View, TemplateView
from products.models import Perfume, Pricing
def getIndex(request):
perfumes = Perfume.objects.all
thePrice= perfumes.prices.all()
return render(request, 'index.html', {'perfumes': perfumes, 'thePrice':thePrice})
Model
from django.conf import settings
from django.db import models
from django.utils import timezone
class Perfume(models.Model):
genderChoice = (
('unisex','unisex'), ('male', 'male'), ('female', 'female'))
name = models.CharField(max_length=50, default='')
brand = models.CharField(max_length=40, default='')
gender = models.CharField(max_length=7, choices=genderChoice, default='unisex')
description = models.TextField()
image = models.ImageField(upload_to='images/product_image')
created = models.DateField()
author =models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
active = models.BooleanField(default=False)
show_for_consumer = models.BooleanField(default=False)
def __str__(self):
return self.name
class Pricing(models.Model):
product =models.ForeignKey(Perfume, on_delete=models.CASCADE,related_name='prices')
price= models.DecimalField(max_digits=10, decimal_places=2)
volume= models.DecimalField(max_digits=10, decimal_places=2)
def __str__(self):
return 'Perfume {} - Price{} - Volume {}'.format(self.product.name,self.price, self.volume)
First, you need to correct the missing () in getting the perfumes queryset
perfumes = Perfume.objects.all()
This thePrice = perfumes.prices.all() is incorrect. You have to do this for every perfume, not for a queryset of perfumes.
Since Perfume and Pricing have a one-to-many relationship, you can directly access the prices from every perfume instance. This way you only need to pass the perfumes queryset
def getIndex(request):
perfumes = Perfume.objects.all()
return render(request, 'index.html', {'perfumes': perfumes})
Finally, in your template, you can call the prices like this
{% for product in perfumes %}
...
{% for p in product.prices.all %}
<option>{{p.volume}}ml - {{p.price}}$ </option>
...
{% endfor %}
{% endfor %}

Flask-Admin - Inline Model not working with field named something other than 'id'

I am looking at inline models and have been testing out the example here:
https://github.com/mrjoes/flask-admin/tree/master/examples/sqla-inline
I have noticed that if the primary key field of the LocationImage model/table is renamed to something other than ID, then the after_delete handler does not get triggered.
So this works
class LocationImage(db.Model):
id = db.Column(db.Integer, primary_key=True)
alt = db.Column(db.Unicode(128))
path = db.Column(db.String(64))
location_id = db.Column(db.Integer, db.ForeignKey(Location.id))
location = db.relation(Location, backref='images')
#event.listens_for(LocationImage, 'after_delete')
def _handle_image_delete(mapper, conn, target):
try:
if target.path:
os.remove(op.join(base_path, target.path))
except:
pass
But if I rename the id column like so,
image_id = db.Column(db.Integer, primary_key=True)
Then _handle_image_delete does not get called.
I cannot fathom where this field is specified and how to make it work with a PK named something other than 'id'.
Thank you
In field_list.html a hidden field is rendered for the primary key. You need to change it to output your renamed field {{ field.form.image_id }}.
{% import 'admin/model/inline_list_base.html' as base with context %}
{% macro render_field(field) %}
{% set model = field.object_data %}
{% if model and model.path %}
{{ field.form.image_id }}
<img src="{{ url_for('static', filename=model.path) }}" style="max-width: 300px;"></img>
{% else %}
{{ field }}
{% endif %}
{% endmacro %}
{{ base.render_inline_fields(field, template, render_field) }}

How access tuple item

models.py
class MyModel(models.Model):
GENDER = (('M',"Male"),('F',"Female"))
gender = models.CharField(max_length = 1, choices = GENDER)
template.html
{% for item in mymodels %}
{{ item.GENDER[item.gender] }} #HOW TODO?
{% endfor %}
You can find some answers here
I think that best bet is to use helper function that returns proper value or extend/write new filter.

Django: Multiple COUNTs from two models away

I am attempting to create a profile page that shows the amount of dwarves that are assigned to each corresponding career. I have 4 careers, 2 jobs within each of those careers and of course many dwarves that each have a single job. How can I get a count of the number of dwarves in each of those careers? My solution was to hardcore the career names in the HTML and to make a query for each career but that seems like an excessive amount of queries.
Here's what I "want" to see:
Unassigned: 3
Construction: 2
Farming: 0
Gathering: 1
Here's my models. I add some complexity by not connecting Careers directly to my Dwarves model (they have connected by their jobs).
from django.contrib.auth.models import User
from django.db import models
class Career(models.Model):
name = models.CharField(max_length = 64)
def __unicode__(self):
return self.name
class Job(models.Model):
career = models.ForeignKey(Career)
name = models.CharField(max_length = 64)
career_increment = models.DecimalField(max_digits = 4, decimal_places = 2)
job_increment = models.DecimalField(max_digits = 4, decimal_places = 2)
def __unicode__(self):
return self.name
class Dwarf(models.Model):
job = models.ForeignKey(Job)
user = models.ForeignKey(User)
created = models.DateTimeField(auto_now_add = True)
modified = models.DateTimeField(auto_now = True)
name = models.CharField(max_length = 64)
class Meta:
verbose_name_plural = 'dwarves'
def __unicode__(self):
return self.name
EDIT 1
my view looks something like:
def fortress(request):
careers = Career.objects.annotate(Count('dwarf_set'))
return render_to_response('ragna_base/fortress.html', {'careers': careers})
and template:
{% for career in careers %}
<li>{{ career.dwarf_set__count }}</li>
{% endfor %}
The error is:
Cannot resolve keyword 'dwarf_set' into field. Choices are: id, job, name
SOLUTION
view:
def fortress(request):
careers = Career.objects.all().annotate(dwarfs_in_career = Count('job__dwarf'))
return render_to_response('ragna_base/fortress.html', {'careers': careers})
template:
{% for career in careers reversed %}
<li>{{ career.name }}: {{ career.dwarves_in_career }}</li>
{% endfor %}
EVEN BETTER SOLUTION
careers = Career.objects.filter(Q(job__dwarf__user = 1) | Q(job__dwarf__user__isnull = True)) \
.annotate(dwarves_in_career = Count('job__dwarf'))
Don't forget to from django.db.models import Count, Q
What I like about the above solution was it not only returns careers that have dwarves working but even the careers that have none which was the next problem I encountered. Here's my view for completeness:
<ul>
{% for career in careers %}
<li>{{ career.name }}: {{ career.dwarves_in_career }}</li>
{% endfor %}
</ul>
Django's ORM isn't gonna make this uber-simple. The simple way is to do something like:
for career in Career.objects.all():
career.dwarf_set.all().count()
That will execute 1 query for each job (O(n) complexity).
You could try to speed that up by using Django's Aggregation feature, but I'm not entirely sure if it'll do what you need. You'd have to take a look.
The third option is to use custom SQL, which will absolutely get the job done. You just have to write it, and maintain it as your app grows and changes...
Does this do what you want?
from django.db.models import Count
Career.objects.annotate(Count('dwarf'))
Now each career object should have a dwarf__count property.
Can't you just get a count grouped by career? And do an outer join if you need the zero rows returned too.