Passing context to django-registration's views - python

I'm utilizing django-registration with a set of premade templates I found on Github for doing a two-step (registration-activation) workflow using HMAC.
I want to pass global variables (defined in context-processors) like my website's name to the emails sent by django-registration. the activation email sent to a new registrant, for example, or the password change one.
The "problem" is I don't directly have access to those views. That's kinda the point of django-registration, you include its path in the urls.py file, and everything works:
urlpatterns = [
url(r'^', include('core.urls')),
url(r'^admin/', admin.site.urls),
url(r'^accounts/', include('registration.backends.hmac.urls')),
]
What's the minimum effort way of adding context to those views? I've already created and am successfully passing context to emails in my own views (using context processors):
def send_some_email_view(request):
msg_plain = render_to_string('email_change_email.txt', context, request=request)
msg_html = render_to_string('email_change_email.html', context, request=request)
But what about views I didn't create?
Edit: So I made some progress, finding django-registration's registration view, and this method inside of it:
def send_activation_email(self, user):
"""
Send the activation email. The activation key is simply the
username, signed using TimestampSigner.
"""
activation_key = self.get_activation_key(user)
context = self.get_email_context(activation_key)
context.update({
'user': user
})
subject = render_to_string(self.email_subject_template,
context)
# Force subject to a single line to avoid header-injection
# issues.
subject = ''.join(subject.splitlines())
message = render_to_string(self.email_body_template,
context)
user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
I don't want to override it inside the source code because that would prevent me from updating. So now the question becomes: Is my only way out writing a view that subclasses this view, and overriding the method? This means I'm writing separate views for every view provided by django-registartion that needs to send an email...

First create your own view based on the vendor view and overwrite your desired method:
from registration.backends.hmac.views import RegistrationView
class CustomRegistrationView(RegistrationView):
def get_email_context(self, user):
context = super().get_email_context(user)
return RequestContext(self.request, context)
Than have a look at registration.backends.hmac.urls.py (source). They just define a bunch of urls there.
You can easily overwrite just one of them by adding your own before you include the one from the app.
from yourapp import views
urlpatterns = [
# [...]
url(r'^accounts/register/$', views.CustomRegistrationView.as_view(), name='registration_register'),
url(r'^accounts/', include('registration.backends.hmac.urls')),
# [...]
]
While changing just the necessary things you also get some insight of whats going on in your 3rd party app, which is always an advantage. This applies to most 3rd party apps not just the one you are currently using.

Here's what I ended up doing, Thanks to the direction dahrens' answer sent me to:
# myapp/processors.py
def get_website_name(request):
website_name = 'ExcitingWebsiteThatsComingSoon'
return {'mysite_name': website_name}
# some views.py file
from myapp.processors import get_website_name
class RegistrationViewWithContext(RegistrationView):
def get_email_context(self, user):
context = super().get_email_context(user)
context['req'] = get_website_name(self.request)
return context
Basically, I'm simply using my custom processor to inject the website's name. It isn't as clean as I hoped it would be: While in my templates I can simply use {{ mysite_name}}, in the email template I have to use {{req.mysite_name}}. But this does have the DRY-ness I aimed for: all templates updating accordingly if the variable in the function changes.
I'll mark my answer as correct for now and will update accordingly if any new answers come in.

Related

Apply django authentication for all views

