I have an UpdateView in which I edit a single object. When the form is valid I am redirecting the user to a list with many objects.
In this list I want to highlight the object that was just successfully edited. I know Django's message framework where I can transfer messages from a view to another but how can I send which id to highlight?
Messages aren't really the right solution. Instead, use the sessions framework.
# in editing view:
obj = data.save() # or whatever
request.session['edited_object_id'] = obj.id
return ....
# in list view:
highlighted_id = request.session['edited_object_id']
# and in template:
{% for obj in objects %}
<li class="{% if obj.id == highlighted_id %}">...</li>
{% endif %}
Related
I have a function to check if the user is a staff:
class VerificateUser():
def allowed_user(request):
if request.user.is_staff:
return True
In my template, I’m going to show this section only for staff users, but this is not working.
{% url if not allowed_user %}
<h1>
Welcome to the show
</h1>
If do something like it, works:
```html
{% if not request.user.is_staff %}
<h1>
Welcome to the show
</h1>
But I need to use a view function to clean my code because I’m probably going to add more conditionals.
Since you are using a class-based view then I would suggest updating the context dictionary which you could use in the html template to determine whether the user is allowed or not. For example, Within the views.py.
class VerificateUser():
# Update the context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Call the allowed_user() and return whatever value, passing that value it to the context variable
context['allowed_user'] = self.allowed_user()
return context
# Checking if the user is allowed here
def allowed_user():
if self.request.user.is_staff:
return True
return False
Now within the html file, you can reference that allowed_user from the context variable.
{% if allowed_user %}
<h1>Hi, you are allowed to see here...</h1>
{% endif %}
That should do the trick.
You can do this sort of thing in many ways, but simply you can do by the following way-
{% if request.user.is_authenticated %}
<p>Welcome,{{request.user.first_name}} </p>
{% endif %}
request object is by default available in all of your Django templates context.
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.
What i have:
In my Django configuration, I create a painting model in models.py
class Painting(models.Model):
Number = models.CharField(max_length=128)
When the right URL is called, views.py returns a HTML paged rendered with a SQL query.
def index(request):
painting_list = Painting.objects.order_by('id')[:5]
context_dict = {'paintings':painting_list}
return render(request, 'binaryQuestionApp/index.html', context=context_dict)
Now, I can access the Number fields in my HTML like so:
<div>
{% if paintings %}
<ul>
{% for painting in paintings %}
<li> {{ painting.Number }} </li>
{% endfor %}
</ul>
{% endif %}
</div>
What i want:
Next, I wish to be able to change the values on the website. Something like this
{{ painting.Number = 1}} // set the value to 1
{{ painting.Number += 1}} // or maybe take the current value and increment
Next, I would want to send the newly set values back to Django to update the Painings, probably with a POST request.
Question:
What is the best way to get the desired behavior?
You need to send a post request either with a form, which is probably the easiest, or javascript. Send the value that you want to change the Number field by and the pk of the model or something else which you can use to identify the model.
Then in that index view you can handle the post request.
def index(request):
painting_list = Painting.objects.order_by('id')[:5]
context_dict = {'paintings':painting_list}
if request.method == 'POST':
# You can add some validation here to check that the values aren't empty
pk = request.POST['pk'] # if this is what you posted
value = request.POST['value'] # value to change the number field by
painting = Painting.objects.get(pk=pk)
painting.Number += value
return render(request, 'binaryQuestionApp/index.html', context=context_dict)
You can also achieve this with a model form, but this method is probably the easiest one to go for right now.
P.S. I would suggest you make the number field an IntegerField instead of CharField if you want to change the value like this.
I think the best way is to create a new Post View with 2 fields, one for the painting id to modify and one for the new value.
In Rails if I want to setup a "context" which is basically objects that every view will need like the user object for a logged in user, or say a store/account/location object, how would I have this available on every view in the django framework?
In Rails I would do something like this:
class BaseController
before_action :setup_context
def setup_user
#user = # load user from db
#location = # load location for user
end
end
class HomeController < BaseController
def index
# I now can use the variables #user and #location
end
end
Does Django have these types of events that I can load objects and use them in all my views?
You can write your own context processor:
Study :
https://docs.djangoproject.com/en/dev/ref/templates/api/#writing-your-own-context-processors
http://www.b-list.org/weblog/2006/jun/14/django-tips-template-context-processors/
If I understand your question correctly, I think you're trying to do something like this. I'm still fairly inexperienced with Django myself, so take this with a grain of salt.
Basic example:
views.py
from django.shortcuts import render
from exampleapp.models import User # User object defined in your models.py
def index(request):
context = { 'users': User.objects.all() }
return render(request, 'exampleapp/example.html', context)
example.html
{% if users %}
{% for user in users %}
<p>{{ user.name }}</p>
{% endfor %}
{% else %}
<p>No users.</p>
{% endif %}
My apologies if I've missed the mark on your question.
I am trying to build a page that displays all the friend requests for a user. Everything seems to work until I get to the line where I render the page.
I am using Django.
View
def friend_requests(request):
current_user = request.user.username
user_id = (User.objects.get(username=current_user)).id
all_requests = FriendshipRequest.objects.filter(to_user_id=user_id)
friend_requests = []
for request in all_requests:
from_user_id = request.from_user_id
from_user_id_username =(User.objects.get(pk=from_user_id)).username
friend_requests.append(from_user_id_username)
# for some reason i'm getting an attribute error here
return render(request, 'Users/friend_requests.html', {'friend_requests': friend_requests})
Template (friend_requests.html)
{% for request in friend_requests %}
{{request}}
{% endfor %}
I think the variable request you create when looping through friend_requests is shadowing the context processor {{request}} which represents the current request client has made.
Change your variable name sth different than request and you'll probably be good to go:
{% for friend_request in friend_requests %}
{{friend_request}} object of the current user {{request.user}}
{% endfor %}