Same api endpoint for CRUD operations in Django generic apiview - django

I have been creating different api endpoint for different requests, for eg every single api for get, post, delete and update in generic apiview. But my frontend developer has told me it's a very bad practice and I need to have a single api for all those 4 requests. When I looked it up in the documentation, there is a ListCreateApiView for listing and creating an object, but I cant use it for delete and also for update. How can I include those two in a single endpoint. I don't use modelset view and also functional view. I mostly use generic api views.

Did you try rest framework's ModelViewSet?
i.e.:
from rest_framework.viewsets import ModelViewSet
Which has all the mixins (CRUD) and you can inherit from it in your API view.
Or you can add these mixins based on your requirements:
from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
and you can inherit from each one of them seperately. For instance:
Class SomeView(CreateModelMixin, DestroyModelMixin, GenericViewSet):
pass
which has create and delete ability. You can also use mixins with GenericAPIView:
Class SomeView(CreateModelMixin, DestroyModelMixin, GenericAPIView):
pass

One of the easiest ways to achieve this is by use of Generic views.
Simply start by adding this import to your views.py:
from rest_framework import generics
from .serializers import modelnameSerializer
from .model import modelname
Then here goes your generic class based view.
This view is for listing all items in your model of choice
class yourmodelnameList(generics.ListCreateAPIView):
queryset = modelname.objects.all()
serializer_class = modelnameSerializer
This view is for updating, deleting and retrieving:
class modelnameDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = modelname.objects.all()
serializer_class = modelnameSerializer
In urls.py simply add:
path('yourulchoice/',views.modelnameList.as_view()),
path('yourulchoice/<int:pk>/', views.modelnameDetail.as_view()),
Other things to note:
django rest framework is installed.
ensure rest_framework is added in INSTALLED_APPS in settings.py file.
The two url routes should be able to handle your CRUD needs.

Related

Django: How to Access Installed Third Party App's Model and Data

