Show information based on users - django

I have a class called Schedule with a field (is this correct?) I've set using
admins = models.ManyToManyField(User). This field contains a list of users I can select multiples of.
In the view for the schedule, I show a bunch of information. I would like to show a couple additional things based on if the currently logged in user is included in the admins of that schedule being viewed.

According to Django philosophy, you should have your business logic within views and presentation logic in the template. So the computation if the logged user is among the admins should be done in a view, and if the user is, then what is displayed should be determined in the template. You can accomplish that by:
# views.py
def schedule(request, id):
schedule = get_object_or_404(Schedule, pk=id)
if request.user.is_authenticated():
is_admin = schedule.admins.filter(pk=schedule.pk).exists()
else:
is_admin = False
data = {
'schedule': schedule,
'is_admin': is_admin,
}
return render_to_response('template.html', data)
# template.html
{% if is_admin %}
<p>You are an admin of the schedule!</p>
{% else %}
<p>Sorry. You are not an admin of the schedule</p>
{% endif %}

Related

Alert user in template after they have successfully created/edited/deleted a post(object)

Summary: I am trying to create a job board website. I have a dashboard view that returns all job objects and displays them in a template. The user can navigate here just to view their posts and the user is also redirected here after creating a new post, editing a post or deleting a post. I would like to be able to alert the user "Your post was created/edited/deleted successfully" depending on the circumstance of how they got to this page, but am not sure the best way to go about. Below is how I implemented the functionality to alert the user a post has been created, but I don't think it was the best way of doing this.
The first view I created was the post_job view where a user can create a new job post. To mark if a job was new I thought to add a boolean field to the Job model:
class Job(models.model):
#...
new = models.BooleanField(default = True) # post is new by default, set to False later
and then do this in the dashboard :
#login_required
def dashboard(request):
jobs = Job.objects.all()
new_job = False # set to true if there is a new job ( would only be the case if the user got directed to this view after posting a job)
for job in jobs: # loop through jobs to see if any have new=True
if job.new:
new_job = True
job.new = False # set to false so it's not considered new next time dashboard is loaded
job.save()
return render(request, 'job/dashboard.html', {'jobs':jobs, 'new_job': new_job})
and in dashboard.html:
{% if new_job %}
<p>Your job was posted successfully</p>
{% endif %}
This works and successfully alerts the user only when they have just created a new post. However, I feel like there has to be a better way to implement this functionality then adding an edited field to the Job model and was about to say a deleted field but I guess the object wouldn't exist anymore. Anyway, if you can suggest anything to achieve this thanks for the help. Not sure if it's the right term but it seems like there would be a flagging system that alerts when an object is created/edited/updated being that this is fairly common?
Edit: Is there a way to pass additional variables when redirecting to the dashboard view? For instance here is the post_job view:
def post_job(request):
if request.method == 'POST':
form = JobForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.business= Business.objects.get(user=request.user)
instance.save()
return redirect('dashboard') # is there a way to tell the dashboard view the post_job view sent the user?
else:
form = JobForm()
return render(request, 'job/post_job.html', {'section': 'dashboard', 'form':form})
if there were I could just do this for the edit and delete views.
Django's messages framework is built for use cases exactly like this. Instead of having
new_job = True
before calling .save(), use:
messages.success(request, 'Job successfully created/updated/deleted.')
Then, in your html file, you'll have:
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
Also, I would suggest running some sort of clean/validation before calling .save(). If that validation fails, you can create an error or warning message to display. Documentation on the Django messages framework can be found here.

How to use models associated with a user in Django when rendering an HTML page

