This is my model structure.
Institution
- name
- logo
- ....
Course
- Owner ( foriegnKey with Institution )
- name
- terms
- .....
So now, am just calling data's like.
courses = Course.objects.filter(name__icontains=query).values('id','name','terms','owner__name', 'owner__logo')
And trying to display the owner__logo as {{data.owner__logo.url}}. But its not working, as the img src shows (unknown). But when this Institution logo thing works on some other view when i directly call it. but when i call via relationship its not working.
If i use {{data.owner__logo}} then it just passes the url without the full path. How to make this work, please help !
An approach that worked for me after I've experimented is...
# Use the FK object id (owner_id) since you're dealing with a FK.
courses = Course.objects.filter(name__icontains=query).values('id','name','terms','owner_id')
If you should print the courses dictionary you'll see the fields it contains for each course. In this case, you'll see the FK with the id listed for that object. Example:
{'id': 10, 'owner_id': 7, 'name': 'Random', 'terms': 'Anything', ...}
What I did from here since the courses object is a dictionary, I've added a new key with with the value being the logo url.
for course in courses:
# First is to get the owner object with the course['owner_id']
owner = Institution.objects.get(id=course['owner_id'])
# From the owner object, you have access to the logo and it's URL
# Creating a new key called 'logo_url' and assigning the owner logo URL to it
course['logo_url'] = owner.logo.url
# Add the courses dictionary to the context variable
context['courses'] = courses
Then within your template, you can access the logo_url for each course.
{% for course in courses %}
<img src="{{ course.logo_url }}"/>
{% endfor %}
OPTION #2: A MORE EFFICIENT WAY
The next option here is to use a template filter to get the Institution object's logo url in just one go while you loop over the items in the template.
In your custom_tags.py file:
from django import template
from django.shortcuts import get_object_or_404
register = template.Library()
#register.filter
def get_logo_url(bypass, owner_id):
# owner = Institution.objects.get(id=owner_id)
# or
owner = get_object_or_404(Institution, id=owner_id) # Recommended
if owner:
url = owner.logo.url
else:
url = ""
return url
In your template and for loop.
{% load custom_tags %} # Must do to be able to use the custom filter
{% for course in courses %}
{% with logo_url=""|get_logo_url:course.owner_id %}
<img src="{{ logo_url }}" />
{% endwith %}
{% endfor %}
If you're not familiar with django custom tags and filters, you can see the doc here.
Related
Suppose I have a Item model, where Item objects can either be public (accessible to all users) or private (accessible only to authenticated users):
class Item(models.Model):
title = models.CharField(max_length=100)
is_public = models.BoleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
#...
secret_key = ...
class Meta:
# I need to keep items in order:
ordering = ('-created_at',)
What I need is to list all items using a generic.ListView - keeping the order - but hide the secret_key of those items with is_public=False for anonymous users.
So in the template, I hide the secret_key if the user is not authenticated, like:
{% if request.user.is_authenticated %}
<p>{{ item.title }} - {{ item.secret_key }}</p>
{% else %}
<p>{{ item.title }} - This item is private. Sign up to see the secret_key!</p>
{% endif %}
and the ListView is like:
class ItemListView(ListView):
model = Item
paginate_by = 10
I'm aware that I can send two separate querysets for non logged-in users to the template, one for public items and the other for private ones; but I'm not sure how can I keep the order ('-created_at') in this approach.
The question is:
Is it safe to send all the secret_keys to the template and hide them for non logged-in users there?
(if it is safe, then) Is there a more efficient way of doing this?
I tried overriding the get_queryset method of my ItemListView and move the if condition from template to there (I think this would increase the performance, right?). I handled the situation where the users is authenticated (simply return all the objects); but for non logged-in users, I thought about somehow joining two separate querysets, one holding the public items and the other holding only the title and created_at of private items; but I didn't find to keep the order in this approach:
class ItemListView(ListView):
model = Item
paginate_by = 10
def get_queryset(self):
if self.request.user.is_authenticated:
return Item.objects.all()
else:
# ???
This was only a minimal-reproducible-example; Actually in the project, I have multiple access_levels; Each user has an access_level, based on their plan (e.g. basic, normal, pro, etc.) and each Item has an access_level; And an I'm dealing with about +100K objects, fetched from different databases (postgresql - some cached on redis) so the performance really matters here. Also the system is up-and-running now; so I prefer less fundamental solutions.
Thanks for your time. Your help is greatly appreciated.
Is it safe to send all the secret_keys to the template and hide them for non logged-in users there?
Your template is rendered server-side, and the client only get the rendered markup, so yes, it is totally safe. Well, unless someone in your team messes with the template code of course ;-)
(if it is safe, then) Is there a more efficient way of doing this?
Just filter the queryset in your view - you don't need two distinct querysets, and filtering the queryset will not change it's ordering.
def get_queryset(self):
qs = super(ItemListView, self).get_queryset()
if not self.request.user.is_authenticated:
qs = qs.filter(is_private=False)
return qs
and in your template:
{# avoids doing the same constant lookup within the loop #}
{% with is_auth=request.user.is_authenticated %}
{# I assume the queryset is named "objects" in the context ?#}
{% for item in objects %}
<p>{{ item.title }}{% if is_auth %} - {{ item.secret_key }}{% endif %}</p>
{% endfor %}
{% endwith %}
EDIT: bdoubleu rightly mentions in his answer that his solution makes testing easier. If you only need fields from your model (no method call), you can also use QuerySet.values() instead:
def get_queryset(self):
qs = super(ItemListView, self).get_queryset()
fields = ["title", "created_at"]
if self.request.user.is_authenticated:
fields.append("secret_key")
else:
qs = qs.filter(is_private=False)
return qs.values(*fields)
This will also make your code a bit more efficient since it doesn't have to build full model instances.
Another option is to annotate the queryset to add an extra attribute for display_secret_key which is going to be more efficient than checking the user access level for each item in the queryset while templating.
from django.db.models import F, Value as V
class ItemListView(ListView):
queryset = Item.objects.all()
paginate_by = 10
def get_queryset(self):
annotations = {
'display_secret_key': V('')
}
if self.request.user.access_level == 'PRO':
annotations['display_secret_key'] = F('secret_key')
return (
super().get_queryset()
.annotate(**annotations)
)
Then in your template:
<p>{{ item.title }} - {{ item.display_secret_key }}</p>
You could use 2 Templates, one for the authenticated user one for the unauthenticated. (just overwrite the get_template_names() for authentication check and add something like _sectempl.html to the found name and add the appropriate copy of the template with the secret data)
But I would say with bruno desthuilliers that if you switched off the debug mode there could be no constellation where unauthenticated users see content within
{% with authenticated=request.user.is_authenticated %}
{% if authenticated %}
do secret stuff
{% endif %}
{% endwith %}
or
{% if request.user.is_authenticated %}
hide secret stuff for all the others
{% endif %}
If you got a complex user-grouping-combination outside the standard django user right management (where you could ask for user-permissions in templates) then I would write the user_status (your "plan" or accesslevel) into the user-session (while authentication) and check for this user_status in the output-function of the attribute of the object.
Sketch:
Use in template:
{% for i in object_list %}
{{ i.name}}, {{ i.print_secret }}
{% endfor %}
In the model you create a "print_secret"-method witch returns the secret according to the previous recorded user_status in the session-data.
I have a model which has a column categories in a Product table, which has one entry, 'beds'.
When I try to get a list of categories, or products (all of which have names, descriptions, etc), the only thing I can get returned is numbers.
This is the relevant view from my views.py
def product(request):
template = loader.get_template('/home/user/webapps/webapp/my_site/main_page/templates/main_page/product.html')
prods = xroduct.objects.values_list('product')
context={'prods': prods}
return HttpResponse(template.render(context))
This is my code within a django template to display data
{% for instance in prods %}
<li>{{ instance }}</li>
{% endfor %}
However, all that gets returned, going by HTML when viewing the webpage, is:
<li>(2,)</li>
<li>(1,)</li>
There should be a lot more information returned. Names, descriptions, etc. Why is this not being returned via my view?
edit: How xroduct is defined:
from oscar.core.loading import get_class, get_model
xroduct = get_model('catalogue', 'product')
Since we are talking about django-oscar there are number of things to understand:
To retrieve the models from oscar you need to use get_model which is their own implementation of dynamically importing the models of interest.
get_model fetched the models from https://github.com/django-oscar/django-oscar/blob/master/src/oscar/apps/catalogue/models.py whic are defined by https://github.com/django-oscar/django-oscar/blob/master/src/oscar/apps/catalogue/abstract_models.py
What you need to do if you want to list products and their information is the following:
from oscar.core.loading import get_model
Product = get_model("catalogue", "Product")
def list_products(request):
template = loader.get_template(...)
products = Product.objects.all()
context = {"products": products}
return HttpResponse(template.render(context))
And in the template you can simply access the instances like:
{% for instance in prods %}
<li>{{ instance.title }}</li>
{% endfor %}
I'm trying to pass an object to my HTML template consisting of the parent object and all child objects that relate to it. For instance:
Model Chamber:
class Chamber(models.Model):
chamber_name = models.CharField(max_length=100)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
Has ChamberProperty:
class ChamberProperty(models.Model):
chamber = models.ForeignKey(Chamber, on_delete=models.CASCADE)
property_name = models.CharField(max_length=50)
property_value = models.CharField(max_length=100)
user_defined = models.BooleanField(default=True)
They are two separate models because the customer can add as many properties as they want to their chamber.
In my views.py
class ChambersView(generic.DetailView):
template_name = 'pages/chambers.html'
def get(self, request):
user = User.objects.get(username=request.user)
customer = Customer.objects.get(user=user)
chambers_list = list(Chamber.objects.filter(customer=customer))
try:
chamber_properties = list(ChamberProperty.objects.filter(chamber__in=chambers_list).order_by('id'))
except:
chamber_properties = "No properties"
form = ChambersFilterForm(request=request)
return render(request, self.template_name, {'filter_form':form, 'chambers_list': chambers_list, 'chamber_properties': chamber_properties})
Now, that does get me a list of all chambers, and a list of all chamber properties. Except they're not linked to each other. I'm not sure how to build a list of related objects. I read about backwards relationships just now, but I don't seem to grasp how to use them.
I tried the following:
chambers_and_props = Chamber.chamberproperty_set.all()
And I get the following error:
AttributeError: 'ReverseManyToOneDescriptor' object has no attribute 'all'
So I'm not quite sure how to use it. The threads I saw mentioned that a relationship in Django automatically add a reverse on ForeignKeys and the usage should be parent.child_set.all() with child in lower caps.
I get a ReverserManyToOneDescriptor object, but not sure how to turn that into a usable list I can pass on to my HTML template.
Any ideas as to what I'm doing wrong?
Your query does not work because you have not specified which Chamber you want to get the backwards relation for.
However, this is not the right approach. Presumably you want the ChamberProperty so you can list them against each Chamber in the template. So, you should follow the relationship there in the template - there's no need to query ChamberProperty separately in the view at all.
{% for chamber in chamber_list %}
{{ chamber.chamber_name }}
{% for property in chamber.chamberproperty_set.all %}
{{ property.property_name }} : {{ property.property_value }}
{% endfor %}
{% endfor %}
You are getting the error because you are trying Chamber.chamberproperty_set on the model Chamber. It will work on individual chamber instances:
You can do this in the view:
for chamber in chambers_list
properties = chamber.chamberproperty_set.all()
Or in the template:
{% for chamber in chambers_list %}
{{ chamber }}
{% for property in chamber.chamberproperty_set.all %}
{{ property }}
{% endfor %}
Then, in your view, you can use prefetch_related to reduce the number of SQL queries:
chambers_list = Chamber.objects.filter(customer=customer).prefetch_related('chamberproperty_set')
I'm making a small site in Django with 'items' and 'characters' in a sqlite database based on the app in the 'first steps' on the documentation page. The tables (currently) follow a similar format so I want to try and make a flexible template. In my views.py, the two views are set up like so:
def characters(request):
sortby = 'lastname'
data_list = Characters.objects.order_by(sortby)
template = loader.get_template('database.html')
context = {
'data_list': data_list,
'directory': 'wiki/characters',
'sortby': sortby,
}
return HttpResponse(template.render(context, request))
def items(request):
sortby = 'name'
data_list = Clothes.objects.order_by(sortby)
template = loader.get_template('database.html')
context = {
'data_list': data_list,
'directory': 'wiki/items',
'sortby': sortby,
}
return HttpResponse(template.render(context, request))
The only difference being the sortby variable and the directory variable. The problem is, I would like to sort the characters by their lastname, and the items by their name. The issue arises in my template, specifically data.sortby:
{% if data_list %}
<ul>
{% for data in data_list %}
<li>{{data.sortby}}</li>
{% endfor %}
</ul>
{% else %}
<p>No characters,</p>
{% endif %}
If I manually reference the field I want without brackets it works fine, so I'm sure it's the string causing the issue. How do I sort by the field I specify in views.py?
Thanks in advance from a noob.
You can get an idea in this (a little outdated) answer, but the bottom line is that there is no built-in way to access attributes by name getattr()-style in django templates. So you would need to write a custom filter, as described in the django docs:
# app/templatetags/attr_tags.py
from django import template
register = template.Library()
#register.filter
def get_attribute(value, arg):
return getattr(value, arg)
Now you can do in your template:
{% load attr_tags %}
# ...
{{ data|get_attribute:sortby }}
# {{ data.sortby }} can't work because that tries to access
# an attribute named 'sortby', not an attribute with the name
# stored in the context variable of the name 'sortby'
I am new to Flask and SQLalchemy. Want to have web page with individual photo that shows all tags assigned to that photo.
I have SQLalchemy working fine with MYSQL db. Nicely retrieves records etc.
I need help with flask/SQLalchemy work flow:
model -> view (input from url eg view / photo id) -> template -> web page
The models are as follows:
class Photos(db.Model):
id = db.Column(db.Integer, primary_key=True)
filename = db.Column(db.String(100))
tags = db.relationship(
'Tags',
secondary=photo_tags,
backref='photos')
class Tags(db.Model):
id = db.Column(db.Integer, primary_key=True)
tagname = db.Column(db.String(100))
photo_tags = db.Table('photo_tags',
db.Column('tag_id', db.Integer, db.ForeignKey('tags.id')),
db.Column('photo_id', db.Integer, db.ForeignKey('photos.id'))
)
My view is as follows:
#app.route('/phototags/<int:id>')
##login_required
def phototags(id=None):
photo_tags = Tags.query.filter(Tags.photos).filter(id == id).all()
return render_template(
'phototags.html',
title='PhotoTags',
message='Your application description page.',
photo_tags = photo_tags
)
My template is as follows:
{% extends "layout.html" %}
{% block content %}
<h2>{{ title }}.</h2>
<h3>{{ message }}</h3>
{% for phototag in photo_tags %}
<div style="float:left; class=" img-responsive">
<p>photo</p>
<p></p>tag</p>
<img src="static/photos/{{ phototag.id }}" width="100" height="100">
</div>
{% endfor %}
{% endblock %}
I am pretty sure the models and association table/model are setup properly.
The template is not perfect, as it currently appears to show tag ids for photo ids. I have tried to do 'phototag.photo_id' or 'phototag.filename' but doesn't come out. Obviously view query is not putting that through.
So the view is what i need help with. Is the query correct? and is it getting url passed parameter for photo_id correctly?
My test data is simple. I have a single Photo record with photos.id = 1
This has 2 related Phototags records: phototags.id = 1 (tag.id = 1), phototags.id = 2 (tag.id = 2)
When I pass url http://localhost:5555/phototags/1 my view query passes tag ids, but changing the passed parameter always gets the same tag ids eg phototags/2 also gets the same two tags. So query is obviously not correct.
I have looked at scores of examples and they all subtly different than what I want. None of the examples/tutorials/SO questions/answers i have seen show how the model, view and template work together to get what I want. They are either:
getting opposite eg equivalent of photos by tag (want tags by photo)
are just query that gets the tags by photo (i don't even think i am getting that)
have another SQLalchemy notation that includes 'sessions' or 'base' instead of 'db.model'
What I want as output is a photo id so i can show photo and its name etc and also show a list of tags associated with that photo eg as a comma separated list.
I am stumped to find a demo/example/tutorial/github project that walks me through what i need for workflow.
Can anyone show me example of view that can get photo id from url parameter and then query my models to get the associated tags?
It seems more appropriate to query a photo instead of photo_tags from what you describe about your use case.
With the view function
#app.route('/phototags/<int:id>')
def phototags(id=None):
photo = Photo.query.get(id)
return render_template(
'phototags.html',
title='PhotoTags',
message='Your application description page.',
photo = photo
)
you can then display the photo and iterate its tags using the 'tags' relationship in the html template:
{% extends "layout.html" %}
{% block content %}
<h2>{{ title }}.</h2>
<h3>{{ message }}</h3>
<img src="static/photos/{{ photo.id }}" width="100" height="100">
{% for phototag in photo.tags %}
<div style="float:left; class=" img-responsive">
<p>photo</p>
<p></p>tag</p>
</div>
{% endfor %}
{% endblock %}
Note that I used the get function in the query for the Photo. It's basically the same as
Photo.query.filter(id == id).first()
Renaming the view function and the template from phototags to photo would also make sense.
If you wanted to display all images for a tag you have to reverse the logic, querying for a tag and iterating over the photos. Should be possible, because you defined a backref.