django variable available to all the views - python

With context_processors it' s easy to define a callable which results variables available to all the templates. Is there any similar technique which makes a variable available to all the views? Is that somehow possible? Maybe with some workaround?
Django: 2.2
Python: 3.5.3
.

You may want to implement a custom Middleware.
https://docs.djangoproject.com/en/dev/topics/http/middleware/
This lets you execute custom code for every request and attach the results to the request object, which is then accessible in your view.

You can try sending your variable to the context of each class based view by having a parent class and inheriting all the views from that.
class MyMixin(object):
def get_context_data(self, **kwargs):
context = super(MyMixin, self).get_context_data(**kwargs)
myvariable = "myvariable"
context['variable'] = myvariable
return context
# then you can inherit any kind of view from this class.
class MyListView(MyMixin, ListView):
def get_context_data(self, **kwargs):
context = super(MyListView, self).get_context_data(**kwargs)
... #additions to context(if any)
return context
Or if you are using function based views, you can use a separate function which can update your context dict.
def update_context(context): #you can pass the request object here if you need
myvariable = "myvariable"
context.update({"myvariable": myvariable})
return context
def myrequest(request):
...
context = {
'blah': blah
}
new_context = update_context(context)
return render(request, "app/index.html", new_context)

Related

Overriding get method in class based view with super()

I created a view that manages the products which belong to the current log-in user (so the current user can watch its own products):
class ProductList(View):
def get(self, request, *args, **kwargs):
products = Product.objects.get(user=self.request.user)
#...
I would like to subclass another view from it, which manages the products that belong to a user specified as a GET parameter in the url (probably different to the current user). Is it possible to do it with super() and overriding the method in product_list? Something like this:
class ProductListFromUser(ProductList):
def get(self, request, *args, **kwargs):
#not sure what code if necessary to put here...
super().get(self, request, *args, **kwargs):
#or here
#...
Basically, how can I override the get method from product_list , changing only "self.request.user" by the parameter taken from the URL
You shouldn't do either of these.
If you have a view that renders a list of items, you should use a subclass of ListView. In that view, you can define the product list in the get_queryset method, which you can define in the relevant way in your two different views.
class ProductList(ListView):
def get_queryset(self, *args, **kwargs):
return Product.objects.filter(user=self.request.user)
class ProductListFromUser(ListView):
def get_queryset(self, *args, **kwargs):
return Product.objects.filter(user__username=self.request.GET['user'])
You should rarely need to define get (or post) directly in a class-based view.
I would move the retrieval of the appropriate QuerySet to an extra method which you can override in order to reuse as much of the initial code as possible:
class ProductList(View):
def get_products(self):
return Product.objects.all()
def get(self, request, *args, **kwargs):
products = self.get_products()
# ...
class ProductList(View):
def get_products(self):
qs = super().get_products()
qs = qs.filter(user=self.request.user)
return qs
# no get needed
This very functionality comes with django's ListView and its get_queryset method.
If you retrieve the queryset for a given request using the get_queryset method, that CBV such as DetailView use, then you can simply override the get_queryset of the parent-class.

Can I use class method in views.py?

