Django: Returning a customized view function - django

I'm developing a Django application that will be used by people in various companies. Some companies require highly customized views, which means I'll change the default template depending on their needs. It could also require some extra context variables.
My idea is to create separate view functions as required, since trying to put it all in one view function will get messy quite quickly. For example, let's say I have a dashboard view:
def dashboard(request):
var1 = some_func()
context = {'var1': var1 }
return render(request, 'normal_template.html', context)
Now company x wants a custom dashboard:
def dashboard_for_company_x(request):
var1, var2 = some_func_x()
context = {'var1': var1, 'var2': var2 }
return render(request, 'template_for_x.html', context)
So when a user from company x goes to a certain page, dashboard_for_company_x should be used. All other user should use the original dashboard function.
The two options I can think of is either:
1. adding the company's name to the url, so that the URL resolver will use the correct view function.
2. Using middleware to intercept the request and redirect them to the appropriate view function if required.
1 seems like the better option, but it would mean I would to change all the url tags in my template to include the company name, so that is not really an option. I think 2 will work, but seems like a bit of a hack.
Is it fine to use number 2, or are there better options available?
Note that only a small percentage of views will require customization.

you could do the routing inside your view function, e.g.:
def dashboard(request):
if request.user.company == 'foo':
return dashboard_foo(request)
elif request.user.company == 'bar':
return dashboard_bar(request)
else:
return dashboard_standard(request)
Depending on the complexity of your view it might make sense to use a class based view, so you only need to swap out the specific logic (e.g.: templates, context vars, etc.) in the specific methods, and let the main logic be the same across all users (i.e.: reduce code duplication).

Related

Django practice: smaller views or less views?

I'm developing an app with django, and I have a view where I use 2 return render_to_response, with two different html files, depending on the status of the user.
I was wondering if it would be a better practice to split my view into two different views or if I should keep a bigger view.
What are the pros and the cons of doing so?
Sorry if my question is not clear. Thank you very much for your advices.
There's no right or wrong answer to this question so your question may not be acceptable on stackoverflow, which usually is intended for questions/problems with specific technical solutions.
That said, here's my view on this topic - I personally like to keep my view function small and if further processing is required, split them out into smaller functions.
For example:-
#permission_required('organizations.organization_add_user')
def organization_add_user(request, org_slug):
org = get_object_or_404(Organization, slug=org_slug)
form = OrganizationAddUserForm(org=org)
if request.method == 'POST':
form = OrganizationAddUserForm(request.POST or None, request.FILES or None, org=org)
if form.is_valid():
cd = form.cleaned_data
# Create the user object & send out email for activation
user = create_user_from_manual(request, data=cd)
# Add user to OrganizationUser
org_user, created = OrganizationUser.objects.get_or_create(user=user,\
organization=org)
dept = org.departments.get(name=cd['department'])
org_user.departments.add(dept)
# Add user to the appropriate roles (OrganizationGroup) and groups (django groups)
org_groups = OrganizationGroup.objects.filter(group__name__in=cd['roles'], \
organization=org)
for g in org_groups:
user.groups.add(g.group)
return HttpResponse(reverse('add_user_success'))
template = 'organizations/add_user.html'
template_vars = {'form': form}
# override request with dictionary template_vars
template_vars = FormMediaRequestContext(request=request, dict=template_vars)
return render(request, template, template_vars)
FormMediaEquestContext is a class I import from another file and has its own logic which helps me to handle javascript and css files associated with my form (OrganizationAddUserForm).
create_user_from_manual is yet another function which is encapsulated separately and deals with the reasonably convolutated logic relating to creating a new user in my system and sending an invitation email to that new user.
And of course, I serve up a different template if this is the first time a user arrives on this "add user" page as opposed to redirecting to a completely different url with its own view function and template when the add user form is successfully executed.
By keeping our view functions reasonably small, I have an easier time tracking down bugs relating to specific functionality.
In addition, it is also a good way to "reuse" my utility functions such as the create_user_from_manual method should I need this same utility function in another view function.
However, at the end of the day, organizing code and encapsulating code is a judgement call that you get to make as you progress as a developer.

Limiting user to single app

I have a Django project with several apps. I'd like to restrict a particular user's access to only one specific app and at the time of the user's creation, i.e. without having to say modify every method of views.py with decorators such as #permission_required.
Is this possible? That is, is it possible to declare that user 'A' can only use app 'X' without modifying any of app 'Y's code?
You could write some middleware that implements the process_view method, then check which app the view function belongs to.
For example, this is one (potentially buggy) way you could do it:
class RestrictAppMiddleware(object):
def process_view(self, request, view_func, *args, **kwargs):
view_module = view_func.__module__
allowed_apps = apps_visible_to_user(request.user)
if not any(app_name in view_module for app_name in allowed_apps):
return HttpResponse("Not authorized", status=403)
Obviously you'd need to improve on the heuristic (ex, this one will allow users with access too "foo" view "foobar" as well) and consider apps which rely on Django built-in views (ex, direct_to_template)… But this is the way I'd do it.

Passing template variables from URL to FormPreview in Django

I'm a Django noob and fear the answer to my question is fairly obvious, but hoping someone can help.
I'm building an app that includes the same form on every page, with the content surrounding the form and the model instance to which the form data is tied dependent on a value passed in the URL. Works fine using the standard Form class and (URL, 'template.html', myapp.view) in URLconf, like so:
url(r'^listings/(?P<listing_id>\d+)/submit$', 'myapp.views.lister'),
With FormPreview, however, instead of calling the view in the URLconf, you're calling the subclass with the view functionality baked in.
url(r'^listings/(?P<listing_id>\d+)/submit$', PickFormPreview(PickForm)),
From what I can gather from the docs, FormPreview uses parse_params to pass values captured in the URL to state.self, which I believe is a dictionary. Unfortunately given my level of experience, I can't figure out from this barebones understanding how to customize my FormPreview subclass how to pass the listing_id captured in the URL to a template variable in my form.html template called by FormPreview. Do I somehow need to override parse_params? Or somehow pass state.listing_id? Or am I missing it entirely?
Any help much appreciated!
You're on the right track. The parse_params method in FormPreview does not do anything, but it is the correct method to override in your subclass.
The following example saves listing_id to self.state.listing_id, which is available in the template context.
class PickFormPreview(FormPreview):
def parse_params(self, *args, **kwargs)
"""
Saves listing_id from the url to state
"""
self.state['listing_id'] = kwargs['listing_id']
Then in your template, you access it as:
Listing {{ state.listing_id }}

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.

How can I get a Django TemplateLoader see the current Context or Request?

I' m trying to build a Django TemplateLoader, but I can't make it 'see' either the current Context or Request, so I can't really do much with it.
Does anyone know how I can make a Django TemplateLoader do this?
Many Thanks
Joe
The place to make decisions about templates is in your view. There, you have access to the request, and can dynamically construct a template name depending on whatever conditions you like. E.g.,
def my_view(request, ...):
...
template_name = 'template.html'
if is_mobile(request): template_name = 'mobile_' + template_name
template = get_template(template_name)
context = RequestContext(request, {...})
return HttpResponse(template.render(context))
Where you provide the is_mobile(). Better would be to provide a method that takes a request and template name, and returns one appropriately modified (so that you can encode that logic once, rather than scattering across several views).
You might also get some leverage from select_template(), which takes a list of candidate template names, returning a template for the first one it finds.