I'm in the learning stages of django. I just dived into a project to learn the framework and am having a series of questions throughout the process.
I basically want to have individual pages for users who create a task list and have them post tasks to their own page.
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserProfile(models.Model):
# This line is required. Links UserProfile to a User model instance.
user = models.OneToOneField(User)
# The additional attributes we wish to include.
website = models.URLField(blank = True)
# Override the __unicode__() method to return out something meaningful!
def __unicode__(self):
return self.user.username
class TaskItem(models.Model):
taskn = models.CharField(max_length = 400)
usern = models.ForeignKey(User)
In my template, if the user has their task entered, how do I call it to render onto the page?
My View:
def profile_page(request, username):
user = User.objects.get(username=username)
taskitems = user.taskn_set.all()
return render_to_response('profile.html', {}, context)
Current issue:
'User' object has no attribute 'taskn_set'
{{ request.user.taskitem_set.all }} would give you all the related task items. Now, to display it in your template:
{% for task_item in user.taskitem_set.all %}
{{ task_item.task_n }}
{% endfor %}
would display the list of tasks.
Here is the documentation on reverse-queries on foreign key (related_name) Also, read this
you would do something like this:
{% for task in user.taskitem_set.all %}
{{ task.task_n }}
{% endfor %}
This will fetch all TaskItem instances related to your user. (notice the extra database query)
While i don't know how your view works, i will assume that you are making the right checks to make sure that every user can only see his own tasks.
One performance trick you will find most useful is to use prefetch_related('taskitem_set'), this will prefetch the TaskItem instances as long as your UserProfile instance with one query:
user = User.objects.filter(id=user_id).prefetch_related('taskitem_set')
You can tune the code to match your preferences.
Hope this helps!

Django if statement to check value in manytomanyfield in for loop in template

In my project, users have a manytomanyfield of users they are following. When they look at the list of users who are following them, I want to show a Follow/Unfollow link depending on if they have that person in their following list. For example, if B is following A, and A is following B, then there should be an Unfollow link next to B's name when A views their following list.
unfortunately, it always says "(Follow)" and never gives me the "(Remove)" link even if I'm logged in as a user that's already following that user.
My userprofile model:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='user_object')
visible = models.BooleanField(default=True)
bio = models.TextField(null=True, blank=True)
avatar = models.FileField(upload_to=get_upload_file_name, null=True, blank=True)
following = models.ManyToManyField(User, related_name='following_list')
and the code I am trying to use in my template:
{% for f in followers %}
{{f.user.username}}
{% if thisuser.user_object.following.all == f.user %}
(Remove)
{% else %}
(Follow)
{% endif %}
{% endfor %}
In my views, I am sending:
def followers(request, user_id=1):
thisuser = request.user
userlookup = User.objects.get(id=user_id)
following = thisuser
followers = UserProfile.objects.filter(following=thisuser)
args = {'following': following, 'thisuser': thisuser, 'userlookup': userlookup, 'followers': followers}
args.update(csrf(request))
return render_to_response('followers.html', args)
Decorate your objects in your view to make life in your template easier:
from django.shortcuts import render
def followers(request, user_id=1):
user = User.objects.get(pk=user_id)
followers = UserProfile.objects.filter(following=request.user)
args = {}
args['following'] = request.user
args['userlookup'] = User.objects.get(pk=user_id)
args['followers'] = []
for f in followers:
user_status = (f, request.user.user_object.following_list.filter(pk=f.pk).exists())
args['followers'].append(user_status)
return render(request, 'followers.html', args)
The key part is this:
(f, request.user.user_object.following_list.filter(pk=f.pk).exists())
This is a 2-tuple, the first item is the user profile object, and the second item is either True or False if this user is being followed. The exists() queryset method returns True if the query would have returned results. I use this to flag each user object.
I then collect this "decorated" lists of user profile objects in a list, which is sent as the context variable followers to the template.
You should avoid doing complex logic in the templates, and whenever possible use the views to send extra information about your objects. This not only enhances the performance of your application (templates are not optimal for heavy processing and the most difficult part to debug); but also keeps your templates free from any logic - beyond what is required to display things.
Now, in your template:
{% for f,following in followers %}
{{f.user.username}}
{% if following %}
(Remove)
{% else %}
(Follow)
{% endif %}
{% endfor %}
You don't need to pass request.user to the template, instead make sure the request context processor is enabled in your settings:
import django.conf.global_settings as DEFAULT_SETTINGS
TEMPLATE_CONTEXT_PROCESSORS += DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS+(
'django.core.context_processors.request',
)