I am trying to implement Django basic authentication for all of the views in my views.py file. Although I can add the authentication code snippet in every view, but it will not be easy to apply this to upcoming views. Is there any way that every view in my views.py will automatically check for the authentication?
views.py
def mgmt_home(request):
##############################################################
# This code is repetitive
##############################################################
if request.user.is_anonymous:
return redirect("/login")
##############################################################
test_name = Test.objects.all()[0].test_name
metadata = {
"test_name": test_name,
}
return render(request, "mgmt_home.html", metadata)
Is there any way where I can avoid this repetitive code in all of my views?
you can use 'login_required()' decorator or 'LoginRequiredMixin' class from django authentication.
https://docs.djangoproject.com/en/3.1/topics/auth/default/
How to specify the login_required redirect url in django?
You have 2 options:
from django.contrib.auth.decorators import login_required
You can add this #login_required() decorator to your every view and it will automatically redirect a user to the login page (or whatever page you want to send the user to) any time your user is not logged in.
This option, in your case, I would not recommend, as this might be an overkill and not required for your simple problem. The solution is to create a custom Middleware and add your code to it, and then, of course, add the Middleware to the Settings.py file. This way, each time your views run, your Middlewares will run prior to that. In fact, that's the purpose of Middlewares. They are designed to reduce redundancies and problems exactly such as yours.
Create a middleware.py file anywhere on your python path. Add the below codes to your created middleware.py file
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
def redirect_to_login():
return HttpResponseRedirect(reverse_lazy('users:login'))
class AuthPageProtectionMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
if request.user.is_authenticated:
if not request.user.is_admin:
return redirect_to_login()
else:
return redirect_to_login()
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
NOTE
You can replace the redirection URL with your application-specific one.

Django: need Dynamic login redirect using info from database redirect after login, using built-in login

After I login, I need to redirect to another page while adding URL parameters to the URL of the next page. I get the value of these parameters after the user is authenticated because they need to be accessed from the user database table. I heard about using the next parameter but I don't know how I would use it since I need to access the database table and I can't do that from urls.py. This is my url.py line for login right now:
url(r'^$',auth_views.login, name='login',kwargs={
'authentication_form':loginPlaceHolderForm,
}),
I'm not really sure what other info you need so just ask for it in the comments and I'll be sure to add it.
Also I'm using Django 1.11
EDIT:
For more clarification: What I want is something like this /colors?team=blue
And let's say the team can be red, blue or green and you get this value from the team column in the given row that you get when the user logs in.
You could try to override djangos class-based view LoginView.
In views.py
from django.contrib.auth.views import LoginView
class MyLoginView(LoginView):
authentication_form = loginPlaceHolderForm
def get_redirect_url(self):
print(self.request.user)
# get data for user here
user_data_query_string = ''
url = '{}?{}'.format(
reverse('app:some-name')
user_data_query_string)
return url
In urls.py
url(r'^$', MyLoginView.as_view(), name='login'),
See also this question about adding GET querystring parameters to djangos HttpRedirect.

How can i route my URL to a common method before passing to actual view in Django

Here is my code looks like :-
url.py file :-
from rest_framework import routers
from view_user import user_signup,user_login
router = routers.DefaultRouter()
urlpatterns = [
url(r'^api/v1/user_signup',csrf_exempt(user_signup)),
url(r'^api/v1/user_login',csrf_exempt(user_login))
]
view_user.py file:-
def user_signup(request):
try:
if request.method == 'POST':
json_data = json.loads(request.body)
return JsonResponse(result, safe=False)
except Exception as e:
logger.error("at method user : %s", e)
So, when I call the url:- http://myserver/api/v1/user_signup
it goes to "user_signup" method of view_user.py file.
But what I want is I should be able validate my request before it goes to the user_signup method.
I want this validation for all the requests that comes to my server for all methods (ex:- user_signup,user_login ...) before it goes to respective methods.
Annotate the concerned views with a decorator that contains the logic you want to execute before the views are called.
See Python - Decorators for a head start.
And How to write a custom decorator in django?
If you want to do this on all requests, regardless of the associated view, then you should consider writing a middleware. See how to setup custom middleware in django

Django Admin Custom Pages and Features

