django get view method name on permission classes like actions - python

I have a custom permission like:
def has_permission(self, request, view):
print("view", view)
I am calling list() method of ListModelMixin.
Here when I print the value of veiw it gives me class name of the view. But what I want is the name of the method that is being called, in this case list.
In view set we can get method name from action attribute.
Is there anyway I can get name of the method not the class ??

You probably can't do what you want to do...
I think it's unlikely you're going to be able to achieve what you want here without some kind of a hack. The reason is, that list isn't really the view that is called. Look at for example ListAPIView:
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
get is fundamentally the view that is called, and it then calls list. You could try and emulate what the dispatch method does to find the view that gets called (it uses request.method.lower()) but that's still just going to give you essentially the http-method.
But you probably don't want to do it either...
My senses tell me that, you probably don't want to decide what kind of permissions are appropriate based off of if you are doing list or retrieve. Even if at the moment, it so happens that all of your lists require a certain behaviour, and all of you retrieves require a different behaviour. Almost certainly, a little bit down the road this will no longer be the case and this permission will become a complicated unmaintainable mess.
Alternatives
Abstract away all of the logic about permissions into helper functions, and then write DRY permission classes for each View as needed.
Alternatively, you can always check permissions as you call the appropriate list and retrieve methods. Write a decorator for each behaviour, and just decorate the relevant methods. e.g
class MyView(ListAPIView):
#my_permission_decotator
def list(request, *args, **kwargs):
return super().list(*args, **kwargs)

Related

Django: Override user queryset to filter out admin/staff users from the public?

I want to filter the user manager self.get_queryset() method in such a way that users on the client application don't see admin and staff users when searching for or viewing other accounts. The issue I'm running into is I am unable to login with my auth system if I override get_queryset entirely. My current setup is:
class AccountManager(BaseUserManager):
def get_public_queryset(self):
return self.get_queryset().filter(active=True, verified=True, admin=False, staff=False)
Using this design works fine if I define various sorting methods in the manager (because I can simply call that method), but it seems as though there should be a better way to do this. Any ideas?
I think additional methods, as you've implemented is good a solution, but if you insist using get_queryset method it would be fine to override the method and preserve base functionality. I'd do something like this:
...
def get_queryset(self, *a, **kw):
queryset = super().get_queryset(*a, **kw)
# filter your queryset here as you wish
queryset = queryset.filter(active=True, verified=True, admin=False, staff=False)
return queryset
...
As I've spotted from question text, you tried to call self.get_queryset() which would be recursive call (not super class implementation call), which will finally cause maximum recursion depth exceeded error.
Hope it helps

Super method from Django documentation

Here is one piece from Django documentation:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
My hesitation is focus on the save method.
Why the author separates do_something from do_something_else?
Because of the existence of 'Call the "real" save() method',what's the meaning of do_something(),which seems to be "false" ?I even can do the manipulation:
def save(self, *args, **kwargs):
super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
do_something()
do_something_else()
Am I right?
Yes, you're right.
There is no deep meaning.
The sentences just mean "You can write some methods which you want to do before super().save or after super().save()."
However, I slightly doubt that you know super() in detail, which is one of the syntax in python.
do_something() and do_something_else() aren't real functions. They aren't defined. They are just hints for you to do something there, then, if you need, do something else, implementing even functions and calling them there, if you need.
Usually you need some field to automatically update/get a value when another field is saved. One way to do this is by overriding the model save method. And usually you do this before you call super().save().
Well, it is just showing that you can fall functions (or write code) before or after calling a parent or sibling method with super.
But first of all, you must know what super is...
In your example, you e calling save with super, which triggers the django based methods and functions and do many other stuff that is within ModelBase.save().
You are doing this to follow standart django save procedure.
But, you may want to make some checks or make some pre-save work, so you can call a method to do stuff that should be done before saving the model instance. Maybe you want to log the record time to a file and you just write a method and call it to log timestamp before you call super().save()
The same is also valid as post-save actions.

Different users get the same search results

The point of my question is the following. I have Django form with a field, which inherits the Selet2 field:
class Select2ModelField(MyBaseSelect2ModelField, AutoModelSelect2Field):
'''
Select2ModelField, that uses ajax to get autocomplete options.
Should be used by default.
'''
widget = Select2ChoiceWidget
class LimitedDepartmentChoiceField(Select2ModelField):
def __init__(self, *args, **kwargs):
super(LimitedDepartmentChoiceField, self).__init__(*args, **kwargs)
And then I use it in my form, creating this field in a views.py, because the content of this field depends on the request data:
form = RepresentativeCreateEditForm(request.POST)
form.fields['department'] = LimitedDepartmentChoiceField(label=u'Department',
queryset=Department.objects.filter(
id__in=all_deps_ids))
The problem is that when two different users enter this page at the same time, they both have the same list of options, exactly the one which the user, who first load the page, has. And this behaviour is incorrect, they should have the different lists of options.
Please, could anyone tell me how I can solve this problem?
It sounds like a value is getting set as a class attribute (somewhere, on one of your classes), rather than as an attribute of a particular instance of a class. There's a lot of inheritance going on, so you might have to do some digging to see exactly where the problem is. My guess is that it's the Select2ChoiceWidget class.
From your code example it looks like all instances of Select2ModelField and its subclasses are sharing a single Select2ChoiceWidget class between themselves. I would think this would be the cause of the problem.
I don't know a whole lot about the Django classes you're using, but maybe try something along these lines?
class Select2ModelField(MyBaseSelect2ModelField, AutoModelSelect2Field):
'''
Select2ModelField, that uses ajax to get autocomplete options.
Should be used by default.
'''
def __init__(self, *args, **kwargs):
# Not sure if this is the proper way to instantiate this class,
# but doing so would help avoid leaking data across the instances
# of Select2ModelField and its subclasses.
self.widget = Select2ChoiceWidget()
# Do the parent class(es) for good measure.
super(Select2ModelField, self).__init__(self, *args, **kwargs)