django check which users approved document

I have two models
class Document(models.Model):
name = models.CharField(max_length=250, blank=True)
class Approval(models.Model):
document = models.ForeignKey(Document)
user = models.ForeignKey(User, related_name='approval user')
status = models.IntegerField(choices=APPROVELS, default=1, help_text="Approved")
i want to list all document with current login user approval status, when a user approve/Abstain/Not Approved a document i am recording in Approval table otherwise there is no record about current user approval status in Approval table
Please help me with the view and template.
On view:
userApprovals = Approval.objects.filter( user = request.user )
or
userApprovals = request.user.approval_user_set.all()
forYourApproval = Document.objects.exclude( pk__in = [ a.document.pk for a in userApprovals ] )
and don't forget to include userApprovals on render_to_response:
return render_to_response(
"yourPage.html",
{
"userApprovals": userApprovals,
"forYourApproval": forYourApproval,
},
context_instance=RequestContext(request))
On template:
{% for approval in userApprovals %}
{{ approval.document.name }} status {{ approval.get_status_display }}
{% endfor %}
{% for document in forYourApproval %}
{{ document.name }} waiting for your approval.
{% endfor %}
Note: change related name to 'approval_user'.
It's not clear whether your requirements allow any user to approve a document, but I'm inferring that this is the case based on your models. If a Document can only be approved by a specific user, or set of users, or if a document is approved by multiple users, you will need to make some substantial changes to your model design.
If not, I would write a view like this:
from django.db.models import Q
def approval_view(request):
documents = Document.objects.filter(
Q(approval__id__isnull=True) | Q(approval__user=request.user))
return render_to_response(template_name, {'documents': documents})
This will return a context with documents that have no Approval record in the approval table OR who have been approved by request.user.
You will likely need some additional code to display the appropriate information for each document in documents. For example, a custom template filter that displays a document status ("For your Approval" or "Approved" or whatever) might be necessary. An example template fragment:
{% for document in documents %}
<li>{{ document.name }} - {{ document|approval_status }}</li>
{% endfor %}
As I said at the beginning, your requirements are not clear. Is every user to be given an opportunity to approve a document? Or do all documents get a single opportunity to be approved by any user? There is a big difference and this code reflects the latter assumption.
If every user is to be given an opportunity to approve a document and you need to display the current user's approval decision, then the above code will work with slight modification. I would probably modify the view to display all documents in some order:
documents = Document.objects.all()
And then use a custom template filter as above to display the approval status for the current user (passing in the user as an argument):
<li>{{ document.name }} - {{ document|approval_status:request.user }}</li>

How to get the list of the authenticated users?