Now my code is like:
def use_item(request):
itemname = request.get('value')
if itemname == 'item1':
#do something
if itemname == 'item2':
#do something else
Can I do it in the following way?
views.py
class use_item():
def use_item(self,request):
itemname = request.get('value')
use = getattr(self,itemname) # say itemname is 'item1'
use()
def item1(self,request):
#do something
def item2(self,request):
#do something else
I've tried the second method but it seems that I was not doing it right.
And the reason I want to do it in this way is that I hope to group the methods that they'd be more organized.
the actual code
#views.py
class use_item():
def useitem(self,request):
itemname = request.POST.get('value')
use = getattr(self,itemname)
use()
def jelly(self,request,topic_id):
t = topic.objects.get(id=topic_id)
t.top = True
t.time_topped = datetime.datetime.now()
t.save()
#urls.py
url(r'^use/item/(?P<topic_id>\d+)/$', 'use_item.use_item', name='use_item'),
If you want to have a better organization of your code, and reuse some code accross different views, instead of pasting it where you need, you may use the Django class based views:
# views.py
from django.views import View
class use_item(View):
def get(self, request, *args, **kwargs):
itemname = request.POST.get('value')
use = getattr(self,itemname)
use()
def item1(self,request):
#do something
def item2(self,request):
#do something else
# urls.py
from package.views import use_item
urlpatterns = [
# [...]
url(r'^use/item/(?P<topic_id>\d+)/$', use_item.as_view(), name='use_item'),
# [...]
]
But, if at some point you need to call item1() or item2() from another view (is it the reason you mentioned the other view jelly ?), you will see that it is not possible.
One solution could be moving the common methods in another class and make sure your views inherit it. This is often called mixins in django world.
# views.py
from django.views import View
class ItemsRelatedLMixin:
def item1(self, request):
#do something
def item2(self, request):
#do something else
class use_item(ItemsRelatedLMixin, View):
def get(self, request, *args, **kwargs):
itemname = request.POST.get('value')
use = getattr(self,itemname)
use()
class jelly(ItemsRelatedLMixin, View):
def get(self, request, topic_id):
t = topic.objects.get(id=topic_id)
t.top = True
t.time_topped = datetime.datetime.now()
t.save()
Now, your views jelly and use_item call the common methods. Of course you can define new methods in a view to make them available only from that view. You could also create class members to store values you use often etc. Keep in mind that each request received by Django will trigger creation of a new instance of your view class (you can't keep data stored between 2 requests in class members).
In Django, view functions are usually organized in modules and not in classes.
To keep things organized, use more than one views module: views.py, foo_views.py, bar_views.py, or: views/__init__.py, views/foo.py, views/bar.py.
You need to provide the view in the signature of the class. i.e.:
from django.views import [your_View_name]
Then provide the same view in class definition;
class use_item(your_View_name):
def useitem(self,request):
itemname = request.POST.get('value')
use = getattr(self,itemname)
use()
If you are defining your class for the same view,
class use_item(self):
def useitem(self,request):
itemname = request.POST.get('value')
use = getattr(self,itemname)
use()
You may refer Django docs on Class-Based-View for more in-depth knowledge.
UPDATE:
When you are calling your function useitem you need to use the instance of your class as follows:
user_instance = views.use_item() //Create instance of your class
user_instance.useritem() //call your function using above instance

Class-based views: where to check for permissions?

I am not very comfortable using class-based views but I am aware of their perks so I am forcing myself to start using them more often.
There's this view that receives a path param: manage/:id to manage a particular entity.
class MyView(TemplateView):
template_name = '...'
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['entity'] = get_object_or_404(Entity, pk=self.args[0])
return context
An Entity includes a list of authorized users to perform special actions. This view, MyView is one of those special actions.
I tried making a decorator for the view but it required finding the Entity first so I wasn't sure how to work that out.
Now, I have a check_permission(request, entity) function that checks if the current user is one of these authorized ones.
My question is where should I call this function in the class-based views like MyView which will be any of these views considered "special actions"?
Should I call it just from get_context_data()?
put it into dispatch(). It could look like this:
class MyView(TemplateView):
template_name = '...'
def dispatch(self, request, *args, **kwargs):
entity = get_object_or_404(Entity, pk=args[0])
if not check_permission(request, entity):
raise Http404
return super(MyView, self).dispatch(request, *args, **kwargs)
You can check permissions in the dispatch as yedpodtrzitko has said. I think it's also a good idea to throw it inside of a mixin that you can put on your views.
Here's an example:
from django.core.exceptions import PermissionDenied
class ViewPermissionsMixin(object):
"""Base class for all custom permission mixins to inherit from"""
def has_permissions(self):
return True
def dispatch(self, request, *args, **kwargs):
if not self.has_permissions():
raise PermissionDenied
return super(ViewPermissionsMixin, self).dispatch(
request, *args, **kwargs)
class MyCustomPermissionMixin(ViewPermissionsMixin):
def has_permissions(self):
# here you will have access to both
# self.get_object() and self.request.user
return self.request.user in self.get_object().special_list_of_people
Now you can throw MyCustomPermissionMixin on your view:
class MyView(MyCustomPermissionMixin, TemplateView):
# ...
In your case, since you're using a TemplateView, you should also make a get_object() method that returns the object that you want to deal with. Template views don't have this method by default.
Finally, just want to say that you will love Django's class based views once you learn some more about how to use them.
Take a look at Django Braces, it's a solid set of mixins which are designed around permissions.
How specifically you deal with permissions depends largely on implementation. I've done it in dispatch() before which is the way Braces does it but if it's specific to an object or queryset, I'll do it in the actual get_object or get_queryset methods as part of a DetailView.
For example if you had a creator associated with an Entity, you could override get_object to check the current logged in user is the Entity's creator.
class EntityView(LoginRequiredMixin, DetailView):
model = Thing
def get_object(self, **kwargs):
return Entity.objects.get_object_or_404(
pk=kwargs['entity_id'],
creator=self.request.user
)
Note: LoginRequiredMixin is a part of Braces. Very slick.

Access class variables in functions - Django

I have the following code:
class MyView(View):
var2 = Choices.objects.get(id=1)
my_strings = ['0','1','2','3']
#login_required
def myfunction(self,request):
return render(request,
'app/submit.html',{'my_strings':my_strings, 'var2':var2})
I want to access "var2" and "my_string" variables and display them in the template submit.html. If I use only the function without putting it in a class, everything works fine. But inside the class it shows errors.
Can anybody tell me how to access "var2" and "my_string" class variables in "myfunction" ?
You have to use self. In front of class variables.
Your function names in class based views should correspond to what http method you try to use(get, post etc...)
#login_required
def get(self,request):
return render(request,
'app/submit.html',{'my_strings':self.my_strings, 'var2':self.var2})
Please also read:
https://docs.djangoproject.com/en/1.9/topics/class-based-views/intro/
You don't have to write custom function to dispatch request...Django internally have the GET and POST method to do that... And also preferred way to handle login required is method_decorator
from django.utils.decorators import method_decorator
#method_decorator(login_required, name='dispatch')
class MyView(View):
string = "your string"
def dispatch(self, *args, **kwargs):
return super(MyView, self).dispatch(*args, **kwargs)
def get(self, request):
return render(request, 'template', {'string': self.string})

Failure when using setattr to add field to Django form class

I'm trying to write use Django FormView and a bit of ingenuity to create a view which will allow me to get inputs from a user that will be fed to a function. I'd like the code to be reusable, so I'd like to make a view that will be able to take a target function as a parameter and automagically create a form appropriate to that function. There is more plumbing to be done, but the general idea would be:
class FormViewForFunction(FormView):
template_name = '...'
func = None
def get_form_class(self):
class _FunctionForm(forms.Form):
pass
a = inspect.getargspec(self.func)
for argname in a['args']:
setattr(_FunctionForm, argname, forms.CharField())
return _FunctionForm
The idea would be that then you could set up something in your URLConf that used FormViewForFunction.as_view(func=***insert any function you want***) and you would wind up being presented with a form that was appropriate for specifying parameters for that function. Let's not worry about what would happen on form submission. For now I'm just stuck getting the form to generate properly.
With the code above, the form doesn't wind up having any fields! What am I doing wrong?
form's fields are initialized during initialization, you should override the __init__ method and then append the fields to the self.fields dictionary
This should work:
class FormViewForFunction(FormView):
template_name = '...'
func = None
def get_form_class(self):
a = inspect.getargspec(self.func)
class _FunctionForm(forms.Form):
def __init__(self, *args, **kwargs):
super(_FunctionForm, self).__init__(*args, **kwargs)
for argname in a['args']:
self.fields[argname] = forms.CharField()
return _FunctionForm

Categories

Resources