Is it safe to reuse multiple models with the same generic view? - django

I have a project with around 60 models so creating a unique Detail, Create, Update, Delete APIView for each would be a lot of wasted resources (or so it feels like). Would it be better performance-wise (or safe?) to simply create a generic view that could cycle between each model like so?
_CLASSES = <Dictionary of my classes>
class GenericModelView(APIView):
def get(self, request, model_name): # model_name would be a required part of the URL.
model_class = _CLASSES[model_name]
serializer_class = model_class.serializer_class # I would instantiate a serializer for each model_class
return Response(serializer_class(model_class.objects.all()).data)

I think, there should not be any concerns safety wise. However, in my experience, this approach will not last long. You will have to customize the functionality for different model according to the requirements. At that stage, you will need to create separate views. Also, it may become complicated to read and understand logs as always same function is being called. So my final recommendation would be to use different views. It should not take you more than an hour. Just copy and paste. Customize later according to your needs.

Related

Django Rest Framework - Efficient API Design

In any rest API,
Data comes in request body
We perform some Logic on
data and perform some queries
3.finally API responds serialized data.
My question is :-
Where to put data processing Logic? And what is the efficient way of designing any REST API?
Logic on data is mainly put on the views. You can use any views, be it functional, generic API views, and viewsets. There are three types of views in DRF.
You should be comfortable in using one of them, however should understand all of them. Go through this link.
https://micropyramid.com/blog/generic-functional-based-and-class-based-views-in-django-rest-framework/
I personally find comfort in using CBV (Class-Based Views). It is in the views where most of the SQL joins take place too.
For eg:
class GetReviewAPIView(ListAPIView):
permission_classes = [IsAuthenticated]
serializer_class = ReviewSerializer
def get_queryset(self):
user = self.request.user
return Review.objects.filter(user=user)
The above is a good example of CBV where the API call gets all the reviews of a particular user only. There is a SQL join happening between the User table and Review table.
Also, you can write the logic in the serializer class as well instead of view. This is done mainly when you have to write the logic for each serializer field differently and set characteristics for each field. I can give you an example for this as well if you want.

Limiting access to objects in Django

I have a particular model and that model has finegrained access settings. Something like:
class Document(models.Model):
...
access = models.ManyToManyField(Group)
Groups consist of particular tags, and those tags are linked to users. Long story short, one way or another the documents are only accessible by particular users. It is very important that this check does not slip through the cracks. So I can see a number of options. One is that every time I access a Document, I add the check:
Document.objects.filter(access__group__tag__user=request.user)
But there are two drawbacks: a) I query the documents model > 100 times in my views so I will have a LOT of repeated code, and b) it's quite likely that someone will at some point forget to add this restriction in, leaving documents exposed.
So I am thinking that overwriting the objects() makes most sense, through a custom manager. That way I don't duplicate code and I don't risk forgetting to do this.
class HasAccessManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(access__group__tag__user=request.user)
class Document(models.Model):
...
access = models.ManyToManyField(Group)
objects = HasAccessManager()
However, the problem becomes that request is not accessible there:
name 'request' is not defined
How to solve this? Or are there better solutions?
Create a mixin that your views inherit from. This will prevent having duplicated code everywhere. You'll want to write unit tests to make sure your views are locked down appropriately.
class HasAccessMixin(object):
def get_queryset(self):
qs = super().get_queryset()
# you can still leverage a custom model manager here if you want
# qs = qs.custom_method(access__group__tag__user=self.request.user)
qs = queryset.filter(access__group__tag__user=self.request.user)
return qs
class SomeListView(HasAccessMixin, ListView):
...
class SomeDetailView(HasAccessMixin, DetailView):
...

Django Rest Framework pre-populate form with specific queryset

