I made a Custom template tag using this doc like this in my Django application :
myproject/
__init__.py
models.py
templatetags/
__init__.py
myCustomTags.py
views.py
in the myCustomTags.py, I need to use some variables that there are in views.py
so I save those variables in session , and tried to get them in myCustomTags.py, but noting worked and it does not recognized my sessions.
I used this doc ,but it seems that this method wants me to use session_keys. in this method my question is that how to use the sessions without the key or somehow pass the keys from views.py to myCustomTags.py too .
here is my code in this method:
views.py:
from importlib import import_module
from django.conf import settings
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
from django.contrib.sessions.backends.db import SessionStore
my_session = SessionStore()
def user_login(request):
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
# some process to validate and etc...
my_session['test_session'] = 'this_is_my_test'
my_session.create()
return redirect(reverse('basic_app:index'))
myCustomTags.py
from django import template
from importlib import import_module
from django.conf import settings
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
from django.contrib.sessions.backends.db import SessionStore
my_session = SessionStore()
register = template.Library()
#register.simple_tag
def userStatusMode():
status = my_session['test_session']
return status
base.html:
{% load dynamic_vars %}
{% userStatusMode as user_status_thing %}
<!-- and somewher in base.html -->
{{user_status_thing}}
the other method was to use requst.sessions in views.py and try to get them in myCustomTags.py that didn't worked too.
by the way , how can I use session outside of the views ?
am I missing something here ?
This is all kinds of wrong.
You're not supposed to instantiate SessionStore directly. The way you've done it, you haven't given any indication of which user's session you are trying to get or set.
Instead you are supposed to access the session for the current user via request.session.
request.session['test_session'] = 'this_is_my_test'
and similarly in the template, where you can directly access the session dict (no need for a template tag):
{{ request.session.test_session }}
Related
I want to call a csfr protected class view inside other view in django, but this is giving me a CSFR not set error.
I tried to disable it with the csfr_exempt function (Reference), but it did not work at all:
from django.contrib.auth import views as django_auth_views
from django.views.decorators.csrf import csrf_exempt
def my_view(request):
response = csrf_exempt(
django_auth_views.PasswordResetView.as_view()
)(request)
It keeps giving me the same error.
Is there anyway I can do it? Thanks.
I was able to do it by adding the token manually:
csrf_token = get_csrf_token(request)
request.POST._mutable = True
request.POST['csrfmiddlewaretoken'] = csrf_token
request.COOKIES[settings.CSRF_COOKIE_NAME] = csrf_token
request.POST._mutable = False
response_from_password_reset_post = django_auth_views.PasswordResetView.as_view()(request)
I created an app called "jobs", basically I'd like to create new "jobs" from the admin console and be able to post it on the jobs.html page.
I created the model and views but I think there is something wrong with the views that doesn't allow me to print the "jobs" on the html template.
Can you please tell me if the error is in views.py?
jobs/models.py
from django.db import models
# Create your models here.
class post_job(models.Model):
posizione= models.TextField(max_length=20)
descrizione= models.TextField(max_length=20)
requisiti= models.TextField(max_length=20)
def __str__(self):
"""String for representing the MyModelName object (in Admin site etc.)."""
return self.posizione
jobs/admin.py
from django.contrib import admin
from .models import post_job
# Register your models here.
admin.site.register(post_job)
jobs/views.py
from django.shortcuts import render
from .models import post_job
# Create your views here.
def viz_job(request):
posizione = post_job.posizione
print(posizione)
return render(request,'jobs/jobs.html',{'posizione':posizione})
Proper answer:
In your views:
from django.shortcuts import render
from .models import PostJob # proper naming
def viz_job(request):
jobs = PostJob.objects.all()
return render(request,'jobs/jobs.html',{'jobs': jobs})
in your template:
<ul>
{% for job in jobs %}
<li>
<h3>{{ job.posizione }}</h3>
<div>
{{ job.descrizione }}
</div>
</li>
{% endfor %}
</ul>
Note that all this is documented.
NB: if you're only interested in those two fields and don't need any of the model's methods, related objects or whatever, you can optimize the query a bit by using a values queryset that will yield dicts with the selected fields instead of full model instances:
jobs = PostJob.objects.values("posizione", "descrizione")
Everything else remains the same.
You have to know what do you want to return for the template, for example in the views.py :
from django.shortcuts import render
from .models import post_job
# Create your views here.
def viz_job(request):
jobs = []
descriziones = []
posizione = Job.objects.all()
for pos in posizione:
jobs.append(pos.posizione)
descriziones.append(pos.descrizione)
context = {
'posizione': jobs,
'descrizione': descriziones
}
return render(request, 'jobs/jobs.html',
context=context) # this will return context dictonary to the template
You can filter and get to fetch specific data from your database
I wrote a custom loader in django which is performing some 'regular expression' changes in template and In unit testcase for the loader I want to check the changes in the template. But django loader didn't returns any template_string attribute with template object.
For Example
I am removing new line i.e. '\n' after every '%}' in the template with the help of custom loader by performing re operations over it.
Now I need to check this after loader performs re operations over template in the TestCases.
template:
{% for i in list %}\n
{{ i }}
{% endfor %}\n
in Unit Test Case:
template = loader.get_template("path/to/template")
#How I can retrieve template_string from template object?
#to put in place of 'output'
self.assertEqual(output ,"{% for i in list%}{{ i }}{% endfor %}")
The expected output I need to match is {% for i in list%}{{ i }}{% endfor %}. How can I get the template_string from the template object?
You can create a test custom loader into your tests.py module like below example.test custom loader will extend your custom template loader to add template's updated source.
If core is the app you have created.
core/
__init__.py
models.py
tests.py
views.py
loader.py # where your custom template loader can be located.
templates
tests.py
import io
from django.test import TestCase
from django.template.engine import Engine
from core.loader import CustomLoader as BaseLoader # this is your custom template loader you want to test
class HelperLoader(BaseLoader):
is_usable = True
def load_template_source(self, template_name, template_dirs=None):
out_source, path = super(HelperLoader, self).load_template_source(template_name, template_dirs=None)
self.out_source = out_source
return out_source, path
class CustomLoaderTest(TestCase):
def test_custom_loader(self):
template_path = 'index.html'
engine = Engine()
engine.dirs = ['core/templates',] # `core/templates` is where your test template_path is located.
out = HelperLoader(engine)
template = out.load_template(template_path )
self.assertEqual('template source with updated content.', out.out_source)
I can't see a way to get the 'template string' that you want. You could test the loader by checking the rendered output instead, for example:
template = loader.get_template("path/to/template")
output = template.render({'list': [1, 2, 3, 4, 5]})
self.assertEqual(output, '12345')
While Working on the above issue I found that we can also call load_template() directly from the Custom Loader instead of calling it by creating another HelperLoader() by overriding Custom Loader and then calling load_template()
import io
from django.test import TestCase
from django.template.engine import Engine
from core.loader import CustomLoader # this is your custom template loader you want to test
class CustomLoaderTest(TestCase):
def test_custom_loader(self):
template_path = 'index.html'
engine = Engine()
engine.dirs = ['core/templates',] # `core/templates` is where your test template_path is located.
out = CustomLoader(engine)
template = out.load_template(template_path )
self.assertEqual('template source with updated content.', out.source)
I am trying to implement sharing in my registration-required website. I would like the user to be able to share a page for a certain duration of time (1 day, 1 week, etc.) so that anyone with a special link can access that page. Is this possible in Django?
EDIT: My solution (based on Saurabh Goyal’s answer):
Add a new model to your models.py, something like this:
class ShareKey(models.Model):
location = models.TextField() # absolute path
token = models.CharField(max_length=40, primary_key=True)
creation_date = models.DateTimeField(auto_now_add=True)
expiration_seconds = models.BigIntegerField()
data = PickledObjectField() # custom sharing data
(The data field is optional, and requires django-picklefield).
In your views.py, add a decorator function like this:
def allow_shares(view_func):
def sharify(request, *args, **kwargs):
shared = kwargs.get('__shared', None)
if shared is not None:
if '__shared' not in view_func.func_code.co_varnames[:view_func.func_code.co_argcount]:
del kwargs["__shared"]
return view_func(request, *args, **kwargs)
else: return login_required(view_func)(request, *args, **kwargs)
return sharify
This decorator allows a view to require a login, unless the page is shared.
Decorate any views you want to be shareable with #allow_shares.
Add a new Exception subclass, SharifyError, like this:
class SharifyError(Exception):pass
Also in views.py, add a view to resolve the shared URLs, like this:
def sharedPage(request, key):
try:
try:
shareKey = ShareKey.objects.get(pk=key)
except: raise SharifyError
if shareKey.expired: raise SharifyError
func, args, kwargs = resolve(shareKey.location)
kwargs["__shared"] = True
return func(request, *args, **kwargs)
except SharifyError:
raise Http404 # or add a more detailed error page. This either means that the key doesn’t exist or is expired.
Add a url to urls.py, like this:
urlpatterns = patterns('',
# ...
url(r'^access/(?P<key>\w+)$', views.sharedPage, name="sharedPage"),
# ...
)
Finally, add URLs to create a shared link, and implement the view like this:
# in imports
from django.utils.crypto import get_random_string
def createShare(request, model_id):
task = MyModel.objects.get(pk=model_id)
key = ShareKey.objects.create(pk=get_random_string(40),
expiration_seconds=60*60*24, # 1 day
location = task.get_absolute_url(),
)
key.save()
return render(request, 'share.html', {"key":key});
(Your share.html template should look something like this):
{% extends 'base.html' %}
{% block content %}
<h1>Sharing link created.</h1>
<p>The link is {{ base_url }}{% url 'taskShared' key.pk %}. It will be valid until {{ key.expiration_date|date:"l, N dS" }} at {{ key.expiration_date|time:"g:i a" }}.</p>
{% endblock %}
This will require users to login to view the decorated pages unless they have entered a key.
Yes, you can do that simply by having one additional field in dB which will represent the last datetime till when page is accessible by all. Then whenever someone accesses it, just check if current datetime is before or equal to value of that field, if yes, let them access, if no, deny access.
When user makes request to make page accessible, create the special link and update the field accordingly.
Make sure to handle routing for special link you generate on every request.
Edit-
To handle things using keys, you can take following steps-
1) generate a random key whenever user requests for special link,
2) store the key and corresponding latest datetime of access for page in db
3) assuming main url for your page was '/mypage/', and your urls.py is something like this-
from django.conf.urls import patterns, url
from apps.myapp import views as myapp_views
# See: https://docs.djangoproject.com/en/dev/topics/http/urls/
urlpatterns = patterns(
'',
url(r'^mypage/$', myapp_views.MyPageView.as_view(), name='mypage'),
)
you add another url, something like this-
from django.conf.urls import patterns, url
from apps.myapp import views as myapp_views
# See: https://docs.djangoproject.com/en/dev/topics/http/urls/
urlpatterns = patterns(
'',
url(r'^mypage/$', myapp_views.MyPageView.as_view(), name='mypage'),
url(r'^mypage/(?P<key>\w+)/$', myapp_views.MyPageView.as_view(), name='mypage_special'),
)
What above results in is that a url '/mypage/any_alphanumeric_key/' will also redirect to your page view.
4) In your views, access the key, fetch the latest datetime of access for that key from dB, and if valid, give access else deny access.
I would like to be able to render some or all of the views in my project with a different base template. In other words, for url /some/view I would like to be able to have /inline/some/view and have it render the same content, but using a different base template.
Modifying each view to accept a different template is not an option, because I would like to apply this behaviour across all apps in the project, including things like django.contrib.auth.
So far, I have, in urls.py:
url("^inline/(?P<uri>.*)", format.inline, name='inline'),
And the view, format.py:
from django.core.urlresolvers import resolve
def inline(request, uri=''):
# get the view that would normally handle this request
view, args, kwargs = resolve('/' + uri)
# call the view
kwargs['request'] = request
template_response = view(*args, **kwargs)
# ...now what?
I'm not sure where to go from here. Can I modify the entire template chain before I call view(), so that template_response.render() does the right thing?
Perhaps I am entirely off-base with this approach and should be looking at a middleware solution, but I am attached to the idea of this behaviour keying off URLs, because it will be easy to explain to the content people later on.
UPDATE
I was able to achieve the effect I desired, but the implementation is severely lacking. Here's what I did:
copied the templates for the views I wished to inline into templates/inline/
replaced {% extends base.html %} with {% extends inline/base.html %}
modified the view thus:
from django.core.urlresolvers import resolve
def inline(request, uri=''):
# get the view that would normally handle this request
view, args, kwargs = resolve('/' + uri)
# call the view
kwargs['request'] = request
template_response = view(*args, **kwargs)
response.template_name = os.path.join('inline', response.template_name)
return response
I don't like this solution because it will require those inline templates to be managed, being replaced/updated whenever apps in the project change, and so on. I would still dearly love a cleaner solution.
Update 2: Solution
chris-wesseling was 100% correct; a custom template loader was exactly what I needed. For posterity, here is my implementation.
app/loaders.py:
from django.conf import settings
from django.template.loader import BaseLoader
from django.template.base import TemplateDoesNotExist
import os
class BaseTemplateOverrideLoader(BaseLoader):
"""
Load templates from a specified subdirectory in the current app's directory.
"""
subdir = 'templates'
def load_template_source(self, template_name, template_dirs=None):
template_dir = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
self.subdir
)
try:
t = os.path.join(template_dir, template_name)
with open(t, 'rb') as fp:
return (fp.read().decode(settings.FILE_CHARSET), template_dir)
except IOError:
pass
raise TemplateDoesNotExist(template_name)
class InlineTemplateLoader(BaseTemplateOverrideLoader):
"""
Override the location of base.html for inline views.
"""
is_usable = True
subdir = 'templates/inline'
# ... other custom override classes here ....
app/views/inline.py:
from django.conf import settings
from django.core.urlresolvers import resolve
from django.template import loader
def default(request, slug=None):
view, args, kwargs = resolve('/' + slug)
old_loaders = settings.TEMPLATE_LOADERS
# Temporarily insert the inline template loader into TEMPLATE_LOADERS;
# we must also force BaseLoader to reload all templates loaders since
# they are cached at compile time.
settings.TEMPLATE_LOADERS = ('app.loaders.InlineTemplateLoader', ) + \
settings.TEMPLATE_LOADERS
loader.template_source_loaders = None
# now call the original view that handles this request
kwargs['request'] = request
response = view(*args, **kwargs)
response_string = response.render()
# restore the template loaders to their original condition
settings.TEMPLATE_LOADERS = old_loaders
loader.template_source_loaders = None
return response_string
app/templates/inline/base.html:
{% comment %}
inline/base.html
-- render just the main content without any styles etc,
for loading as inline content via ajax or whatever.
{% endcomment %}
{% block main %}{% endblock %}
You can implement your own TemplateLoader and set it in your settings.TEMPLATE_LOADERS. You can have a look at this similar question for an approach of what you're trying to do.
Basically what you're looking for is a way to load base.html from a different location.