I have been using django-river(https://github.com/javrasya/django-river) application within my app (https://github.com/rupin/WorkflowEngine)
I have used the Django rest framework in my application and would like to get the data from the django-river tables. The name of the table is State.
My serializer is as follows
from river.models import State
from rest_framework import serializers
class StateSerializer(serializers.Serializer):
class Meta:
model = State
fields = ['id', 'label']
Also, my API view is as follows
from rest_framework import generics
from workflowengine.riverserializers.StateSerializer import StateSerializer
from river.models import State
class StateList(generics.ListAPIView):
serializer_class = StateSerializer
def get_queryset(self):
return State.objects.all()
Through the Admin console, I have added 11 states inside my state table, which I have checked with pgadmin.
When i access the API through the browser, I get 11 empty sections in my API ( no error, just the data is missing).
I cant seem to understand how the 11 data points presented in the API are empty. That it presented 11 elements, but no data, which is pretty weird.
I think you need to use:
class StateSerializer(serializers.ModelSerializer):

Trying to create user types: 'Client' and 'Employee'. Would like all users who register on site to be Clients, who see different page than employee

I'm reading through these docs on Django:
https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#extending-user
I'm just not sure which route to go. They suggest using a custom user model like so:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
Doing so, what would the procedure be to make two different user types. Should I have something like this:
class Client(AbstractUser):
pass
class Employee(AbstractUser):
pass
But then how would new registered users be 'clients' when they sign up? And how would I make it so they see a different part of the site than employees?
I'm just looking for some guidance in how I should approach this.
Are you only addressing a question of permissions, or do 'Client' and 'Employee' need their own specific functionality?
If it is just about permissions, then it would be easier to use the permissions functionality of the auth library.
You can then protect your views with the permissions decorator.
From the docs:
from django.contrib.auth.decorators import permission_required
#permission_required('polls.can_vote', login_url='/loginpage/')
def my_view(request):
...

How can i use the base django base permissions to limited user access?

Can someone explain with an example or a tutorial of how can i allow model's view using the permissions that this user has.
for example i have 2 users and 6 tables (models), I set that each one have permission for 3 tables, when the user access through the api authentication just allow access to the tables that he has permissions.
PD: sorry i don't speak english.
Since it appears that you're using django-rest-framework, I recommend you look at the documentation on permissions for django-rest-framework. Specifically, the section you will want is DjangoModelPermissions.
An example of how you would implement this (assuming a class based view) would look like:
from rest_framework.permissions import DjangoModelPermissions
from rest_framework.views import APIView
from .models import MyModel
class MyView(APIView):
permission_classes = (DjangoModelPermissions,)
queryset = MyModel.objects.all()
Keep in mind that, as stated in the documentation, you must provide a queryset attribute on the view class in order for this to work.

Using mixins in class based view - Django

So I started using the generic class based views and I gotta say, they do save time. But I was wondering whether I could use mixins to provide generic impl rather than having to code in each view. for e.g.
I have a ListView and DetailView. To restrict the listing and editing, I could override the get_queryset() and filter it by the logged in user. But as you guessed, i would have to do that in each view,
class JediListView(ListView):
def get_queryset(self):
q = <call super>.filter(user=request.user) #assume i have 'login_required' in the urls
class JediDetailView(DetailView):
def get_queryset(self):
q = <call super>.filter(user=request.user) #assume i have 'login_required' in the urls
I could create a new parent class for each of the view but I would still be repeating the code.
class RepublicListView(ListView):
# override get_queryset code as above
class JediListView(RepublicListView):
# repeat fot DetailView, DeleteView, UpdateView
I was wondering about mixins, I am exactly sure how mixins work [from java background, so I am awed and fearful at the same time]
You actually almost found the answer by yourself. You can write the following Mixin:
class UserFilterMixin:
def get_queryset(self):
return <call super>.filter(user=self.request.user)
And then use it in the classes like this:
class RepublicListView(LoginRequiredMixin, UserFilterMixin, ListView):
And so on for the other classes...
You can use LoginRequiredMixin from django-braces.
from django.views.generic import ListView, DetailView
from braces.views import LoginRequiredMixin
class JediListView(LoginRequiredMixin, ListView):
model = JediModel
class JediDetailView(LoginRequiredMixin, Detail):
model = JediModel
As per Chapter-8: Best Practices for Class-Based Views from TWO SCOOPS of DJANGO,
THIRD-PARTY PACKAGES: CBVs + django-braces Are Great Together We feel
that django-braces is the missing component for Django CBVs. It
provides a set of clearly coded mixins that make Django CBVs much
easier and faster to implement. !e next few chapter will demonstrate
it's mixins in various code examples.

testing admin.ModelAdmin in django

I am trying to find out the best way for testing admin.ModelAdmin in admin.py. Specifically I am overriding the save_model() function which I want to test. From the research I have done, the only solution I have found was writing a request/response test and then query the database.
As suggested in Udi's answer, we can study Django's own ModelAdmin tests, to determine the basic ingredients for a ModelAdmin test. Here's a summary:
Basic ingredients
In addition to the Django TestCase stuff, the basic ingredients are:
An instance of AdminSite:
from django.contrib.admin.sites import AdminSite
Your model class and corresponding ModelAdmin (sub)class:
from my_app.models import MyModel
from my_app.admin import MyModelAdmin
Optionally, depending on your needs, a (mock) request and/or form.
Recipe
The first two ingredients are required to create an instance of your (custom) ModelAdmin:
my_model_admin = MyModelAdmin(model=MyModel, admin_site=AdminSite())
Based on the ModelAdmin source, the default save_model implementation only requires an instance of your model, so it can be called, for example, as follows:
my_model_admin.save_model(obj=MyModel(), request=None, form=None, change=None)
# some test assertions here
It all depends on what your save_model does, and what you want to test.
Suppose your save_model checks user permissions, then you would need to provide a request (i.e. the third ingredient) with a valid user, in addition to the model instance:
from unittest.mock import Mock
...
my_user = User.objects.create(...)
my_model_admin.save_model(
obj=MyModel(), request=Mock(user=my_user), form=None, change=None
)
# some test assertions here
Here we use unittest.mock.Mock to create a mock-request. Based on the Django test source, a minimal request consists of a Python object with a user attribute.
The user attribute may refer to a mock user, or an actual instance of your AUTH_USER_MODEL, depending on your needs. An alternative would be to use django.test.RequestFactory.
This basic approach applies to the other ModelAdmin methods as well.
Check out Django's ModelAdminTests for examples.
You can specify custom modelform for modeladmin then simply test this modelform ;)
https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form
forms
class SomeModelForm(forms.ModelForm):
class Meta:
model = SomeModel
admin
class SomeModelAdmin(admin.ModelAdmin):
form = SomeModelForm
admin.site.register(SomeModel, SomeModelAdmin)
tests
class TestSomeModel(TestCase):
def test_form(self):
form = SomeModelForm(**kwargs)
self.assertTrue(form.is_valid())
# ...