I'm building an API and I've started using the GenericAPIViews. In this example
class InsertActivityChange(UpdateAPIView):
authentication_classes = []
permission_classes = []
serializer_class = ActivitiesSerializer
lookup_field = 'id'
def get_queryset(self):
return Activities.objects.filter(id=self.kwargs['id'])
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
path('insertactivitychange/<int:id>', views.InsertActivityChange.as_view(), name='insertactivitychange'),
I've used a generic class to do updates for an object. My concern is if I'm risking much by allowing the ID to be directly inputted after the URL. Obviously, I'd further mitigate the risk by properly configuring the authentication and permission class, so is the ID of the entry even a security risk?
If you're worried about access, remember to add permissions and filters. If you're focused on security and hiding resources, make sure they will 404 instead of 401 or 403. Have a look at DRF filtering section and in serializers use PrimaryKeyRelatedField queryset where you filter based on user.
If you're worried about exposing pks, just use UUIDs. It will still be visible but a human can't predict what the next id is.
So even if 3rd party knows the id of a resource, it should return 404 if you want to hide a resource. That way you expose a meaningless value to the attacker.
Let me try to answer this question.
One can't ensure security just by hiding things
One of my mentor always emphasized on this line whenever I had asked similar questions.
To answer you question, according to me no you don't expose any security flaws by exposing IDs, in fact it's one of the most common way of allowing users to interact with resources over APIs.
You will be surely required to have authentication and authorisation in place to safeguard the resource manipulation by malicious sources. In context of DRF and django you can achieve this by using authentication and permissions.
Hope this answers your question.
I have a generic ListView that returns a list of objects.
All was good before I decided to print how many time passed since creation time (not just when the object was created) for every object.
Then I need a piece of code to count it and I need to pass the results to the template.
But now I can't use a generic view.
I thought about context processors, but context processors take only request as a parameter and I need to give it also the id of an object to count the time delta.
So, is there any way to make it using generic views or context processors or I have to code a custom view? Or is there a better way?
UPDATE
Thanks everyone for detailed answers, it will greatly help me in the future.
I learned that the mentioned task can be solved using timesince built-in template tag. Hope it will help someone.
You can override get_context_data and add more data to the context dictionary:
class MyView(ListView):
# ...
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['mydata'] = ...
return context
You can then use mydata in the template.
You can also access context['object_list'] which contains the list of objects that will be displayed in the template.
Simeon's answer is the obvious one if you need to add more stuff in the context for a specific ListView, but you can also use custom templatetags or filters if you need something that doesn't directly depend on the view itself. In you case a simple filter computing the delta from any date or datetime might be the ticket. Extra bonus: you can reuse it on any date in any template without having to change anything in the views...
In my django admin section, I'd like to show different versions of the admin page depending on what kind of user is currently logged in. I can think of a couple ways this might work, but haven't figured out how to do any of them.
Perhaps I could put logic into the admin.ModelAdmin to look at the current user and change the 'exclude' field dynamically. Does that work? Or maybe run different custom templates based on who's logged in, and have the templates include / exclude the fields as appropriate.
I could register two versions of the admin.ModelAdmin class, one for each type of user, and maybe restrict access through permissions? But the permissions system seems to believe fairly deeply in one set of permissions per model class so I'm not sure how to change that.
I could grab a couple of the widgets that are used in rendering the admin page templates, and include them in my own page that does the one specific job I need powerful users to be able to do.
I could set up multiple AdminSites and restrict access to them through the url / view system. But then I'm not sure how to register different admin.ModelAdmin classes with the different AdminSites.
Any advice on this would be appreciated.
Answer
Thanks for the hint. Here's how I did it...
def get_form(self, request, obj=None, **kwargs):
"""This dynamically inserts the "owners" field into the exclude list
if the current user is not superuser.
"""
if not request.user.is_superuser:
if self.exclude:
self.exclude.append('owners')
else:
self.exclude = ['owners']
else:
# Necessary since Admin objects outlive requests
try:
self.exclude.remove('owners')
except:
pass
return super(OwnersModelAdmin,self).get_form(request, obj=None, **kwargs)
There are quite a few hooks provided in the ModelAdmin class for this sort of thing.
One possibility would be to override the get_form method. This takes the request, as well as the object being edited, so you could get the current user from there, and return different ModelForms dependent on the user.
It's worth looking at the source for ModelAdmin - it's in django.contrib.admin.options - to see if overriding this or any other other methods might meet your needs.
I'm trying to find a way to filter down rows of objects within Django Admin, using a queryset.
e.g. Person.objects.filter(Q(name='John')|Q(surname='Doe'))
I'm finding quite complicated to figure out.
Any ideas?
You might be able to accomplish this by overriding the queryset() method on your modeladmin instance. See http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py?rev=15347#L196
# untested code
class MyModelAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(MyModelAdmin, self).queryset(request)
return qs.filter(Q(name='John') | Q(surname='Doe'))
This would only affect results for the model registered with that ModelAdmin, but you could probably subclass it as a starting point for other ModelAdmin classes in order to stay DRY.
I'm not saying this is a good idea.
Django view points to a function, which can be a problem if you want to change only a bit of functionality. Yes, I could have million keyword arguments and even more if statements in the function, but I was thinking more of an object oriented approach.
For example, I have a page that displays a user. This page is very similar to page that displays a group, but it's still not so similar to just use another data model. Group also has members etc...
One way would be to point views to class methods and then extend that class. Has anyone tried this approach or has any other idea?
I've created and used my own generic view classes, defining __call__ so an instance of the class is callable. I really like it; while Django's generic views allow some customization through keyword arguments, OO generic views (if their behavior is split into a number of separate methods) can have much more fine-grained customization via subclassing, which lets me repeat myself a lot less. (I get tired of rewriting the same create/update view logic anytime I need to tweak something Django's generic views don't quite allow).
I've posted some code at djangosnippets.org.
The only real downside I see is the proliferation of internal method calls, which may impact performance somewhat. I don't think this is much of a concern; it's rare that Python code execution would be your performance bottleneck in a web app.
UPDATE: Django's own generic views are now class-based.
UPDATE: FWIW, I've changed my opinion on class-based views since this answer was written. After having used them extensively on a couple of projects, I feel they tend to lead to code that is satisfyingly DRY to write, but very hard to read and maintain later, because functionality is spread across so many different places, and subclasses are so dependent on every implementation detail of the superclasses and mixins. I now feel that TemplateResponse and view decorators is a better answer for decomposing view code.
I needed to use class based views, but I wanted to be able to use the full name of the class in my URLconf without always having to instantiate the view class before using it. What helped me was a surprisingly simple metaclass:
class CallableViewClass(type):
def __call__(cls, *args, **kwargs):
if args and isinstance(args[0], HttpRequest):
instance = super(CallableViewClass, cls).__call__()
return instance.__call__(*args, **kwargs)
else:
instance = super(CallableViewClass, cls).__call__(*args, **kwargs)
return instance
class View(object):
__metaclass__ = CallableViewClass
def __call__(self, request, *args, **kwargs):
if hasattr(self, request.method):
handler = getattr(self, request.method)
if hasattr(handler, '__call__'):
return handler(request, *args, **kwargs)
return HttpResponseBadRequest('Method Not Allowed', status=405)
I can now both instantiate view classes and use the instances as view functions, OR I can simply point my URLconf to my class and have the metaclass instantiate (and call) the view class for me. This works by checking the first argument to __call__ – if it's a HttpRequest, it must be an actual HTTP request because it would be nonsense to attept to instantiate a view class with an HttpRequest instance.
class MyView(View):
def __init__(self, arg=None):
self.arg = arg
def GET(request):
return HttpResponse(self.arg or 'no args provided')
#login_required
class MyOtherView(View):
def POST(request):
pass
# And all the following work as expected.
urlpatterns = patterns(''
url(r'^myview1$', 'myapp.views.MyView', name='myview1'),
url(r'^myview2$', myapp.views.MyView, name='myview2'),
url(r'^myview3$', myapp.views.MyView('foobar'), name='myview3'),
url(r'^myotherview$', 'myapp.views.MyOtherView', name='otherview'),
)
(I posted a snippet for this at http://djangosnippets.org/snippets/2041/)
If you're simply displaying data from models, why not use the Django Generic Views? They're designed to let you easy show data from a model without having to write your own view and stuff about mapping URL paramaters to views, fetching data, handling edge cases, rendering output, etc.
You can always create a class, override the __call__ function and then point the URL file to an instance of the class. You can take a look at the FormWizard class to see how this is done.
Unless you want to do something a little complex, using the generic views are the way to go. They are far more powerful than their name implies, and if you are just displaying model data generic views will do the job.
Sounds to me like you're trying to combine things that shouldn't be combined. If you need to do different processing in your view depending on if it's a User or Group object you're trying to look at then you should use two different view functions.
On the other hand there can be common idioms you'd want to extract out of your object_detail type views... perhaps you could use a decorator or just helper functions?
-Dan
Generic views will usually be the way to go, but ultimately you're free to handle URLs however you want. FormWizard does things in a class-based way, as do some apps for RESTful APIs.
Basically with a URL you are given a bunch of variables and place to provide a callable, what callable you provide is completely up to you - the standard way is to provide a function - but ultimately Django puts no restrictions on what you do.
I do agree that a few more examples of how to do this would be good, FormWizard is probably the place to start though.
If you want to share common functionality between pages I suggest you look at custom tags. They're quite easy to create, and are very powerful.
Also, templates can extend from other templates. This allows you to have a base template to set up the layout of the page and to share this between other templates which fill in the blanks. You can nest templates to any depth; allowing you to specify the layout on separate groups of related pages in one place.
You can use the Django Generic Views. You can easily achieve desired functionality thorough Django generic Views