I'm (new to) working in Django and would like to create two features that do not rely upon models/database tables. The basis of this app is as a web-based wrapper to a Python application.
The features are:
I would like to be able to load a ConfigObj text file into a page and edit it's configuration prior to saving again.
I would like to be able to call command line python/bash scripts and display their output on a page - like exec in PHP.
At the moment I'm working on simple custom admin pages without model as described here:
Django admin, section without "model"?
Would this be the right direction to go in? I'm not sure proxy tables apply as the features I desire have nothing to do with any data.
So far I have looked at is it possible to create a custom admin view without a model behind it and a few other links. At the moment I have:
main/urls.py
url(r'^admin/weectrl', include('weectrl.urls')),
which links with weectrl/urls.py
from weectrl import views
urlpatterns = patterns('',
(r'^admin/weectrl/manage/$', weectrl_manage_view),
(r'^admin/weectrl/config/$', weectrl_config_view),
)
which points to weectrl/views.py
def weectrl_manage_view(request):
r = render_to_response('admin/weectrl/manage.html', context, RequestContext(request))
return HttpResponse(r)
def weectrl_config_view(request):
r = render_to_response('admin/weectrl/config.html', context, RequestContext(request))
return HttpResponse(r)
The current error message is name 'weectrl_manage_view' is not defined
Ok, found something that works.
In the main url.py
url(r'^admin/weectrl/', include('weectrl.urls')),
In app/urls.py
urlpatterns = patterns('',
url(r'^config/$', views.config, name='config'),
url(r'^manage/$', views.manage, name='manage'),
)
and in app/views.py
def config(request):
context = ""
return render(request, 'weectrl/config.html', context)
def manage(request):
context = ""
return render(request, 'weectrl/manage.html', context)
html files are in app/templates/app/...

How do I make my post form handling more flexible

I have written what I hope to be a re-usable Django app, but I have a bit of a conundrum on how to make the post form handling flexible. The simplified version of my view code looks like:
def do_form(request, entity_id, template_name, success_url):
form = MyForm(request.POST or None)
if request.method =='POST':
if form.is_valid():
#do some business logic
return HttpResponseRedirect(finished_url)
return render_to_response(template_name,
{'form': form},
context_instance=RequestContext(request))
I have followed the advice in James Bennets book "Practical Django Projects" and so you can now configure the template and the success url in the url conf, so for example my url conf could look like this:
urlpatterns = patterns('myapp.views',
url(r'^do/(?P<entity_id>\d+)/$',
view = 'do_form',
name = 'do_form_view',
kwargs={'template_name':'form.html',
'success_url':'/finish/'},),
url(r'^finish/$',
view = 'finish',
name = 'finish_view')
)
This is all very well and good but when I have come to use this in my real world application I find myself in a situation that this form sits in the middle of some workflow, and I want the success url to be something like /continue/<workflow_id>/ , and the problem is that you can only have a hardcoded url in the url conf, and the workflow_id will vary every time I hit the do_form code.
Can any one suggest a way to get around this?
You can achieve that by changing the following..
in do_form() in views.py
change the return HttpResponseRedirect to
return HttpResponseRedirect('/continue/%s' %(workflowid))
And in urls.py, you can have
url(r'^continue/(?P<workflowid>\d+)/$',
view = 'continue',
name = 'continue_view')
and for the continue() view in views.py
def continue(request, workflowid=None):
...
This way.. whenever you access the url /continue/ without a number, workflowid will be equal to None. Every other time when you do have a workflowid attached for e.g. like /continue/23/ , then inside your continue() view you can access that id through the variable workflowid.
When you pass a hypothethical "flexible" success_url to a view, that view MUST supply the desired identifier. So if you mismatch the URL and the view, we can't avoid having a "breach of contract" between the two.
Therefore if we are to have flexible URLs, some kind of contract shall have to be enforced, and there will be no loss of generality if we do this through a special syntax for URLs:
'finished_url': '/finish/<workflow_id>/'
Then, of course, the view shall have to instantiate the variable through a string replacement to honor its side of the contract: instead of
return HttpResponseRedirect(finished_url)
you will have
return HttpResponseRedirect(finished_url.replace('<workflow_id>', WorkflowID))
This should keep things reasonably simple.
When reusing code, you will have to keep in mind that <workflow_id> is whatever that app uses to call workflow id, and that's why I use a complicated string such as workflow_id instead of id or maybe $1.
EDIT: I was going to add the code for the next step (intercepting workflow ID in argument of finish), but I see that keithxm23 beat me to the punch :-)
You can do it the same way people have been "overriding" Django's function-based generic views for years: simply wrap the view in another view:
def custom_do_form(request, entity_id, template_name, success_url):
template_name = some_method_to_get_template()
return do_form(request, entity_id, template_name, success_url)

Categories