I have built an application that shows users their storage usage and quotas on a system. Since there are many users, sifting through their storage allocations can be tedious so I want to give admins the option of acting as that user. The application decides which user is accessing the application based on an employee ID received via a secure badg, the variable (EMPLOYEE_ID) is stored in the request.META dictionary.
Ideally, I want the admins to be able to override this employee ID with another user's ID by posting it in a form. The form works and then serves the storage_home.html page as the employee the admin wishes to act as via a POST request, but when I or another admin clicks and does a GET for the quotas, the request.session dictionary is empty!
EMPLOYEE_ID is the original employee id of the admin
SIM_EMPLOYEE_ID is the employee the admin wishes to act as
I wonder if it's the way I'm linking to the quotas view in the storage_home.html template? Not sure.
Here is my code, I believe you should only need views, and the template that calls the quotas view function to see what the issue is since the request.sessions dictionary does have the SIM_EMPLOYEE_ID variable after the post that serves storage_home.html. I've omitted some variables from the views that are used in the template, but they work just fine, didn't want to clutter the code too much.
The sim_user function is called when the form is submitted. This then just recalls the storage function and right now successfully displays what I want it to, it's the GET request subsequently that fail to keep the session. I also have the following set in my settings:
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_DOMAIN = '.mydomain.com'
SESSION_SAVE_EVERY_REQUEST = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
views.py
def home(request):
"""Redirect requests at root url to /storage"""
return HttpResponseRedirect('/storage/')
def storage(request):
"""Return the home template."""
context = {}
context.update(csrf(request))
empid = request.session.get('SIM_EMPLOYEE_ID')
if not empid:
empid = request.META.get('EMPLOYEE_ID')
if functions.is_admin(empid):
form = UserForm()
context['form'] = form
template = loader.get_template('storage_admin.html')
else:
template = loader.get_template('storage_home.html')
data = RequestContext(request, context)
return HttpResponse(template.render(data))
def sim_user(request):
context = {}
context.update(csrf(request))
if request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
empid = form.cleaned_data['empid']
request.session['SIM_EMPLOYEE_ID'] = empid
request.session.modified = True
return storage(request)
template = loader.get_template('deny.html')
data = RequestContext(request, context)
return HttpResponse(template.render(data))
def quotas(request, sitename):
"""Return quota page depending on the
id of the employee. If employee is an
administrator, show all the quota information
for all users/projects. If employee is a user
of the sitename, show them user specific quota information.
Otherwise, deny access and display a custom template."""
context = {}
site = sitename.capitalize()
# EMPLOYEE_ID is in the Http Request's META information
empid = request.session.get('SIM_EMPLOYEE_ID')
if not empid:
empid = request.META.get('EMPLOYEE_ID')
if not empid:
template = loader.get_template('deny.html')
return HttpResponse(template.render(RequestContext(request, context)))
if functions.is_admin(empid):
template = loader.get_template('all_quotas.html')
else:
template = loader.get_template('personal_quotas.html')
data = RequestContext(request, context)
return HttpResponse(template.render(data))
storage_home.html
{% extends 'base.html' %}
{% block title %}Storage Utilization{% endblock %}
{% block content %}
<h1 id="header"><b>Storage Utilization</b></h1>
<p></p>
<table id="storage_table" cellspacing="15">
<tbody>
{% for site in sites %}
{% url "su.views.quotas" as quota %}
<tr>
<td><img src="/static/images/{{ site }}.png"></td>
</tr>
{% endfor %}
</tbody>
</table>
<br></br>
{% endblock %}
Thanks for any help, please let me know if you need more explanation, code, or simplification.
Turns out removing SESSION_COOKIE_SECURE = True fixed the issue. This is my fault for not forgetting that my dev environment uses http and prod https. I actually have separate settings files, but failed to use them properly when I went back to test this new feature. I believe setting the SESSION_COOKIE_SECURE to True when using https should work once I test the production server.
Django provided session stopped working for me for some reason. I made my own it's really easy:
models.py
class CustomSession(models.Model):
uid = models.CharField(max_length=256)
def __str__(self):
return self.uid
How to work with CustomSession
from oauth.models import CustomSession
session = CustomSession.objects # get a list of session objects
new_user = CustomSession(uid=<UID>) # save a user to the session (by uid)
session.get(id=<ID>).uid # get user id
session.get(id=<ID>).delete() # delete user from session (logout)
session.all().delete() # delete all user data in session
Related
What is the preferred way of pre-populating database (Model) objects in a Django app? I am inclined to try to script POSTing data to the relevant endpoints, but am being stymied by the CSRF protection.
This is not part of the testing framework, this is for setting up demonstration and training instances in a beta testing or production environment.
As a notional example, I'd like to populate the the "Player" database
with three entries: Alice(sender), Bob(reciever) and Charlie(eavesdropper), and I'd like to script/automate the process of creating these entries after deploying and starting the application.
I already have a form based mechanism for creating new Players. By visiting /create via a browser, there is a form that allows a person to type in the name, e.g. "Bob" and a role, e.g. "reciever", and submit the form to create the new Player instance.
Thus it makes sense to me to try to try to use the existing web API for this: e.g. make calls like
requests.post('0.0.0.0:8000/create', data={'name':'Bob', 'role':'reciever'}) in the script that pre-populates the database. However doing this results in 403 errors due to the CSRF tokens, which I don't want to disable. This problem also occurs if I just try to use a requests.Session to try to maintain the cookies between calls.
One viable solution would involve effectively managing the cookies involved to allow for posting data. However, I'm open to different ways to allow for creating model instances for initial system configuration.
Relevant code snippets:
def create(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = PlayerForm(request.POST)
# check whether it's valid:
if form.is_valid():
data = form.cleaned_data
s = models.Player()
s.name = data['name']
s.role = data['role']
s.save()
msg = "TODO: make a senisble return message"
return HttpResponse(msg)
else:
msg = "TODO: make invalid sources message"
return HttpResponse(msg)
# if a GET (or any other method) we'll create a blank form
else:
form = PlayerForm()
return render(request, 'player/create.html', {'target':'/create', 'form': form})
class Player(Model):
name = models.CharField(max_length=168)
role = models.CharField(max_length=64)
class PlayerForm(forms.Form):
name = forms.CharField(label='Name:', max_length=168)
role = forms.CharField(label='Role:', max_length=64)
Note that the 'target':'/create' is the target for the form's submit action, i.e. when the user hits "Submit" the data from the form are posted to this endpoint (which then hits the if request.method == 'POST' branch to create and save the new instance.
The form is just
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="">
<style>
</style>
<script src=""></script>
<body>
<form action="{{target}}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
</body>
</html>
Just started Django last week and I am facing this problem regarding two types of users (normal and VIP) and how to restrict normal users from viewing VIP contents.
I am using Django with MySQL. My site does not requires user to signup but instead, I will be giving them username/password. I manually creates user via Django admin and group them according to VIP or normal. I will be uploading contents (again via admin) and I have a Boolean check-box that determine whether it is a VIP content or not.
What I done so far:
User must login to view the contents
Upon logging in, VIPs can see VIP contents and normal users will not be able to see VIP contents.
FAQ.html
{% extends 'base.html' %}
...skipping...
{% for faqs in fq reversed %}
{{ faqs.name }}
{{ endfor }}
faq_topics.html
<h2>{{ faqs.name }}</h2>
<h6 style="padding: 20px 20px 20px 20px">{{ faqs.description|linebreaksbr }}</h6>
urls.py
urlpatterns = [
path('faq/', views.faq, name='faq'),
url(r'^faq/(?P<pk>\d+)/$', views.faq_topics, name='faq_topics'),]
views.py
def is_user(user):
return user.groups.filter(name='User').exists()
def is_vip(user):
return user.groups.filter(name='VIP').exists()
#login_required
#user_passes_test(is_user)
def faq(request):
fq = FAQ.objects.filter(vip_only=False)
return render(request, 'faq.html', {'fq':fq})
models.py
class FAQ(models.Model):
name = models.CharField(max_length=30, unique=True)
description = models.CharField(max_length=300)
vip_only = models.BooleanField(default=False)
Error Case
3 FAQ entries are made, the first one is classified as VIP only.
When a normal user log in into FAQ section, he/she will only see 2 contents
Welcome to FAQ section
127.0.0.1:8000/faq/3/
127.0.0.1:8000/faq/2/
However, the user can easily guess that faq/1/ exists and when they actually tries, they can have access to vip contents too.
I have been searching the web for the past 2 days, I learn how to user #user_passes_test to restrict users and objects.filter(vip_only=False) to restrict contents.
Additionally, I wish to have my views.py controlling the contents for both kind of user but I have yet to implement that yet. Will be very grateful if someone can teach me how to implement "2 views in one templates".
I have read from,
show different content based on logged in user django
django - 2 views in one template
My solution for splitting user
#login_required
def faq(request):
'''user is separate using groups'''
if request.user.is_superuser or request.user.groups.filter(name='VIP').exists():
fq = FAQ.objects.all()
else:
fq = FAQ.objects.filter(vip_only=False)
return render(request, 'faq.html', {'fq':fq})
You should do something like this
def faq(request):
if request.user.is_superuser:
fq = FAQ.objects.all()
else:
fq = FAQ.objects.filter(vip_only=False)
return render(request, 'faq.html', {'fq':fq})
You can also use similar approach for doing things in the template:
{% if user.is_superuser %}
#...show what you only want to show the superuser
{% endif %}
And in your method for the single faq, do something like:
def faq_topics(request, pk):
faq_item=FAQ.objects.get(id=pk)
if not request.user.is_superuser and not request.user.groups.filter(name='VIP').exists():
if fqa_item.vip_only:
return render(request, 'path_to_access_error_template', {'fq':faq_nr})
return render(request, 'faq_item.html', {'fq':faq_nr})
I have a app called dashboard which is where I redirect all logged in users with an option to add articles by the user.
After the user hits Submit button in the form, the data is sent to /dashboard/article/save URL via POST and after the data is stored, the view returns HttpResponseRedirect to show_dashboard which renders dashboard.html with a session variable result.
In the dashboard template file, I have added a notify.js code to show acknowledgements to user. The problem is if this session var is defined, everytime the dashboard page is showed, the notification is triggered EVEN if the user didn't add an article.
(I'm new to using web frameworks so I do not know how this all works properly)
Some code:
dashboard/models.py:
class Article(models.Model):
id = models.IntegerField(primary_key=True)
ar_title = models.CharField(max_length=25)
ar_data = models.CharField(max_length=500)
user = models.ForeignKey(User,on_delete=models.CASCADE)
def getArticleTitle(self):
return self.title
def getArticleData(self):
return self.title
def getArticleAuthor(self):
return self.user
dashboard/urls.py:
urlpatterns = [
url(r'^$', views.show_dashboard,name='home_dashboard'),
url(r'^profile/save/', views.save_profile,name="save_profile"),
url(r'^newsfeed/', views.get_newsfeed,name="newsfeed",),
url(r'^profile/', views.show_profile,name="show_profile"),
url(r'^article/save/', views.add_new_article,name="add_new_article"),
]
dashboard/views.py:
#login_required
def show_dashboard(request):
return render(request,'dashboard/dashboard.html',{'form':NewArticleForm()})
def add_new_article(request):
if(request.method == 'POST'):
ar_title= request.POST['ar_title']
ar_data = request.POST['ar_data']
user = request.user
form = NewArticleForm(request.POST)
if(form.is_valid()):
Article.objects.create(ar_title=ar_title,ar_data=ar_data,user=user)
request.session["result"] = "add_article_OK"
return HttpResponseRedirect(reverse('home_dashboard'))
dashboard.html:
{% ifequal request.session.result 'add_article_OK' %}
<script>
$.notify("New article added successfully",
{position:"bottom right","className":"success","autoHide":"yes","autoHideDelay":"3000"});
</script>
{% endifequal %}
Now, how do I remove this session value after it has displayed the message? I know del request.session['result'] can be issued but where can I put it in this heirarchy of moves?
Do it in the show_dashboard view.
Instead of getting the value from the session in the template, pop it in the view and pass it to the template; that way you take care of getting and clearing it in one go.
#login_required
def show_dashboard(request):
context = {
'form': NewArticleForm(),
'result': request.session.pop('result', None)
}
return render(request,'dashboard/dashboard.html',context)
...
{% ifequal result 'add_article_OK' %}
I'm trying to go inside of my database and see if a certain submission has been submitted before and if it has stop it from being submitted again. Currently I have this bit of code testing each field of the form (obviously changes per field but i thought I'd only show one field for simplicity)
if request.method == 'POST':
Cashtexts.objects.filter(cashTexts=request.POST['cashTexts']).exists() == False:
Then if it does not exist it goes on and save the submissions into the database. If it returns true it goes to an else statement that tells the user that what they entered has already been entered. previously this type of thing worked but I changed some variable names and now it stopped working. I've been browsing the code over a few time so I thought maybe something was fundamentally wrong with this type of filter but it makes sense in my head.
def submit_win(request):
if request.method == 'POST':
if Cashtexts.objects.filter(cashTexts=request.POST['cashTexts']).exists() or Cashtexts.objects.filter(superPoints=request.POST['superPoints']).exists() or Cashtexts.objects.filter(varolo= request.POST['varolo']).exists() or Cashtexts.objects.filter(swagbucks = request.POST['swagbucks']).exists() or Cashtexts.objects.filter(neobux = request.POST['neobux']).exists() or Cashtexts.objects.filter(topline=request.POST['topline']).exists() or Cashtexts.objects.filter(Paidviewpoint=request.POST['Paidviewpoint']).exists() or Cashtexts.objects.filter(cashcrate=request.POST['cashcrate']).exists() == False:
form = CashtextsForm(request.POST)
if form.is_valid():
form.save()
ref_create = Cashtexts.objects.filter(cashTexts=request.POST['cashTexts'])
return render_to_response('submitted_page.html', {'ref_create': ref_create})
else: #Error message reading wither you didnt insert codes or you enter the same code twice
error = 'That code has already been submitted'
ref_create = CashtextsForm()
return render_to_response('submit.html', {'ref_create': ref_create,'error':error}, context_instance=RequestContext(request))
else: #displays the page when the user asks to go to the submit page
ref_create = CashtextsForm()
return render_to_response('submit.html', {'ref_create': ref_create}, context_instance=RequestContext(request))
Also, I turned Cashtexts.objects.filter(cashTexts=request.POST['cashTexts']) intoa variable and passed it to my template to see what it was returning and it was returning objects that should have told the statement True. but it just seemed to ignore them and submit them anyway.
I suppose i could delete all the previous objects that match the thing they entered upon submission but that makes it less secure and could lead to users submitting over and over again thinking that they are getting it more in there. I'd rather just stop it before it happens.
You can just set the definition of your cashTexts field to unique in the models definition:
def Cashtexts(models.Model):
name = CharField(max_length=50, unique = True)
Just set the "unique" argument to true for each field you want to be unique and you're done. The way Django's forms API is working, the form fields will take over all the necessary error handling.
With the model fields set to unique where necessary you can have this much easier:
def view(request):
if request.method == 'POST':
form = CashtextsForm(request.POST)
""" the next line is going to check uniqueness for each
of the models fields where you have set unique = True
If the field is not unique, it s field will have an
attribute error, which you can then render to the template """
if form.is_valid():
form.save()
else:
form = CashtextsForm()
context = RequestContext(request)
# now i pass the form to the template as 'ref_create'
context.update( {'ref_create':form} )
return render_to_response('submit.html',context)
Then you simply render that form in your template (either with form.as_p(), form.as_table() or with some custom code like this):
{% for field in ref_create %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% endfor %}
Each field, set to unique in the model definition, which which would not be unique in the database any more if your form is saved, will now have an error message saying something like "A Cashtext with this name allready exists".
Customizing you error messages can be done in two ways: 1: customize the forms (i do not recommend that, until you have better understandings of how Django works) 2: instead of {{ field.errors }} write something like this:
{% if field.errors %}
The Cashtext field {{field.name}} value should be unique.
{% endif %}
I'm writing a Django admin action to mass e-mail contacts. The action is defined as follows:
def email_selected(self,request,queryset):
rep_list = []
for each in queryset:
reps = CorporatePerson.objects.filter(company_id = Company.objects.get(name=each.name))
contact_reps = reps.filter(is_contact=True)
for rep in contact_reps:
rep_list.append(rep)
return email_form(request,queryset,rep_list)
email_form exists as a view and fills a template with this code:
def email_form(request,queryset,rep_list):
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
send_mail(
cd['subject'],
cd['message'],
cd.get('email','noreply#localboast'),['redacted#email.com'],
)
return HttpResponseRedirect('thanks')
else:
form = EmailForm()
return render_to_response('corpware/admin/email-form.html',{'form':form,})
and the template exists as follows:
<body>
<form action="/process_mail/" method="post">
<table>
{{ form.as_table }}
</table>
<input type = "submit" value = "Submit">
</form>
</body>
/process_mail/ is hardlinked to another view in urls.py - which is a problem. I'd really like it so that I don't have to use <form action="/process_mail/" method="post"> but unfortunately I can't seem to POST the user inputs to the view handler without the admin interface for the model being reloaded in it's place (When I hit the submit button with , the administration interface appears, which I don't want.)
Is there a way that I could make the form POST to itself (<form action="" method="post">) so that I can handle inputs received in email_form? Trying to handle inputs with extraneous URLs and unneeded functions bothers me, as I'm hardcoding URLs to work with the code.
You can use django's inbuilt url tag to avoid hardcoding links. see...
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#url
Chances are you'd be better off setting up a mass mailer to be triggered off by a cron job rather than on the post.
Check out the answer I posted here
Django scheduled jobs
Also if you insist on triggering the email_send function on a view update perhaps look at
http://docs.djangoproject.com/en/dev/topics/signals/