Is it possible to modify a ViewSet's update() function to update based on a query string parameter instead of a URL resource name or the request body?
For example, I want trigger something like this:
payload = {'field' : '2'}
r = requests.put("http://127.0.0.1:9876/job-defs?job-def-id=2", data=payload)
and have this update my field when job-def-id = 2.
What I have so far is this:
class JobDefinitionsViewSet(mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
serializer_class = JobDefinitionsSerializer
def update(self, request, pk=None):
job_def_id = self.request.query_params.get('job-def-id', None)
super(JobDefinitionsViewSet, self).update(self, request, pk=job_def_id)
...
# other unrelated code
...
I'm not too sure how to continue.
I want to reuse as much as the update() function from mixins.UpdateModelMixin as possible.
Any help is appreciated.
Assuming you want to use the same url for listing and for retrieving the details of the entity, discriminating on the presence of the url parameter job-def-id, here is a crazy idea:
class JobDefinitionsAllInOneView(mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = JobDefinitions.objects.all()
serializer_class = JobDefinitionsSerializer
def get_object(self):
job_def_id = self.request.query_params.get('job-def-id', None)
if job_def_id is not None:
self.kwargs['pk'] = job_def_id
return super(JobDefinitionsAllInOneView, self).get_object()
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
job_def_id = self.request.query_params.get('job-def-id', None)
if job_def_id is not None:
# return the details
return self.retrieve(request, *args, **kwargs)
else:
# return a list
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(self, request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(self, request, *args, **kwargs)
I haven't tried it yet. I had a look at the source code here for get_object and here for the mixins
Related
So I'm able to add a decorator to several functions within my code, but I'm unsure as to how to add it to a class.
Here are my decorators:
def unauthenticated_user(view_func):
def wrapper_func(request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('index')
else:
return view_func(request, *args, **kwargs)
return wrapper_func
def allowed_users(allowed_roles=[]):
def decorator(view_func):
def wrapper_func(request, *args, **kwargs):
group = None
if request.user.groups.exists():
group = request.user.groups.all()[0].name
if group in allowed_roles:
return view_func(request, *args, **kwargs)
else:
return HttpResponse('You are not authorised to view this page')
return wrapper_func
return decorator
And here is the class:
class PasswordsChangeView(PasswordChangeView):
form_class = PasswordChangedForm
success_url = reverse_lazy('password-success')
While it is possible to work with decorators that work with functions with the #method_decorator(…) [Django-doc], usually one writes mixins.
For example for your unauthenticated_user decorator, you can write a mixin:
class UnauthenticatedUserMixin:
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('index')
else:
return super().dispatch(request, *args, **kwargs)
and then mix this into your view:
class MyView(UnauthenticatedUserMixin, TemplateView):
# …
or for allowed_users:
AllowedUsersMixin:
allowed_roles = ()
def dispatch(self, request, *args, **kwargs):
if request.user.groups.filter(name__in=self.allowed_roles).exists():
return super().dispatch(request, *args, **kwargs)
else:
HttpResponse('You are not authorised to view this page')
and then use the mixin with:
class PasswordsChangeView(AllowedUsersMixin, PasswordChangeView):
allowed_roles = ('group1', 'group2')
form_class = PasswordChangedForm
success_url = reverse_lazy('password-success')
I have to implement several forms, therefore I need the combination of SingleObjectMixin, TemplateView. I always receive 'AssignAttendee' object has no attribute 'object'. Do you see why I get that error?
class AssignAttendee(SuccessMessageMixin, SingleObjectMixin, TemplateView):
template_name = 'attendees/front/assign_attendee.html'
success_message = _("Attendee has been successfully updated.")
def get_object(self):
return get_object_or_404(
Attendee,
ticket_reference=self.kwargs['ticket_reference'],
ticket_code=self.kwargs['ticket_code'],
)
#cached_property
def attendee_form(self):
return AssignAttendeeForm(
prefix='attendee',
data=self.request.POST or None,
# instance=self.attendee_contact,
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = {
'attendee_form': self.attendee_form,
}
The problem was that it was missing:
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super().get(request, *args, **kwargs)
I want to fill my model form with initial data. However, I always receive an 'Attendee' object is not iterable. Full traceback: http://dpaste.com/0BH9MAM
When I comment this out: initial=self.object, the error disappears. However, my from is not pre-filled with any data. As I add more forms I can't work with FormMixin or UpdateForm
class AssignAttendee(SuccessMessageMixin, SingleObjectMixin, TemplateView):
template_name = 'attendees/front/assign_attendee.html'
success_message = _("Attendee has been successfully updated.")
def get_object(self):
return get_object_or_404(
Attendee,
ticket_reference=self.kwargs['ticket_reference'],
ticket_code=self.kwargs['ticket_code'],
)
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super().get(request, *args, **kwargs)
# def post(self, request, *args, **kwargs):
# self.object = self.get_object()
# return super().post(request, *args, **kwargs)
#cached_property
def attendee_form(self):
return AssignAttendeeForm(
prefix='attendee',
data=self.request.POST or None,
initial=self.object,
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = {
'attendee': self.object,
'attendee_form': self.attendee_form,
}
return context
forms.py
class AssignAttendeeForm(forms.ModelForm):
class Meta:
model = Attendee
fields = (
'ticket_reference',
'first_name',
'last_name',
'company_name',
'email',
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['ticket_reference'].widget = forms.HiddenInput()
self.fields['ticket_reference'].disabled = True
for visible_field in self.visible_fields():
visible_field.field.widget.attrs['class'] = 'form-control'
You should pass a dict to Form.initial, not an object.
You are probably looking for the ModelForm.instance keyword argument, which allows updating an existing instance of a model.
I have some page that displays various players of a team. In the configuration you can switch between the teams, so if you put TEAM_NAME = 'test' it loads the test team obviously. The problem is, that if you put TEAM_NAME = 'test2', it starts it up for that team, but I can still change the URL to switch between teams (while I should only be able to view the team I selected)
The URL looks like this:
http://127.0.0.1:8000/team/1/player/, where 1 would be the first created team, which is test.
When I load the view, I would like to have some permission checks to see if the current view's team is the same as the team in the configuration.
This is the view:
class PlayerList(ListView):
model = player_model
template_name = 'player_list.html'
def get_team(self):
if not hasattr(self, '_team'):
team_id = self.kwargs.get('team_id')
self._team = team_model.objects.get(pk=self.kwargs.get('team_id'))
return self._team
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['team'] = self.get_team()
return context
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
return queryset.filter(team_id=self.kwargs.get('team_id'))
def get(self, request, *args, **kwargs):
return super(PlayerList, self).get(request, *args, **kwargs)
You could do that in get method to block/allow access:
from django.core.exceptions import PermissionDenied
def get(self, request, *args, **kwargs):
team_id = self.kwargs.get('team_id')
team = team_model.objects.get(pk=team_id)
if team.name != TEAM_NAME:
raise PermissionDenied
else:
return super(PlayerList, self).get(request, *args, **kwargs)
I am using django rest framework. I need to pass some extra context value in response but not getting extra_value in response.
class ResultRowView(generics.ListAPIView):
serializer_class = ResultRowSerializer
permission_classes = (AccountPermission, )
def get_serializer(self, *args, **kwargs):
context = {'extra_value': 5000}
return self.serializer_class(*args, context=context, **kwargs)
def get_queryset(self):
qs = ResultRow.objects.none()
pk = self.kwargs.get('pk', None)
try:
route = IncomingRoute.objects.get(account=self.request.user.account, pk=pk)
qs = route.app_module.rows.all()
except Exception, e:
print 'result_row_query: ', e
return qs
What is missing here ?
you can do this by overriding the list method like:
def list(self, request, *args, **kwargs):
response = super(ResultRowView, self).list(request, args, kwargs)
response.data[ 'extra_value' ] = 5000
return response
You will have to override the get_context_data method and then add context to it
def get_context_data(self, **kwargs):
context = super(PropertyListView, self).get_context_data(**kwargs)
context['some_key'] = some_value
return context