Add extra context variables into django oscar emails - django

Trying to add extra context variables for all django oscar email templates. The only way I've got it to work is overriding specific views like ProfileUpdateView. This method seems very messy, I'd have to override a lot of files. Is there a better way of doing this?

From checking the source code, the ProfileUpdateView uses Django's Class Based View FormView, which in turn implements the get_context_data method allowing the injection of extra data in the view.
You can simply create a view inehiriting ProfileUpdateView and override the get_context_data:
class MyProfileUpdateView(ProfileUpdateView):
...
def get_context_data(self, **kwargs):
context = super(MyProfileUpdateView, self).get_context_data(**kwargs)
context['somevar'] = SomeQueryMaybe.objects.all()
return context

Ended up making a custom management command to do it manually since the need to change the emails templates would be really rare.

Related

How to call a class based generic API view from a custom function based view

I am having some problems and doubts about how to call my own API within my app.
So I have an API to which I can send data. What I want to do in a different view is calling this sent data so I can visualize it in a template.
First I was trying to call the API with the library requests inside of my view. Even though that works I am having problems with authentication. So I was thinking I could call my class based API view from my custom function based view.
But I don't know if that is possible, nor do I know if that is recommendable. I was also thinking that it might be better to do that with javascript? I don't know.... So my question is twofold:
a) What is the best practice to call an API view/get API data from my own app so that I can manipulate and visualize it
b) If this is good practice, how can I call my class based generic API view from my custom function based view?
Here is what I am trying so far:
my generic view
class BuildingGroupRetrieveAPIView(RetrieveAPIView):
"""Retrieve detail view API.
Display all information on a single BuildingGroup object with a specific ID.
"""
authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication, BasicAuthentication]
serializer_class = BuildingGroupSerializer
queryset = BuildingGroup.objects.all()
my function based view with which I try to call it:
def visualize_buildings(request, id):
returned_view = BuildingGroupRetrieveAPIView.as_view()
return returned_view
my url
path('data/<int:pk>/', BuildingGroupRetrieveAPIView.as_view(),
name="detail_buildings_api"),
When I call my class based view I get AttributeError: 'function' object has no attribute 'get'
Help is very much appreciated! Thanks in advance!
What you can do if you want is to call your CBV after its declaration inside its file for the sake of easiness when declaring its URL.
views.py
class BuildingGroupRetrieveAPIView(RetrieveAPIView):
.....
visualize_buildings = BuildingGroupRetrieveAPIView.as_view()
Then on your URLs, you use that name.
urls.py
from . import views
path('data/<int:pk>/', views.visualize_buildings, name="detail_buildings_api"),
Correct way:
from django.url import reverse, resolve
def get_view_func(name_of_your_view):
return resolve(reverse(name_of_your_view)).func
# if your are in a drf view:
get_view_func()(request._request)
# if you are in normal django view:
get_view_func()(request)

Customizing Longclaw ProductIndex page

Longclaw/Wagtail newbie here. Wagtail CMS provides an overridable get_context method that makes it possible to pass dictionary values into the template. From the documentation:
class BlogIndexPage(Page):
...
def get_context(self, request):
context = super(BlogIndexPage, self).get_context(request)
# Add extra variables and return the updated context
context['blog_entries'] = BlogPage.objects.child_of(self).live()
return context
Longclaw is an eCommerce project built on top of Wagtail. Longclaw has an inbuilt Page model called ProductIndex. Is there any way to pass variables into the ProductIndex template, like I can with get_context?
Great question. It does not currently support this, but it would be great if it could. I recommend raising an issue on GitHub so we can't get this into development (https://github.com/JamesRamm/longclaw)
It may be possible to dynamically add the function;
def get_context(...):
....
ProductIndex.get_context = get_context
Ive not tried it so can't say for certain that it will work!

How to generate form in browsable API without using a serializer_class?

Lets assume I've an API that expects a couple GET args: 'foo' and 'bar'. And I haven't defined serializer_class due to it isn't tied to any specific model:
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
class myAPI(GenericAPIView):
def get(self, request, format=None):
foo = request.GET.get('foo')
bar = request.GET.get('bar')
# do something with foo & bar
return Response({'fooplusbar': _something(foo,bar)})
Is there any way to tell to djangorestframework to build the form in this kind of situation?
The Django REST Framework only supports forms out of the box for POST, PUT and PATCH requests.
In those cases, the form is generated from the serializer. There is nothing wrong with defining a serializer which does not return Model objects, nor with using the serializer for form display only, and write e.g. a custom POST handler which does not make use of it. But for Django REST Framework to show a form, you must define a serializer.
It would be quite possible to add support for proper GET forms, but you'd need to extend rest_framework.renderers.BrowsableAPIRenderer.render to add the new form to the context, and write your own templates in rest_framework/api.html.

Django - Extra context in django-registration activation email