I would like to display the list of the authenticated users.
On the documentation: http://docs.djangoproject.com/en/dev/topics/auth/
class models.User
is_authenticated()ΒΆ
Always returns True. This is a way to tell if the user has been authenticated. ...
You can know on the template side is the current User is authenticated or not:
{% if user.is_authenticated %}
{% endif %}
But I didn't found the way the get the list of the authenticated users.
Any idea?
Going along with rz's answer, you could query the Session model for non-expired sessions, then turn the session data into users. Once you've got that you could turn it into a template tag which could render the list on any given page.
(This is all untested, but hopefully will be close to working).
Fetch all the logged in users...
from django.contrib.auth.models import User
from django.contrib.sessions.models import Session
from django.utils import timezone
def get_all_logged_in_users():
# Query all non-expired sessions
# use timezone.now() instead of datetime.now() in latest versions of Django
sessions = Session.objects.filter(expire_date__gte=timezone.now())
uid_list = []
# Build a list of user ids from that query
for session in sessions:
data = session.get_decoded()
uid_list.append(data.get('_auth_user_id', None))
# Query all logged in users based on id list
return User.objects.filter(id__in=uid_list)
Using this, you can make a simple inclusion template tag...
from django import template
from wherever import get_all_logged_in_users
register = template.Library()
#register.inclusion_tag('templatetags/logged_in_user_list.html')
def render_logged_in_user_list():
return { 'users': get_all_logged_in_users() }
logged_in_user_list.html
{% if users %}
<ul class="user-list">
{% for user in users %}
<li>{{ user }}</li>
{% endfor %}
</ul>
{% endif %}
Then in your main page you can simply use it where you like...
{% load your_library_name %}
{% render_logged_in_user_list %}
EDIT
For those talking about the 2-week persistent issue, I'm assuming that anyone wanting to have an "active users" type of listing will be making use of the SESSION_EXPIRE_AT_BROWSER_CLOSE setting, though I recognize this isn't always the case.
Most reliable solution would only be the something you store when user logs in or logs out. I saw this solution and i think its worth sharing.
models.py
from django.contrib.auth.signals import user_logged_in, user_logged_out
class LoggedUser(models.Model):
user = models.ForeignKey(User, primary_key=True)
def __unicode__(self):
return self.user.username
def login_user(sender, request, user, **kwargs):
LoggedUser(user=user).save()
def logout_user(sender, request, user, **kwargs):
try:
u = LoggedUser.objects.get(user=user)
u.delete()
except LoggedUser.DoesNotExist:
pass
user_logged_in.connect(login_user)
user_logged_out.connect(logout_user)
views.py
logged_users = LoggedUser.objects.all().order_by('username')
Sounds similiar with this solution, you can create a custom middleware to do it. I found awesome OnlineNowMiddleware here.
Where you will get these;
{{ request.online_now }} => display all list of online users.
{{ request.online_now_ids }} => display all online user ids.
{{ request.online_now.count }} => display total online users.
How to set up?
Create file middleware.py where location of settings.py has been saved, eg:
projectname/projectname/__init__.py
projectname/projectname/middleware.py
projectname/projectname/settings.py
Then following this lines;
from django.core.cache import cache
from django.conf import settings
from django.contrib.auth.models import User
from django.utils.deprecation import MiddlewareMixin
ONLINE_THRESHOLD = getattr(settings, 'ONLINE_THRESHOLD', 60 * 15)
ONLINE_MAX = getattr(settings, 'ONLINE_MAX', 50)
def get_online_now(self):
return User.objects.filter(id__in=self.online_now_ids or [])
class OnlineNowMiddleware(MiddlewareMixin):
"""
Maintains a list of users who have interacted with the website recently.
Their user IDs are available as ``online_now_ids`` on the request object,
and their corresponding users are available (lazily) as the
``online_now`` property on the request object.
"""
def process_request(self, request):
# First get the index
uids = cache.get('online-now', [])
# Perform the multiget on the individual online uid keys
online_keys = ['online-%s' % (u,) for u in uids]
fresh = cache.get_many(online_keys).keys()
online_now_ids = [int(k.replace('online-', '')) for k in fresh]
# If the user is authenticated, add their id to the list
if request.user.is_authenticated:
uid = request.user.id
# If their uid is already in the list, we want to bump it
# to the top, so we remove the earlier entry.
if uid in online_now_ids:
online_now_ids.remove(uid)
online_now_ids.append(uid)
if len(online_now_ids) > ONLINE_MAX:
del online_now_ids[0]
# Attach our modifications to the request object
request.__class__.online_now_ids = online_now_ids
request.__class__.online_now = property(get_online_now)
# Set the new cache
cache.set('online-%s' % (request.user.pk,), True, ONLINE_THRESHOLD)
cache.set('online-now', online_now_ids, ONLINE_THRESHOLD)
Finally update your MIDDLEWARE inside file of projectname/projectname/settings.py:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
....
# Custom middleware
'projectname.middleware.OnlineNowMiddleware',
]
For other condition, you can also check the current user is online or not with:
{% if request.user in request.online_now %}
{# do stuff #}
{% endif %}
There is no easy, built-in way to do what you want that I know of. I'd try a combination of expiring sessions and filtering on last_login. Maybe even write a custom manager for that.