Basically in a popup (bootstrap) I would like to have all specified pre-populated fields from my model.
I found this code (https://groups.google.com/forum/#!searchin/django-rest-framework/HTMLFormRenderer/django-rest-framework/s24WFvnWMxw/hhmaD6Qw0AMJ)
class CreatePerformanceForm(forms.ModelForm):
model = Performance
fields = ('field1', 'field2')
class PerformanceCreateView(ListCreateAPIView):
serializer_class = PerformanceCreateSerializer
model = Performance
template_name = 'core/perform.html'
def get(self, request, format=None):
data = {'
form': CreatePerformanceForm()
}
return Response(data)
My question is the same.
Is there a way to create the form directly from the serializer so I don't have to create a Django form?
I looked at HTMLFormRenderer, but the DRF doc is quiet poor about this issue.
Thanks,
D
See this issue. Important part:
There are some improvements that could be made there [to HTMLFormRenderer], notably supporting error messaging against fields, and rendering the serializer directly into html without creating a Django form in order to do so [...]
So basically, HTMLFormRenderer also uses Django forms. Also, you are right, the documentation doesn't provide too much support for it. Even more, it seems that this renderer might soon change. See here. Quote:
Note that the template used by the HTMLFormRenderer class, and the context submitted to it may be subject to change. If you need to use this renderer class it is advised that you either make a local copy of the class and templates, or follow the release note on REST framework upgrades closely.
I know this doesn't help much, but for now there is no better way than the way you did it.

Django admin for User's objects

I would like to allow a User to have an admin interface to their own Video objects. I was planning on writing some views that allowed things like setting attributes like "published" or deleting objects.
I've started looking into using django's Admin site - but that seems like it might be overly complicated for what I want (just deleting/ setting the published attribute).
Is one approach better than the other? Writing something from scratch or using the Admin site?
If I were to write something from scratch - what is the correct way to achieve ModelAdmin style actions (ie. delete_selected(queryset, request))
This is exactly what the admin should be used for! How could it be too complicated? Even writing a handful of lines of HTML would take longer.
If you built this yourself, no matter how simple, you'll have to define views that list objects, validate input, check permissions, write HTML, implement some kind of multiple action system that maps to python code, ....
Assuming you don't want to do that:
You're going to want to look into making multiple admin sites and filtering admin results to only those that belong to the user via overriding the queryset method on a ModelAdmin
# pasted from docs
class MyModelAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(MyModelAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)

What is the advantage of Class-Based views?

I read today that Django 1.3 alpha is shipping, and the most touted new feature is the introduction of class-based views.
I've read the relevant documentation, but I find difficult to see the big advantage™ that I could get by using them, so I'm asking here for some help in understanding them.
Let's take an advanced example from the documentation.
urls.py
from books.views import PublisherBookListView
urlpatterns = patterns('',
(r'^books/(\w+)/$', PublisherBookListView.as_view()),
)
views.py
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from books.models import Book, Publisher
class PublisherBookListView(ListView):
context_object_name = "book_list"
template_name = "books/books_by_publisher.html",
def get_queryset(self):
self.publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
return Book.objects.filter(publisher=self.publisher)
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(PublisherBookListView, self).get_context_data(**kwargs)
# Add in the publisher
context['publisher'] = self.publisher
return context
And now let's compare it to a “plain-old-views” solution, made by myself in 5 minutes for this question (I apologize for any error you may find in it).
urls.py
urlpatterns = patterns('books.views',
url(r'^books/(\w+)/$', 'publisher_books_list', name="publisher_books_list"),
)
views.py
from django.shortcuts import get_object_or_404
from books.models import Book, Publisher
def publisher_books_list(request, publisher_name):
publisher = get_object_or_404(Publisher, name__iexact=publisher_name)
book_list = Book.objects.filter(publisher=publisher)
return render_to_response('books/books_by_publisher.html', {
"book_list": book_list,
"publisher": publisher,
}, context_instance=RequestContext(request))
The second version to me looks:
Equivalent in functionality
A lot more readable (self.args[0]? awful!)
Shorter
Not less DRY-compliant
Is there something big I'm missing? Why should I use them? Are those on the documentation? If so then what would be the ideal use case? Are mixins that useful?
Thanks in advance to anybody who contributes!
P.S. for those who might wonder, I was never enthralled by generic views as well: as soon as I needed some advanced functionality, they became no shorter than regular views.
You can subclass a class and refine methods like get_context_data for specific cases, and leave the rest as-is. You can't do that with functions.
For instance, you might need to create a new view that does everything a previous one does, but you need to include extra variable in the context. Subclass the original view and override the get_context_data method.
Also, separating the steps needed to render the template into separate methods promotes clearer code - the less done in a method, the easier it is to understand. With regular view functions, it's all dumped into the one processing unit.
If self.args[0] is bothering you, the alternative is:
urlpatterns = patterns('books.views',
url(r'^books/(?P<slug>\w+)/$', 'publisher_books_list', name="publisher_books_list"),
)
Then you could use self.kwargs['slug'] instead, making it slightly more readable.
Your example function and class are not equal in features.
The class based version provide pagination for free and forbid the use of other HTTP verbs than GET.
If you want to add this to your function, it's going to be much longer.
But it is, indeed, more complicated.
This is the first I'm hearing of this -- and I like it.
The advantage I see here, honestly, is that it makes views more consistent with Django overall. Models are classes and I've always felt that views should be too. I know not everything is but views and models are the two heavily used types.
As for the technical advantage? Well, in Python everything is a class (or object?) -- so is there really a difference? Isn't it 99% syntactical sugar in the first place?
One way to think about class based views, is that they are like a the Django admin with training wheels off and therefore a lot more flexible (but more difficult to understand).
For example the list-display in the admin is clearly based on the generic ListView. The simplest list view you would only define a model or queryset.
class MyExampleView(ListView);
model = ExampleModel
You will need to supply your own template, but it will basically be the same as the most basic ModelAdmin. The list_display attribute in the model admin will tell it what fields to display, whereas in the ListView you would do this in the template.
class SpeciesAdmin(admin.ModelAdmin):
list_display = ['name']
admin.site.register(ExampleModel , ExampleModelAdmin)
With the admin you have a parameter
list_per_page = 100
which defines how many objects per page. List view has
paginate_by = 100
which achieves the same thing. Likewise if you look into customising the admin heavily, you will see a lot of overlap.
This site here should give you a better idea of what they do as well.
http://ccbv.co.uk/