I'm using django-registration for a project of mine.
I'd like to add some extra contextual data to the template used for email activation.
Looking into the register view source, I cannot figure out how to do it.
Any idea ?
From what I remember, you need to write your own registration backend object (easier then is sounds) as well as your own profile model that inherits from RegistrationProfile and make the backend use your custom RegistrationProfile instead (This model is where the email templates are rendered and there is no way to extend the context, so they need to be overwritten)
A simple solution is to rewrite the send_activation_email
So instead of
registration_profile.send_activation_email(site)
I wrote this in my Users model
def send_activation_email(self, registration_profile):
ctx_dict = {
'activation_key': registration_profile.activation_key,
'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
'OTHER_CONTEXT': 'your own context'
}
subject = render_to_string('registration/activation_email_subject.txt',
ctx_dict)
subject = ''.join(subject.splitlines())
message = render_to_string('registration/activation_email.txt',
ctx_dict)
self.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
And I call it like this
user.send_activation_email(registration_profile)
I don't get what it is your problem but the parameter is just in the code you link (the last one):
def register(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='registration/registration_form.html',
extra_context=None)
That means you can do it from wherever you are calling the method. Let's say your urls.py:
from registration.views import register
(...)
url(r'/registration/^$', register(extra_context={'value-1':'foo', 'value-2':'boo'})), name='registration_access')
That's in urls.py, where usually people ask more, but, of course, it could be from any other file you are calling the method.

Resolving urls to different views for different types of user profiles in Django

I'm adding a new type of user profile to site and this new type of user(say new_type) should not be able to reach the same views like the existings users.
My question is: how can i use different types of views according to user type using the same request paths without altering the existing view codes like adding
if user.profile_type == 'blah':
do_this
else:
do_that
to each view?
In detail:
i'd like to use "http://mysite.com/path/" for both types of users, running different logics and returning different displays without making differences in existing views (since there are lots of views to modify).
I'm thinking of adding different groups of views for new type, then override urls logic to resolve the request paths to relevant views, such as :
if user is of new_type
resolve path to related_view_for_new_type
else
resolve as usual
As a straight forward example: logging in admin and normal user from the same login url, and if user is admin, run the relevant views for admin and return django admin display to her, if normal user, then run the normal view and return normal website view to her, without rewriting or changing the url they are requesting. (/index/ for example)
Is it possible to extend urls in Django in such way and if so how, or should i give up overloading the same request paths and add '/new_type/' to urls (http://mysite.com/new_type/path/)for new_type users?
Thanks
To start with, what does it mean to have different types of users? A very simple way to do this would be to store an attribute on a user. That way, given a user object, you could look at this extra attribute to determine whether the user is of a special type. Django has a standard mechanism for storing additional attributes like this, which you can read about here.
Once you have a way of determining user types, you can create a single decorator and apply it to any view that needs to behave in the way you've described. Decorators are a great way of applying extra conditions or behaviour to existing functions. The logic in the decorator gets to work before and/or after the existing function, so it can very easily accomplish something like displaying a different template based on a the user's type.
Decorator functions look very odd when you first encounter them, but read it carefully and you'll soon get it. The decorator is a function itself, and you give it the function you want to decorate. It gives you back a new function, which is your old function wrapped with the extra logic.
I've written some untested example code below.
def template_based_on_user_type(special_template, ordinary_template):
def decorator(your_view_function):
def inner_decorator(request, *args, **kwargs):
# this is the logic that checks the user type before
# every invocation of this view:
if request.user.type == 'special_type':
template = special_template
else:
template = ordinary_template
# this is the invocation of the view function, with
# a custom template selected:
return your_view_function(request, template)
return inner_decorator
return decorator
#template_based_on_user_type('my-special-template.html', 'ordinary-template.html')
def my_view_function(request, template='default.html'):
# Do what you need to do here
render_to_response(template, data, RequestContext(request)
The syntax for applying a decorator is the "#" symbole, followed by the decorator function. The decorator is customized with the template names specified.
I solved this problem using decorator in urls.py:
def dispatch_by_user(staff_view, external_user_view):
def get_view(request, **kwargs):
if (is_staff_user(request.user)):
return staff_view(request, **kwargs)
else:
return external_user_view(request, **kwargs)
return login_required(get_view)
def is_staff_user(user):
return user.groups.filter(name="privileged-group").exists()
So patterns set as following:
urlpatterns = [
url(r'^$',
dispatch_by_user(
views.StaffHomeView.as_view(),
views.ExternalUserClientView.as_view()),
name='index'),
# ...
]
RTFM as usual :)
Here's the link to a possible solution :
method_splitter # http://www.djangobook.com/en/2.0/chapter08/
new_type related views' names will be derived from the originals by adding new_type_ to beginning of the name, such as index-> new_type_index
then i'll determine the view to return by simply checking the request.user.is_new_type attribute. ugly, but better than modifying gazillions of views.