Change a django form class before it is instantiated

I'm wondering if it's possible to alter or change a form class before it's instantiated.
As a concrete example, I have a payment form which needs to be modified based on the payment system being used.
Ideally I'd rather not create different form classes and then choose different ones based on the payment system; instead the payment system object will "tell" the form class what changes it need to make -- for example, making certain fields optional or instructing them to use different widgets.
Is this possible? The alternative is to pass an object representing the payment system into the form, and then having it modify the form after instantiation, but that just seems clumsy somehow to have it run in the form class rather than the view. I feel like the Django "view" is closer to a controller, and it seems like this is where something like this should happen. I also feel like modifying it'd be better to modify the form_class object rather than the form instance; I'm not even sure if when you add fields after the fact like this it will handle validation and form fill-in correctly. Will it?
Anywhere, here's some sample code of how it would work passing the payment object into a form instantiation call:
payment_system.py:
class ExamplePaymentSystem(BasePaymentSystem):
def modify_form(self, form):
for fld in self.optional_fields:
form.fields[fld].required = False
…etc…
forms.py:
class ModifiablePaymentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.payment_system = kwargs.pop("payment_system", None)
super(ModifiablePaymentSystem, self).__init__(*args, **kwargs)
self.payment_system.modify_form(self)
You should not modify global static data (classes defined at module scope are static), because if you run your code in many threads per process (which is often done) one thread may modify form class used by the other threads.
If you your payment systems are static (you do not add new on the fly, while server is running) I'd define one form per payment system.
If not, you can always, define new form types on the fly like that:
def get_form_type(payment_system):
class DynamicForm(BasePaymentForm):
... add change fields etc...
return DynamicForm
or modify instances like that:
class PaymentForm(BasePaymentForm):
def __init__(self, ..., payment_system):
self.fields['foo'].required = False # <--- I'm writing code
#from the memory, so possibly you'll need t edit it
#but this is doable and easy to do.
How to remove field in forms (per OP request).
When you subclass:
This is hard and I think you'll need to browse through form internals and modify them by hand after subclass creation. This is a wild guess...
def get_form_type(payment_system):
class DynamicForm(BasePaymentForm):
... add change fields etc...
del DynamicForm.base_fields['foo']
return DynamicForm
When you modify instance:
I'm not 100% sure, but I peeked into django source code (unfortunately these details are not in docs). But i guess that you should:
class PaymentForm(BasePaymentForm):
def __init__(self, ..., payment_system):
del self.fields['foo']
The fields are a dict (or I guess -- OrderedDict for that matter) and to delete field you need to remove whole key-vaule mapping.

Is it okay to set instance variables in a Django class based view?

I trying out Django's class based views (CBVs).
class BlahView(TemplateView):
template_name = 'blah/blah.html'
def get_context_data(self, **kwargs):
#code...
def get(self, request, **kwargs):
#more code...
Now, I know that I can get the request params from self.request. Now say I want to parse these request params and store them within the class. Can I store those in self.xxx? Now, obviously based on how classes work, this seems straightforward.
But I can't make out the flow of control, looking at the definition of View (superclass of TemplateView). The source mentions as_view() to be the 'entry-point'
I thought of setting my instance variables at the beginning of get_context_data() but that doesn't seem right to do initialization there.
Can I define an __init__() for my CBV?
If so, will there be threading issues or something where multiple page-accesses possibly work with a global instance of my parsed data?
I know this sounds a bit messy, but I'm just a bit confused with the code flow in CBVs.
According to the source of django.views.generic.base.View.as_view:
on django startup, as_view() returns a function view, which is not called
on request, view() is called, it instantiates the class and calls dispatch()
the class instance is thread safe
According to the source of django.views.generic.base.View.__init__, the request object is out of scope at this point so you can't parse it in your own constructor overload.
However, you could parse the request and set class view instance attributes in an overload of django.views.generic.base.View.dispatch, this is safe according to the source:
class YourView(SomeView):
def dispatch(self, request, *args, **kwargs):
# parse the request here ie.
self.foo = request.GET.get('foo', False)
# call the view
return super(YourView, self).dispatch(request, *args, **kwargs)
#jpic provided a great answer. Inspired from it, I would like to reference the following blog post where the author claims that:
... We cannot override view, as doing so would require overriding
as_view(). Overriding dispatch() is appealing (and what I did
originally when I presented this talk) because it offers a single
simple place to do so, but this defies the logic of dispatch().
Instead, it is best to call set_account() in overrides of both get()
and post(). ...
Therefore, one can override the get or post methods and set any self.whatever variables. It feels somehow cleaner.

Categories